├── website ├── themes │ ├── .gitkeep │ └── sqrl-hexo-theme-doc │ │ ├── index.js │ │ ├── .eslintignore │ │ ├── .gitignore │ │ ├── _doc.scss │ │ ├── mockup.png │ │ ├── MAINTAINERS │ │ ├── layout │ │ ├── _partial │ │ │ ├── navigation.ejs │ │ │ └── google_analytics.ejs │ │ ├── page.ejs │ │ └── layout.ejs │ │ ├── source │ │ ├── fonts │ │ │ ├── DressCodeIcons.eot │ │ │ ├── DressCodeIcons.ttf │ │ │ └── DressCodeIcons.woff │ │ └── style │ │ │ ├── _doc │ │ │ ├── mixins.scss │ │ │ ├── typography.scss │ │ │ ├── support.scss │ │ │ ├── index.scss │ │ │ ├── content.scss │ │ │ ├── search.scss │ │ │ └── layout.scss │ │ │ └── _swagger │ │ │ ├── swagger-ui-v3.scss │ │ │ └── swagger-ui-v2.scss │ │ ├── jest.setup.js │ │ ├── plugins │ │ ├── support.js │ │ ├── favicon.js │ │ ├── project-partial.js │ │ ├── swagger-routes.js │ │ ├── swagger-ui.js │ │ ├── react-initial-state.js │ │ ├── search.js │ │ └── react.js │ │ ├── ISSUE_TEMPLATE.md │ │ ├── .npmignore │ │ ├── .editorconfig │ │ ├── scripts │ │ └── all.js │ │ ├── .zappr.yaml │ │ ├── jest.json │ │ ├── webpack.config.js │ │ ├── zappr.md │ │ ├── .eslintrc │ │ ├── banner.js │ │ └── .travis.yml ├── public │ ├── .nojekyll │ └── images │ │ ├── logo.png │ │ ├── favicon.ico │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ └── session │ │ ├── bot_session.jpeg │ │ ├── one_session.jpeg │ │ ├── two_sessions.jpeg │ │ └── extended_session.jpeg ├── .gitignore ├── scaffolds │ ├── draft.md │ ├── page.md │ └── post.md ├── source │ ├── stdlib │ │ ├── assert.md │ │ ├── entity.md │ │ ├── bool.md │ │ ├── control.md │ │ ├── type.md │ │ ├── math.md │ │ ├── data.md │ │ ├── list.md │ │ └── date-time.md │ ├── packages │ │ ├── sqrl-load-functions.md │ │ ├── sqrl-text-functions.md │ │ └── sqrl-cli-functions.md │ ├── language │ │ ├── labels.md │ │ ├── entities.md │ │ └── textpattern.md │ ├── functions │ │ ├── custom.md │ │ └── simple.md │ ├── examples │ │ ├── repl.md │ │ ├── wikipedia.md │ │ └── redis.md │ └── deployment │ │ ├── queue.md │ │ └── async.md ├── package.json ├── _config.yml └── scripts │ └── emoji.js ├── .dockerignore ├── examples ├── wikipedia │ ├── .gitignore │ ├── bad-words.txt │ ├── stream │ ├── README.md │ ├── main.sqrl │ └── features.sqrl ├── include │ ├── one.sqrl │ └── two.sqrl ├── data │ └── tweet.json ├── hello.sqrl ├── simple.sqrl ├── include.sqrl ├── session.sqrl ├── extract.sqrl ├── rule.sqrl ├── sqrl-example-functions │ ├── tsconfig.json │ ├── src │ │ └── index.ts │ ├── package.json │ └── README.md ├── ratelimit.sqrl └── main.sqrl ├── .npmrc ├── packages ├── sqrl │ ├── __tests__ │ │ ├── cost │ │ │ ├── cost.sqrl │ │ │ ├── a-expensive.json │ │ │ └── b-expensive.json │ │ ├── tslint.json │ │ ├── sqrl │ │ │ ├── __snapshots__ │ │ │ │ └── let.spec.ts.snap │ │ │ ├── testfunc.js │ │ │ ├── parse.spec.ts │ │ │ ├── timeMs.spec.ts │ │ │ ├── compile.spec.ts │ │ │ ├── formatDate.spec.ts │ │ │ ├── dateAdd.spec.ts │ │ │ ├── sort.spec.ts │ │ │ ├── dateDiff.spec.ts │ │ │ ├── clock.spec.ts │ │ │ ├── wrappers.spec.ts │ │ │ ├── test.spec.ts │ │ │ ├── bool.spec.ts │ │ │ ├── comments.spec.ts │ │ │ └── args.spec.ts │ │ ├── tsconfig.json │ │ ├── helpers │ │ │ ├── sqrlTest.ts │ │ │ └── TestFunctions.ts │ │ ├── jslib │ │ │ └── indent.spec.ts │ │ ├── registry │ │ │ └── args.spec.ts │ │ ├── parse │ │ │ └── __snapshots__ │ │ │ │ └── customSqrl.spec.ts.snap │ │ └── ast │ │ │ └── AstTypes.spec.ts │ ├── src │ │ ├── api │ │ │ ├── README.md │ │ │ ├── node.ts │ │ │ ├── feature.ts │ │ │ ├── executable.ts │ │ │ ├── when.ts │ │ │ ├── object.ts │ │ │ ├── entity.ts │ │ │ ├── test.ts │ │ │ ├── ctx.ts │ │ │ ├── log.ts │ │ │ └── spec.ts │ │ ├── jslib │ │ │ ├── jsonTemplate.ts │ │ │ ├── flatten.ts │ │ │ ├── range.ts │ │ │ ├── invariant.ts │ │ │ ├── mapObject.ts │ │ │ ├── emptyFunction.ts │ │ │ ├── hasConstructor.ts │ │ │ ├── isEmptyObject.ts │ │ │ ├── hrtimeToNs.ts │ │ │ ├── timeToBuffer.ts │ │ │ ├── indent.ts │ │ │ ├── isPromise.ts │ │ │ ├── assertNever.ts │ │ │ ├── mapToObj.ts │ │ │ ├── timeoutPromise.ts │ │ │ ├── foreachObject.ts │ │ │ ├── chunk.ts │ │ │ ├── mapRegExpMatches.ts │ │ │ ├── pendingPromise.ts │ │ │ ├── deepEqual.ts │ │ │ └── murmurhashJson.ts │ │ ├── object │ │ │ ├── SqrlObject.ts │ │ │ ├── SqrlDateTime.ts │ │ │ ├── span.ts │ │ │ └── SqrlUniqueId.ts │ │ ├── platform │ │ │ ├── EntityId.ts │ │ │ ├── DatabaseSet.ts │ │ │ └── Trace.ts │ │ ├── compile │ │ │ ├── sqrlInvariant.ts │ │ │ ├── buildSqrlError.ts │ │ │ ├── sqrlSourceArrow.ts │ │ │ ├── sqrlErrorWrap.ts │ │ │ └── SqrlCompile.ts │ │ ├── fast-stable-stringify.d.ts │ │ ├── feature │ │ │ ├── StaticFeatures.ts │ │ │ └── FeatureName.ts │ │ ├── TextEncoder.d.ts │ │ ├── expr │ │ │ └── Expr.ts │ │ ├── helpers │ │ │ ├── InstanceHelpers.ts │ │ │ └── ContextHelpers.ts │ │ ├── index.ts │ │ ├── ast │ │ │ └── ArgumentCheck.ts │ │ ├── browser │ │ │ ├── LocalFilesystem.ts │ │ │ └── JsExecutionContext.ts │ │ ├── node │ │ │ ├── JsExecutionContext.ts │ │ │ └── LocalFilesystem.ts │ │ ├── function │ │ │ ├── EntityFunctions.ts │ │ │ └── TimeFunctions.ts │ │ └── simple │ │ │ ├── SimpleManipulator.ts │ │ │ └── TestLogger.ts │ ├── tsconfig.json │ ├── jest.integration.config.js │ └── jest.config.js ├── sqrl-load-functions │ ├── __tests__ │ │ ├── testdata │ │ │ └── sample.yaml │ │ ├── tslint.json │ │ ├── tsconfig.json │ │ └── load.spec.ts │ ├── tsconfig.json │ ├── jest.integration.config.js │ └── jest.config.js ├── sqrl-cli │ ├── __tests__ │ │ ├── cli │ │ │ ├── no-in-memory-config.json │ │ │ ├── compile.spec.ts │ │ │ ├── config.spec.ts │ │ │ ├── repl.spec.ts │ │ │ └── server.spec.ts │ │ ├── fixed-date-config.json │ │ ├── tslint.json │ │ ├── tsconfig.json │ │ ├── helpers │ │ │ ├── examplePath.ts │ │ │ ├── runCompile.ts │ │ │ ├── runRepl.ts │ │ │ └── runCli.ts │ │ └── doc-tests │ │ │ └── entities.spec.ts │ ├── tsconfig.json │ ├── bin │ │ └── cli.js │ ├── src │ │ ├── cli.ts │ │ ├── jslib │ │ │ ├── DefaultDict.ts │ │ │ └── Closeable.ts │ │ ├── cli │ │ │ ├── CliError.ts │ │ │ ├── WatchedFilesystem.ts │ │ │ └── readJsonFile.ts │ │ ├── spanToShell.ts │ │ └── index.ts │ ├── jest.integration.config.js │ └── jest.config.js ├── sqrl-common │ ├── __tests__ │ │ ├── tslint.json │ │ ├── tsconfig.json │ │ └── bufferToHexEncodedAscii.spec.ts │ ├── tsconfig.json │ ├── src │ │ ├── ensureArray.ts │ │ ├── invariant.ts │ │ ├── AssertService.ts │ │ ├── jsonTemplate.ts │ │ ├── RenderedSpan.ts │ │ ├── SqrlBoxed.ts │ │ ├── hasConstructor.ts │ │ ├── isEmptyObject.ts │ │ ├── cartesianProductOf.ts │ │ ├── foreachObject.ts │ │ ├── removeIndent.ts │ │ ├── bufferToHexEncodedAscii.ts │ │ ├── flatten.ts │ │ ├── promiseFinally.ts │ │ ├── index.ts │ │ ├── range.ts │ │ └── sqrlCartesianProduct.ts │ ├── jest.integration.config.js │ ├── jest.config.js │ └── README.md ├── sqrl-jsonpath │ ├── __tests__ │ │ ├── tslint.json │ │ └── tsconfig.json │ ├── tsconfig.json │ ├── jest.integration.config.js │ └── jest.config.js ├── sqrl-cli-functions │ ├── __tests__ │ │ ├── tslint.json │ │ └── tsconfig.json │ ├── tsconfig.json │ ├── jest.integration.config.js │ ├── jest.config.js │ └── src │ │ └── bufferHumanJson.ts ├── sqrl-redis-functions │ ├── __tests__ │ │ ├── tslint.json │ │ ├── tsconfig.json │ │ ├── include.spec.ts │ │ ├── helpers │ │ │ ├── runSqrl.ts │ │ │ └── services.ts │ │ └── label.spec.ts │ ├── tsconfig.json │ ├── src │ │ ├── mocks │ │ │ └── MockRedisDatabase.ts │ │ ├── addressToHostPort.ts │ │ ├── index.ts │ │ ├── Services.ts │ │ └── parser │ │ │ └── sqrlRedis.ts │ ├── jest.integration.config.js │ └── jest.config.js ├── sqrl-text-functions │ ├── __tests__ │ │ ├── tslint.json │ │ ├── tsconfig.json │ │ ├── hash.spec.ts │ │ ├── email.spec.ts │ │ ├── regex.spec.ts │ │ ├── simhash.spec.ts │ │ ├── charGrams.spec.ts │ │ ├── helpers │ │ │ └── textSqrlTest.ts │ │ └── pattern.spec.ts │ ├── tsconfig.json │ ├── jest.integration.config.js │ └── jest.config.js ├── sqrl-test-utils │ ├── tsconfig.json │ ├── src │ │ └── index.ts │ └── README.md └── wikipedia-diff-stream │ ├── package.json │ └── README.md ├── lerna.json ├── .licensee.json ├── .vscode ├── settings.json ├── extensions.json ├── launch.json └── tasks.json ├── .gitignore ├── .npmignore ├── CODE_OF_CONDUCT.md ├── scripts ├── ensure-installed ├── hexo-website-pull-request └── clean-pegjs-ts ├── .editorconfig ├── docker-compose.yml ├── .github ├── ISSUE_TEMPLATE └── PULL_REQUEST_TEMPLATE ├── jest.base.js ├── jest.integration.config.js ├── Dockerfile ├── CHANGELOG.md ├── jest.config.js ├── sqrl ├── tslint.json ├── .travis.yml └── tsconfig.json /website/themes/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /website/public/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log -------------------------------------------------------------------------------- /examples/wikipedia/.gitignore: -------------------------------------------------------------------------------- 1 | wiki-data 2 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org 2 | -------------------------------------------------------------------------------- /examples/include/one.sqrl: -------------------------------------------------------------------------------- 1 | LET Feature := 'One!'; -------------------------------------------------------------------------------- /examples/include/two.sqrl: -------------------------------------------------------------------------------- 1 | LET Feature := 'Two!'; -------------------------------------------------------------------------------- /website/.gitignore: -------------------------------------------------------------------------------- 1 | /public/* 2 | !/public/images/ 3 | -------------------------------------------------------------------------------- /packages/sqrl/__tests__/cost/cost.sqrl: -------------------------------------------------------------------------------- 1 | LET UsingOr := a() OR b(); -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/.eslintignore: -------------------------------------------------------------------------------- 1 | **/node_modules/** 2 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | target 3 | -------------------------------------------------------------------------------- /website/scaffolds/draft.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: {{ title }} 3 | tags: 4 | --- 5 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/_doc.scss: -------------------------------------------------------------------------------- 1 | @import "source/style/_doc/index" 2 | -------------------------------------------------------------------------------- /packages/sqrl-load-functions/__tests__/testdata/sample.yaml: -------------------------------------------------------------------------------- 1 | string: "hello world" 2 | -------------------------------------------------------------------------------- /packages/sqrl/__tests__/cost/a-expensive.json: -------------------------------------------------------------------------------- 1 | { 2 | "a": 1000, 3 | "b": 10 4 | } 5 | -------------------------------------------------------------------------------- /packages/sqrl/__tests__/cost/b-expensive.json: -------------------------------------------------------------------------------- 1 | { 2 | "b": 1000, 3 | "a": 10 4 | } 5 | -------------------------------------------------------------------------------- /website/scaffolds/page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: {{ title }} 3 | date: {{ date }} 4 | --- 5 | -------------------------------------------------------------------------------- /examples/data/tweet.json: -------------------------------------------------------------------------------- 1 | {"event": "tweet", "username": "floydophone", "text": "hello world!"} -------------------------------------------------------------------------------- /examples/hello.sqrl: -------------------------------------------------------------------------------- 1 | LET Name := input(); 2 | LET Text := concat("Hello, ", Name, "!"); 3 | -------------------------------------------------------------------------------- /examples/wikipedia/bad-words.txt: -------------------------------------------------------------------------------- 1 | /shit/ 2 | /fuck/ 3 | /cunt/ 4 | /asshole/ 5 | /pussy/ 6 | -------------------------------------------------------------------------------- /website/scaffolds/post.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: {{ title }} 3 | date: {{ date }} 4 | tags: 5 | --- 6 | -------------------------------------------------------------------------------- /packages/sqrl-cli/__tests__/cli/no-in-memory-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "state.allow-in-memory": false 3 | } 4 | -------------------------------------------------------------------------------- /website/public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twitter/sqrl/HEAD/website/public/images/logo.png -------------------------------------------------------------------------------- /examples/simple.sqrl: -------------------------------------------------------------------------------- 1 | LET ActionData := input(); 2 | 3 | LET ActionName := jsonValue(ActionData, '$.name'); 4 | -------------------------------------------------------------------------------- /packages/sqrl-cli/__tests__/fixed-date-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "testing.fixed-date": "2019-01-02T03:04:56.789Z" 3 | } 4 | -------------------------------------------------------------------------------- /website/public/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twitter/sqrl/HEAD/website/public/images/favicon.ico -------------------------------------------------------------------------------- /website/public/images/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twitter/sqrl/HEAD/website/public/images/favicon-16x16.png -------------------------------------------------------------------------------- /website/public/images/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twitter/sqrl/HEAD/website/public/images/favicon-32x32.png -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/mockup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twitter/sqrl/HEAD/website/themes/sqrl-hexo-theme-doc/mockup.png -------------------------------------------------------------------------------- /examples/include.sqrl: -------------------------------------------------------------------------------- 1 | LET File := input(); 2 | INCLUDE "include/one.sqrl" WHERE File="one"; 3 | INCLUDE "include/two.sqrl" WHERE File="two"; -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "examples/sqrl-example-functions", 4 | "packages/*" 5 | ], 6 | "version": "0.6.8" 7 | } 8 | -------------------------------------------------------------------------------- /packages/sqrl/src/api/README.md: -------------------------------------------------------------------------------- 1 | # SQRL API Reference Documentation 2 | 3 | Please see https://twitter.github.io/sqrl for project documentation. -------------------------------------------------------------------------------- /website/public/images/session/bot_session.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twitter/sqrl/HEAD/website/public/images/session/bot_session.jpeg -------------------------------------------------------------------------------- /website/public/images/session/one_session.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twitter/sqrl/HEAD/website/public/images/session/one_session.jpeg -------------------------------------------------------------------------------- /website/public/images/session/two_sessions.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twitter/sqrl/HEAD/website/public/images/session/two_sessions.jpeg -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/MAINTAINERS: -------------------------------------------------------------------------------- 1 | Ruben Barilani 2 | Bhaskar Melkani 3 | -------------------------------------------------------------------------------- /website/public/images/session/extended_session.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twitter/sqrl/HEAD/website/public/images/session/extended_session.jpeg -------------------------------------------------------------------------------- /.licensee.json: -------------------------------------------------------------------------------- 1 | { 2 | "license": "(MIT OR BSD-2-Clause OR BSD-3-Clause OR Apache-2.0)", 3 | "whitelist": { 4 | "optimist": "<=0.6.1" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib/", 3 | "[markdown]": { 4 | "editor.rulers": [80, 120] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/sqrl/__tests__/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../tslint"], 3 | "rules": { 4 | "no-implicit-dependencies": [true, "dev"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/layout/_partial/navigation.ejs: -------------------------------------------------------------------------------- 1 |
<%- react_component('Navigation', initial_state) %>
2 | -------------------------------------------------------------------------------- /packages/sqrl-cli/__tests__/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../tslint"], 3 | "rules": { 4 | "no-implicit-dependencies": [true, "dev"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/sqrl-common/__tests__/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../tslint"], 3 | "rules": { 4 | "no-implicit-dependencies": [true, "dev"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/sqrl-jsonpath/__tests__/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../tslint"], 3 | "rules": { 4 | "no-implicit-dependencies": [true, "dev"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/sqrl-cli-functions/__tests__/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../tslint"], 3 | "rules": { 4 | "no-implicit-dependencies": [true, "dev"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/sqrl-load-functions/__tests__/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../tslint"], 3 | "rules": { 4 | "no-implicit-dependencies": [true, "dev"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/sqrl-redis-functions/__tests__/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../tslint"], 3 | "rules": { 4 | "no-implicit-dependencies": [true, "dev"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/sqrl-text-functions/__tests__/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../../tslint"], 3 | "rules": { 4 | "no-implicit-dependencies": [true, "dev"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/session.sqrl: -------------------------------------------------------------------------------- 1 | LET RequestIp := input(); 2 | LET Ip := entity('Ip', RequestIp); 3 | LET SqrlMutate := true; 4 | 5 | LET Session := sessionize(BY Ip MAX 2 EVERY 5 SECONDS); 6 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/source/fonts/DressCodeIcons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twitter/sqrl/HEAD/website/themes/sqrl-hexo-theme-doc/source/fonts/DressCodeIcons.eot -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/source/fonts/DressCodeIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twitter/sqrl/HEAD/website/themes/sqrl-hexo-theme-doc/source/fonts/DressCodeIcons.ttf -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/source/fonts/DressCodeIcons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twitter/sqrl/HEAD/website/themes/sqrl-hexo-theme-doc/source/fonts/DressCodeIcons.woff -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "esbenp.prettier-vscode", 4 | "ms-vscode.vscode-typescript-tslint-plugin", 5 | "redhat.vscode-yaml" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/jest.setup.js: -------------------------------------------------------------------------------- 1 | const { configure } = require('enzyme'); 2 | const Adapter = require('enzyme-adapter-react-16'); 3 | 4 | configure({ adapter: new Adapter() }); 5 | -------------------------------------------------------------------------------- /examples/extract.sqrl: -------------------------------------------------------------------------------- 1 | LET EventData := input(); 2 | LET EventType := jsonValue(EventData, "$.event"); 3 | LET Username := jsonValue(EventData, "$.username"); 4 | LET Text := jsonValue(EventData, "$.text"); -------------------------------------------------------------------------------- /website/source/stdlib/assert.md: -------------------------------------------------------------------------------- 1 | title: Assert Functions 2 | --- 3 | 4 | # Assert Functions 5 | 6 | ## assert 7 | 8 | **assert**(condition) 9 | 10 | Assert that an expected condition is true 11 | 12 | -------------------------------------------------------------------------------- /packages/sqrl-cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | 4 | "compilerOptions": { 5 | "rootDir": "./src", 6 | "outDir": "./lib" 7 | }, 8 | "include": ["src/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/sqrl/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | 4 | "compilerOptions": { 5 | "rootDir": "./src", 6 | "outDir": "./lib" 7 | }, 8 | "include": ["src/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/sqrl-common/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | 4 | "compilerOptions": { 5 | "rootDir": "./src", 6 | "outDir": "./lib" 7 | }, 8 | "include": ["src/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/sqrl-jsonpath/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | 4 | "compilerOptions": { 5 | "rootDir": "./src", 6 | "outDir": "./lib" 7 | }, 8 | "include": ["src/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .DS_Store 3 | .*.swp 4 | *.log 5 | .deploy*/ 6 | .envrc 7 | .nyc_output 8 | coverage/ 9 | lib/ 10 | node_modules/ 11 | website/db.json 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | -------------------------------------------------------------------------------- /examples/rule.sqrl: -------------------------------------------------------------------------------- 1 | LET Name := input(); 2 | 3 | LET FlagNames := ['greg', 'amy', 'steven', 'jennifier', 'josh']; 4 | 5 | CREATE RULE FlaggedName WHERE Name in FlagNames 6 | WITH REASON "The name ${Name} was flagged."; 7 | -------------------------------------------------------------------------------- /packages/sqrl-cli-functions/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | 4 | "compilerOptions": { 5 | "rootDir": "./src", 6 | "outDir": "./lib" 7 | }, 8 | "include": ["src/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/sqrl-test-utils/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | 4 | "compilerOptions": { 5 | "rootDir": "./src", 6 | "outDir": "./lib" 7 | }, 8 | "include": ["src/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # gitignore 2 | 3 | coverage/ 4 | node_modules/ 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | .nyc_output 9 | lib 10 | lib_test 11 | 12 | # npmignore 13 | 14 | src/ 15 | __tests__/ 16 | .vscode/ -------------------------------------------------------------------------------- /packages/sqrl-load-functions/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | 4 | "compilerOptions": { 5 | "rootDir": "./src", 6 | "outDir": "./lib" 7 | }, 8 | "include": ["src/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/sqrl-redis-functions/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | 4 | "compilerOptions": { 5 | "rootDir": "./src", 6 | "outDir": "./lib" 7 | }, 8 | "include": ["src/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/sqrl-text-functions/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | 4 | "compilerOptions": { 5 | "rootDir": "./src", 6 | "outDir": "./lib" 7 | }, 8 | "include": ["src/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/sqrl-common/__tests__/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | 4 | "compilerOptions": { 5 | "rootDir": "..", 6 | "outDir": "build" 7 | }, 8 | "include": ["../src/**/*", "**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/sqrl-jsonpath/__tests__/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | 4 | "compilerOptions": { 5 | "rootDir": "..", 6 | "outDir": "build" 7 | }, 8 | "include": ["../src/**/*", "**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/sqrl-cli-functions/__tests__/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | 4 | "compilerOptions": { 5 | "rootDir": "..", 6 | "outDir": "build" 7 | }, 8 | "include": ["../src/**/*", "**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/sqrl/src/jslib/jsonTemplate.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | export { jsonTemplate } from "sqrl-common"; 7 | -------------------------------------------------------------------------------- /packages/sqrl/src/object/SqrlObject.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | export { SqrlObject } from "sqrl-common"; 7 | -------------------------------------------------------------------------------- /packages/sqrl/src/platform/EntityId.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | export { EntityId } from "../api/entity"; 7 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/plugins/support.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {filter} = require('../lib/nodejs/support'); 4 | 5 | module.exports = ({hexo}) => { 6 | hexo.extend.filter.register('template_locals', filter); 7 | }; 8 | -------------------------------------------------------------------------------- /packages/sqrl-load-functions/__tests__/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | 4 | "compilerOptions": { 5 | "rootDir": "..", 6 | "outDir": "build" 7 | }, 8 | "include": ["../src/**/*", "**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/sqrl-redis-functions/__tests__/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | 4 | "compilerOptions": { 5 | "rootDir": "..", 6 | "outDir": "build" 7 | }, 8 | "include": ["../src/**/*", "**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/sqrl-test-utils/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | export { JestAssertService } from "./JestAssert"; 7 | -------------------------------------------------------------------------------- /packages/sqrl-text-functions/__tests__/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | 4 | "compilerOptions": { 5 | "rootDir": "..", 6 | "outDir": "build" 7 | }, 8 | "include": ["../src/**/*", "**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/sqrl/src/compile/sqrlInvariant.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | export { sqrlInvariant } from "../api/parse"; 7 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/source/style/_doc/mixins.scss: -------------------------------------------------------------------------------- 1 | // formatting 2 | // ---------- 3 | @mixin doc-inline-code { 4 | background: $doc-color-lighter; 5 | font-size: 90%; 6 | padding: 1px 5px; 7 | border-radius: 2px; 8 | } 9 | -------------------------------------------------------------------------------- /packages/sqrl/src/api/node.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | 7 | export { LocalFilesystem } from "../node/LocalFilesystem"; 8 | -------------------------------------------------------------------------------- /packages/sqrl/src/compile/buildSqrlError.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | export { buildSqrlError } from "../api/parse"; 7 | -------------------------------------------------------------------------------- /packages/sqrl-cli/bin/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // Copyright 2019 Twitter, Inc. 3 | // Licensed under the Apache License, Version 2.0 4 | // http://www.apache.org/licenses/LICENSE-2.0 5 | 6 | "use strict"; 7 | 8 | require("../lib/index").run(); 9 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | We feel that a welcoming community is important and we ask that you follow Twitter's 2 | [Open Source Code of Conduct](https://github.com/twitter/code-of-conduct/blob/master/code-of-conduct.md) 3 | in all interactions with the community. 4 | -------------------------------------------------------------------------------- /packages/sqrl-cli/__tests__/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | 4 | "compilerOptions": { 5 | "rootDir": "..", 6 | "outDir": "build" 7 | }, 8 | "include": ["../src/**/*", "**/*"], 9 | "exclude": ["build"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/sqrl/__tests__/sqrl/__snapshots__/let.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`supports basic statements: multiple-definitions 1`] = ` 4 | [Error: Multiple definitions of A 5 | :3 LET A := FeatureOne + 1;] 6 | `; 7 | -------------------------------------------------------------------------------- /packages/sqrl/__tests__/sqrl/testfunc.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Twitter, Inc. 2 | // Licensed under the Apache License, Version 2.0 3 | // http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | module.exports = function (name) { 6 | return "hello " + name; 7 | }; 8 | -------------------------------------------------------------------------------- /packages/sqrl/src/jslib/flatten.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { flatten } from "sqrl-common"; 7 | export default flatten; 8 | -------------------------------------------------------------------------------- /packages/sqrl/src/jslib/range.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { range } from "sqrl-common"; 7 | 8 | export default range; 9 | -------------------------------------------------------------------------------- /examples/sqrl-example-functions/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "lib", 6 | "lib": ["es6"], 7 | "rootDir": "src" 8 | }, 9 | "exclude": ["node_modules"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/sqrl/src/jslib/invariant.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { invariant } from "sqrl-common"; 7 | export default invariant; 8 | -------------------------------------------------------------------------------- /packages/sqrl/src/jslib/mapObject.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { mapObject } from "sqrl-common"; 7 | export default mapObject; 8 | -------------------------------------------------------------------------------- /scripts/ensure-installed: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eo pipefail 3 | 4 | root="$(dirname $0)/.." 5 | 6 | if [ ! -e "$root/node_modules" ]; then 7 | echo 'Core dependencies not found!' >&2 8 | echo 'Please run `npm install` first.' >&2 9 | exit 1 10 | fi 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /examples/wikipedia/stream: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eo pipefail 3 | 4 | cd "$(dirname $0)/../.." 5 | ./scripts/ensure-installed 6 | 7 | node packages/wikipedia-diff-stream/main.js en.wikipedia.org | \ 8 | ./sqrl run ./examples/wikipedia/main.sqrl --stream=EventData "$@" 9 | -------------------------------------------------------------------------------- /packages/sqrl/src/fast-stable-stringify.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | declare function stringify(obj: any): string; 7 | 8 | export = stringify; 9 | -------------------------------------------------------------------------------- /packages/sqrl/src/jslib/emptyFunction.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { emptyFunction } from "sqrl-common"; 7 | export default emptyFunction; 8 | -------------------------------------------------------------------------------- /packages/sqrl/src/jslib/hasConstructor.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { hasConstructor } from "sqrl-common"; 7 | export default hasConstructor; 8 | -------------------------------------------------------------------------------- /packages/sqrl/src/jslib/isEmptyObject.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { isEmptyObject } from "sqrl-common"; 7 | export default isEmptyObject; 8 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/plugins/favicon.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('../lib/nodejs/hexo-util'); 4 | 5 | module.exports = ({hexo}) => { 6 | const {themeConfig} = util({hexo}); 7 | 8 | themeConfig({ favicon: '/favicon.ico' }); 9 | }; 10 | -------------------------------------------------------------------------------- /packages/sqrl/src/api/feature.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { isValidFeatureName } from "../feature/FeatureName"; 7 | 8 | export { isValidFeatureName }; 9 | -------------------------------------------------------------------------------- /packages/sqrl-redis-functions/src/mocks/MockRedisDatabase.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | export interface MockRedisDatabase { 7 | db: { [key: string]: any }; 8 | } 9 | -------------------------------------------------------------------------------- /packages/sqrl/src/api/executable.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import SqrlSourcePrinter from "../compile/SqrlSourcePrinter"; 7 | 8 | export { SqrlSourcePrinter as SourcePrinter }; 9 | -------------------------------------------------------------------------------- /packages/sqrl/__tests__/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | 4 | "compilerOptions": { 5 | "rootDir": "..", 6 | "outDir": "build" 7 | }, 8 | "include": [ 9 | "../src/**/*", 10 | "**/*", 11 | "../../sqrl-load-functions/__tests__/load.spec.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /website/source/stdlib/entity.md: -------------------------------------------------------------------------------- 1 | title: Entity Functions 2 | --- 3 | 4 | # Entity Functions 5 | 6 | ## entityId 7 | 8 | **entityId**(entity) 9 | 10 | Returns the entity id of the entity 11 | 12 | ## uniqueId 13 | 14 | **uniqueId**(entity) 15 | 16 | Returns the unique id of the entity as a string 17 | 18 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Expected Behavior 2 | 3 | 4 | ## Actual Behavior 5 | 6 | 7 | ## Steps to Reproduce the Problem 8 | 9 | 1. 10 | 2. 11 | 3. 12 | 13 | ## Specifications 14 | 15 | - NodeJS Version: 16 | - NPM Version: 17 | - OS: 18 | - Hexo Version: 19 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | web: 4 | build: . 5 | ports: 6 | - "2288:2288" 7 | volumes: 8 | - ./examples:/sqrl 9 | command: 10 | - sqrl 11 | - --redis=redis:6379 12 | - serve 13 | - /sqrl/main.sqrl 14 | redis: 15 | image: "redis:alpine" 16 | -------------------------------------------------------------------------------- /scripts/hexo-website-pull-request: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -xeo pipefail 3 | 4 | cd "$(dirname $0)/../website" 5 | rm -rf .deploy_git 6 | git clone --branch gh-pages git@github.com:twitter/sqrl .deploy_git 7 | hexo deploy --generate 8 | (cd .deploy_git && hub pull-request -f -b gh-pages -h gh-pages-staging -m 'Deploy site') 9 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/source/style/_doc/typography.scss: -------------------------------------------------------------------------------- 1 | a { @include dc-link } 2 | p { @extend .dc-p } 3 | 4 | h1 { @include dc-h1 } 5 | h2 { @include dc-h2 } 6 | h3 { @include dc-h3 } 7 | h4 { @include dc-h4 } 8 | 9 | hr { 10 | @include dc-divider; 11 | @include dc-divider--secondary; 12 | } 13 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/.npmignore: -------------------------------------------------------------------------------- 1 | # dotfiles 2 | .* 3 | 4 | # tests 5 | *__tests__ 6 | 7 | # tools 8 | banner.js 9 | webpack.config.js 10 | jest* 11 | zappr.* 12 | .travis* 13 | 14 | # misc 15 | mockup* 16 | CONTRIBUTING.md 17 | ISSUE_TEMPLATE.md 18 | CODE_OF_CONDUCT.md 19 | *.log 20 | target 21 | public 22 | -------------------------------------------------------------------------------- /website/source/stdlib/bool.md: -------------------------------------------------------------------------------- 1 | title: Bool Functions 2 | --- 3 | 4 | # Bool Functions 5 | 6 | ## choice 7 | 8 | **choice**(value[, ...]) 9 | 10 | Returns the first truthy value, otherwise the final value. 11 | 12 | ## coalesce 13 | 14 | **coalesce**(value[, ...]) 15 | 16 | Returns the first value that is not null 17 | 18 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/source/style/_doc/support.scss: -------------------------------------------------------------------------------- 1 | .doc-support-footer { 2 | margin-top: 3.8rem; 3 | border-top: 2px solid $doc-support-footer-border-color; 4 | padding: 2.4rem 0; 5 | 6 | &__text { 7 | color: $doc-support-footer-color; 8 | } 9 | 10 | &__link { 11 | font-weight: 500; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/sqrl-cli/src/cli.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | // This is intentionally simple so that it can be implemented easily in pure 7 | // javascript for ./bin/cli.js 8 | import { run } from "./index"; 9 | run(); 10 | -------------------------------------------------------------------------------- /packages/sqrl/src/api/when.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | export interface FiredRule { 7 | name: string; 8 | reason: string | null; 9 | } 10 | export interface WhenCause { 11 | firedRules: FiredRule[]; 12 | } 13 | -------------------------------------------------------------------------------- /packages/sqrl/src/feature/StaticFeatures.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | export const defaultStatementFeatures = [ 7 | "SqrlAssertionStatements", 8 | "SqrlLogStatements", 9 | "SqrlExecutionComplete", 10 | ]; 11 | -------------------------------------------------------------------------------- /packages/sqrl/src/jslib/hrtimeToNs.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | const SECOND_IN_NS = 1e9; 7 | 8 | export default function hrtimeToNs(hrtime: [number, number]) { 9 | return hrtime[0] * SECOND_IN_NS + hrtime[1]; 10 | } 11 | -------------------------------------------------------------------------------- /packages/sqrl/src/jslib/timeToBuffer.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { toBufferBE } from "bigint-buffer"; 7 | 8 | export function timeToBuffer(timeMs: number) { 9 | return toBufferBE(BigInt(timeMs), 8); 10 | } 11 | -------------------------------------------------------------------------------- /examples/wikipedia/README.md: -------------------------------------------------------------------------------- 1 | Wikipedia SQRL Example 2 | ====================== 3 | 4 | To stream live data from wikipedia and run it against the SQRL files here, simply run: 5 | $ ./stream 6 | 7 | If you want to batch process days worth of data from a file, and only highlight bad entries 8 | cat wiki-data | ../../sqrl -- run ./main.sqrl --stream=EventData --only-blocked 9 | -------------------------------------------------------------------------------- /packages/sqrl-cli/__tests__/helpers/examplePath.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { join } from "path"; 7 | 8 | export function examplePath(filename: string) { 9 | return join(__dirname, "../../../../examples", filename); 10 | } 11 | -------------------------------------------------------------------------------- /packages/sqrl-common/src/ensureArray.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | export function ensureArray(input: T | T[]): T[] { 7 | if (Array.isArray(input)) { 8 | return input; 9 | } else { 10 | return [input]; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/sqrl/src/feature/FeatureName.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | export function isValidFeatureName(featureName) { 7 | return ( 8 | typeof featureName === "string" && /^[A-Z][A-Za-z0-9_]*$/.test(featureName) 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /packages/sqrl/__tests__/helpers/sqrlTest.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { runSqrlTest } from "../../src/simple/runSqrlTest"; 7 | 8 | export function sqrlTest(name: string, sqrl: string) { 9 | test(name, () => runSqrlTest(sqrl)); 10 | } 11 | -------------------------------------------------------------------------------- /packages/sqrl/jest.integration.config.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Twitter, Inc. 2 | // Licensed under the Apache License, Version 2.0 3 | // http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | const config = require("./jest.config"); 6 | 7 | module.exports = { 8 | ...config, 9 | globals: { 10 | ...(config.globals || {}), 11 | __INTEGRATION__: true, 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /packages/sqrl/src/jslib/indent.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | export function indent(string: string, spaces: number) { 7 | const indentString = " ".repeat(spaces); 8 | return indentString + string.replace(/\n/g, `\n${indentString}`); 9 | } 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE: -------------------------------------------------------------------------------- 1 | One line summary of the issue here. 2 | 3 | ### Expected behavior 4 | 5 | As concisely as possible, describe the expected behavior. 6 | 7 | ### Actual behavior 8 | 9 | As concisely as possible, describe the observed behavior. 10 | 11 | ### Steps to reproduce the behavior 12 | 13 | Please list all relevant steps to reproduce the observed behavior. 14 | -------------------------------------------------------------------------------- /packages/sqrl-cli/jest.integration.config.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Twitter, Inc. 2 | // Licensed under the Apache License, Version 2.0 3 | // http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | const config = require("./jest.config"); 6 | 7 | module.exports = { 8 | ...config, 9 | globals: { 10 | ...(config.globals || {}), 11 | __INTEGRATION__: true 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /packages/sqrl-common/jest.integration.config.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Twitter, Inc. 2 | // Licensed under the Apache License, Version 2.0 3 | // http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | const config = require("./jest.config"); 6 | 7 | module.exports = { 8 | ...config, 9 | globals: { 10 | ...(config.globals || {}), 11 | __INTEGRATION__: true 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /packages/sqrl-jsonpath/jest.integration.config.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Twitter, Inc. 2 | // Licensed under the Apache License, Version 2.0 3 | // http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | const config = require("./jest.config"); 6 | 7 | module.exports = { 8 | ...config, 9 | globals: { 10 | ...(config.globals || {}), 11 | __INTEGRATION__: true 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /packages/sqrl/__tests__/sqrl/parse.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { parseSqrl } from "../../src/parser/SqrlParse"; 7 | 8 | test("Basic parser works", () => { 9 | expect(parseSqrl("LET X := 5;").statements[0].type).toEqual("let"); 10 | }); 11 | -------------------------------------------------------------------------------- /packages/sqrl/src/TextEncoder.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | 7 | import { TextEncoder } from "util"; 8 | 9 | declare global { 10 | // TextEncoder is available as a global since node v12 11 | export const TextEncoder: new () => TextEncoder; 12 | } 13 | -------------------------------------------------------------------------------- /packages/sqrl-cli-functions/jest.integration.config.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Twitter, Inc. 2 | // Licensed under the Apache License, Version 2.0 3 | // http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | const config = require("./jest.config"); 6 | 7 | module.exports = { 8 | ...config, 9 | globals: { 10 | ...(config.globals || {}), 11 | __INTEGRATION__: true 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /packages/sqrl-load-functions/jest.integration.config.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Twitter, Inc. 2 | // Licensed under the Apache License, Version 2.0 3 | // http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | const config = require("./jest.config"); 6 | 7 | module.exports = { 8 | ...config, 9 | globals: { 10 | ...(config.globals || {}), 11 | __INTEGRATION__: true 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /packages/sqrl-redis-functions/jest.integration.config.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Twitter, Inc. 2 | // Licensed under the Apache License, Version 2.0 3 | // http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | const config = require("./jest.config"); 6 | 7 | module.exports = { 8 | ...config, 9 | globals: { 10 | ...(config.globals || {}), 11 | __INTEGRATION__: true 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /packages/sqrl-text-functions/jest.integration.config.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Twitter, Inc. 2 | // Licensed under the Apache License, Version 2.0 3 | // http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | const config = require("./jest.config"); 6 | 7 | module.exports = { 8 | ...config, 9 | globals: { 10 | ...(config.globals || {}), 11 | __INTEGRATION__: true 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/layout/page.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 | <%- page.content %> 7 | 8 |
9 |
10 |
11 |
12 | -------------------------------------------------------------------------------- /examples/ratelimit.sqrl: -------------------------------------------------------------------------------- 1 | LET RequestIp := input(); 2 | LET Ip := entity('Ip', RequestIp); 3 | 4 | # Note: You could use `WHERE rateLimited(BY ...)` instead of using `rateLimit()` and 5 | # checking the remaining tokens are empty. 6 | LET Remaining := rateLimit(BY Ip MAX 2 EVERY 30 SECONDS); 7 | CREATE RULE BlockedByRateLimit WHERE Remaining = 0; 8 | 9 | WHEN BlockedByRateLimit THEN blockAction(); 10 | -------------------------------------------------------------------------------- /packages/sqrl/src/jslib/isPromise.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | export default function isPromise(value: any): value is Promise { 7 | return ( 8 | value !== null && 9 | typeof value === "object" && 10 | typeof value.then === "function" 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /packages/sqrl-cli/__tests__/cli/compile.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | 7 | import { runCompile } from "../helpers/runCompile"; 8 | 9 | test("repl works", async () => { 10 | const output = await runCompile(); 11 | expect(JSON.parse(output)).toMatchSnapshot(); 12 | }); 13 | -------------------------------------------------------------------------------- /packages/sqrl/__tests__/sqrl/timeMs.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { sqrlTest } from "../helpers/sqrlTest"; 7 | 8 | sqrlTest( 9 | "timeMs works", 10 | ` 11 | LET Now := '2017-09-26T17:37:42.364Z'; 12 | ASSERT timeMs(Now) = 1506447462364; 13 | ` 14 | ); 15 | -------------------------------------------------------------------------------- /packages/sqrl-cli/src/jslib/DefaultDict.ts: -------------------------------------------------------------------------------- 1 | export class DefaultDict { 2 | [key: string]: T; 3 | constructor(constr: () => T) { 4 | return new Proxy( 5 | {}, 6 | { 7 | get: (target, name) => { 8 | if (!target.hasOwnProperty(name)) { 9 | target[name] = constr(); 10 | } 11 | return target[name]; 12 | }, 13 | } 14 | ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/sqrl-common/src/invariant.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { format as utilFormat } from "util"; 7 | 8 | export function invariant(condition: any, format: string, ...message: any[]) { 9 | if (!condition) { 10 | throw new Error(utilFormat(format, ...message)); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /jest.base.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Twitter, Inc. 2 | // Licensed under the Apache License, Version 2.0 3 | // http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | module.exports = { 6 | setupFilesAfterEnv: ["jest-extended"], 7 | transform: { 8 | "\\.ts$": "ts-jest" 9 | }, 10 | globals: { 11 | __INTEGRATION__: false 12 | }, 13 | moduleFileExtensions: ["ts", "js", "node"], 14 | testEnvironment: "node" 15 | }; 16 | -------------------------------------------------------------------------------- /jest.integration.config.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Twitter, Inc. 2 | // Licensed under the Apache License, Version 2.0 3 | // http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | const config = require("./jest.config"); 6 | const glob = require('glob'); 7 | const path = require('path'); 8 | 9 | module.exports = { 10 | ...config, 11 | projects: glob.sync(path.join(__dirname, 'packages/*/jest.integration.config.js')), 12 | }; 13 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/plugins/project-partial.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {createHelper} = require('../lib/nodejs/project-partial'); 4 | 5 | module.exports = ({hexo}) => { 6 | 7 | hexo.extend.helper.register('project_partial', createHelper({ 8 | theme_config: hexo.config.theme_config, 9 | source_dir: hexo.source_dir, 10 | render: hexo.render, 11 | log: hexo.log 12 | })); 13 | 14 | }; 15 | -------------------------------------------------------------------------------- /packages/sqrl/__tests__/jslib/indent.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { indent } from "../../src/jslib/indent"; 7 | 8 | test("works", () => { 9 | expect(indent("hello\nworld", 2)).toEqual(" hello\n world"); 10 | expect(indent(" hello\nworld", 1)).toEqual(" hello\n world"); 11 | }); 12 | -------------------------------------------------------------------------------- /packages/sqrl/jest.config.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Twitter, Inc. 2 | // Licensed under the Apache License, Version 2.0 3 | // http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | const base = require("../../jest.base"); 6 | const package = require("./package"); 7 | 8 | module.exports = { 9 | ...base, 10 | name: package.name, 11 | rootDir: "../..", 12 | testRegex: `packages/${package.name}/__tests__/.*\\.spec\\.ts$`, 13 | }; 14 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:8 as production 2 | WORKDIR /app 3 | COPY package*.json ./ 4 | RUN npm install --only=production 5 | 6 | FROM production as build 7 | RUN npm install 8 | COPY . . 9 | RUN npm run build 10 | 11 | FROM node:8 12 | WORKDIR /app 13 | VOLUME /sqrl 14 | COPY --from=production /app /app 15 | COPY --from=build /app/lib /app/lib 16 | RUN ln -s /app/lib/cli.js /usr/local/bin/sqrl 17 | 18 | EXPOSE 2288 19 | CMD [ "sqrl" ] 20 | -------------------------------------------------------------------------------- /packages/sqrl-cli/jest.config.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Twitter, Inc. 2 | // Licensed under the Apache License, Version 2.0 3 | // http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | const base = require("../../jest.base"); 6 | const package = require("./package"); 7 | 8 | module.exports = { 9 | ...base, 10 | name: package.name, 11 | rootDir: "../..", 12 | testRegex: `/packages/${package.name}/__tests__/.*\\.spec\\.ts$`, 13 | }; 14 | -------------------------------------------------------------------------------- /packages/sqrl-common/jest.config.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Twitter, Inc. 2 | // Licensed under the Apache License, Version 2.0 3 | // http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | const base = require("../../jest.base"); 6 | const package = require("./package"); 7 | 8 | module.exports = { 9 | ...base, 10 | name: package.name, 11 | rootDir: "../..", 12 | testRegex: `/packages/${package.name}/__tests__/.*\\.spec\\.ts$`, 13 | }; 14 | -------------------------------------------------------------------------------- /packages/sqrl-jsonpath/jest.config.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Twitter, Inc. 2 | // Licensed under the Apache License, Version 2.0 3 | // http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | const base = require("../../jest.base"); 6 | const package = require("./package"); 7 | 8 | module.exports = { 9 | ...base, 10 | name: package.name, 11 | rootDir: "../..", 12 | testRegex: `/packages/${package.name}/__tests__/.*\\.spec\\.ts$`, 13 | }; 14 | -------------------------------------------------------------------------------- /packages/sqrl-cli-functions/jest.config.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Twitter, Inc. 2 | // Licensed under the Apache License, Version 2.0 3 | // http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | const base = require("../../jest.base"); 6 | const package = require("./package"); 7 | 8 | module.exports = { 9 | ...base, 10 | name: package.name, 11 | rootDir: "../..", 12 | testRegex: `/packages/${package.name}/__tests__/.*\\.spec\\.ts$`, 13 | }; 14 | -------------------------------------------------------------------------------- /packages/sqrl-load-functions/jest.config.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Twitter, Inc. 2 | // Licensed under the Apache License, Version 2.0 3 | // http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | const base = require("../../jest.base"); 6 | const package = require("./package"); 7 | 8 | module.exports = { 9 | ...base, 10 | name: package.name, 11 | rootDir: "../..", 12 | testRegex: `/packages/${package.name}/__tests__/.*\\.spec\\.ts$`, 13 | }; 14 | -------------------------------------------------------------------------------- /packages/sqrl-redis-functions/jest.config.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Twitter, Inc. 2 | // Licensed under the Apache License, Version 2.0 3 | // http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | const base = require("../../jest.base"); 6 | const package = require("./package"); 7 | 8 | module.exports = { 9 | ...base, 10 | name: package.name, 11 | rootDir: "../..", 12 | testRegex: `/packages/${package.name}/__tests__/.*\\.spec\\.ts$`, 13 | }; 14 | -------------------------------------------------------------------------------- /packages/sqrl-text-functions/jest.config.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Twitter, Inc. 2 | // Licensed under the Apache License, Version 2.0 3 | // http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | const base = require("../../jest.base"); 6 | const package = require("./package"); 7 | 8 | module.exports = { 9 | ...base, 10 | name: package.name, 11 | rootDir: "../..", 12 | testRegex: `/packages/${package.name}/__tests__/.*\\.spec\\.ts$`, 13 | }; 14 | -------------------------------------------------------------------------------- /examples/wikipedia/main.sqrl: -------------------------------------------------------------------------------- 1 | INCLUDE "features.sqrl"; 2 | 3 | LET BadWords := patternMatches("bad-words.txt", AddedContent); 4 | LET CountByUser := count(BY User); 5 | 6 | log("Page: %s", PageUrl); 7 | log("Diff: %s", DiffUrl); 8 | log("Count by user: %d (%s)", CountByUser, User); 9 | 10 | CREATE RULE UsedBadWords WHERE BadWords 11 | WITH REASON "Matched pattern ${BadWords}: ${AddedContent}"; 12 | 13 | WHEN UsedBadWords THEN blockAction(); 14 | -------------------------------------------------------------------------------- /packages/sqrl-common/src/AssertService.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | export interface AssertService { 7 | compare( 8 | manipulator: any, 9 | left: any, 10 | operator: string, 11 | right: any, 12 | arrow: string 13 | ): void; 14 | 15 | ok(manipulator: any, value: any, arrow: string): void; 16 | } 17 | -------------------------------------------------------------------------------- /packages/sqrl-text-functions/__tests__/hash.spec.ts: -------------------------------------------------------------------------------- 1 | import { textSqrlTest } from "./helpers/textSqrlTest"; 2 | 3 | /** 4 | * Copyright 2018 Twitter, Inc. 5 | * Licensed under the Apache License, Version 2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | */ 8 | 9 | textSqrlTest( 10 | "works", 11 | ` 12 | 13 | ASSERT sha256("67hello joe") = 'b42a77e208707718d939c3529dbfb5c9cfbf57f52551a72a352cbd64c74111b4'; 14 | 15 | ` 16 | ); 17 | -------------------------------------------------------------------------------- /packages/sqrl-common/src/jsonTemplate.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | export function jsonTemplate( 7 | strings: TemplateStringsArray, 8 | ...args: any[] 9 | ): string { 10 | return strings.reduce((accum: string, part: string, idx: number) => { 11 | return accum + JSON.stringify(args[idx - 1]) + part; 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/.editorconfig: -------------------------------------------------------------------------------- 1 | # /.editorconfig 2 | # EditorConfig helps developers define and maintain consistent 3 | # coding styles between different editors and IDEs 4 | # editorconfig.org 5 | 6 | root = true 7 | [*] 8 | 9 | indent_style = space 10 | indent_size = 2 11 | 12 | # We recommend you to keep these unchanged 13 | end_of_line = lf 14 | charset = utf-8 15 | trim_trailing_whitespace = true 16 | insert_final_newline = true 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.6.4 (October 29, 2020) 2 | 3 | Upgrade dependency versions 4 | 5 | ## 0.6.3 (March 13, 2019) 6 | 7 | Re-release due to build errors 8 | 9 | ## 0.6.2 (March 11, 2019) 10 | 11 | Split `jsonPath` into `sqrl-json` package. 12 | 13 | Re-release as previous release was still referencing `sqrl-engine`. 14 | 15 | ## 0.6.1 (March 7, 2019) 16 | 17 | Rename `sqrl-engine` to `sqrl` 18 | 19 | ## 0.6 (March 6, 2019) 20 | 21 | Initial public release 22 | -------------------------------------------------------------------------------- /packages/sqrl/src/expr/Expr.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { Expr } from "../api/expr"; 7 | export * from "../api/expr"; 8 | 9 | export function walkExpr(root: Expr, walkCallback: (node: Expr) => void): void { 10 | (root.exprs || []).forEach((expr) => { 11 | walkExpr(expr, walkCallback); 12 | }); 13 | walkCallback(root); 14 | } 15 | -------------------------------------------------------------------------------- /packages/sqrl-common/src/RenderedSpan.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | interface RenderedSpanText { 7 | class: string[]; 8 | text: string; 9 | children?: never; 10 | } 11 | interface RenderedSpanGroup { 12 | class: string[]; 13 | children: RenderedSpan[]; 14 | text?: never; 15 | } 16 | export type RenderedSpan = RenderedSpanText | RenderedSpanGroup; 17 | -------------------------------------------------------------------------------- /packages/sqrl-common/src/SqrlBoxed.ts: -------------------------------------------------------------------------------- 1 | import { SqrlObject } from "./SqrlObject"; 2 | 3 | /** 4 | * Copyright 2018 Twitter, Inc. 5 | * Licensed under the Apache License, Version 2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | */ 8 | 9 | export class SqrlBoxed extends SqrlObject { 10 | constructor(private value: any) { 11 | super(); 12 | } 13 | getData() { 14 | return this.value; 15 | } 16 | getBasicValue() { 17 | return this.value; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Twitter, Inc. 2 | // Licensed under the Apache License, Version 2.0 3 | // http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | const base = require("./jest.base"); 6 | const glob = require("glob"); 7 | const path = require("path"); 8 | 9 | module.exports = { 10 | ...base, 11 | projects: glob.sync(path.join(__dirname, 'packages/*/jest.config.js')), 12 | coverageDirectory: "/coverage/", 13 | testRegex: `.*/__tests__/.*\\.spec\\.ts$` 14 | }; 15 | -------------------------------------------------------------------------------- /packages/sqrl-text-functions/__tests__/email.spec.ts: -------------------------------------------------------------------------------- 1 | import { textSqrlTest } from "./helpers/textSqrlTest"; 2 | 3 | /** 4 | * Copyright 2018 Twitter, Inc. 5 | * Licensed under the Apache License, Version 2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | */ 8 | 9 | textSqrlTest( 10 | "email", 11 | ` 12 | LET RawActorEmail := "pete.HUNT1+978@gmail.com"; 13 | LET ActorEmail := normalizeEmail(RawActorEmail); 14 | 15 | ASSERT ActorEmail = "petehunt1@gmail.com"; 16 | ` 17 | ); 18 | -------------------------------------------------------------------------------- /packages/sqrl/__tests__/sqrl/compile.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { compileToExecution } from "../helpers/runCompile"; 7 | 8 | test("Basic compile works", async () => { 9 | const { execution } = await compileToExecution(` 10 | LET X := 5 + 1; 11 | LET Y := X * 3; 12 | `); 13 | expect(await execution.fetchBasicByName("Y")).toEqual(18); 14 | }); 15 | -------------------------------------------------------------------------------- /website/source/packages/sqrl-load-functions.md: -------------------------------------------------------------------------------- 1 | title: sqrl-load-functions 2 | --- 3 | 4 | # sqrl-load-functions 5 | 6 | These functions assist with loading data from disk at compile time. 7 | 8 | ## loadJson 9 | 10 | **loadJson**(path) 11 | 12 | Loads data from a given JSON file 13 | 14 | ## loadLines 15 | 16 | **loadLines**(path) 17 | 18 | Loads data as a list of lines from a given text file 19 | 20 | ## loadYaml 21 | 22 | **loadYaml**(path) 23 | 24 | Loads data from a given YAML file 25 | 26 | -------------------------------------------------------------------------------- /packages/sqrl-common/src/hasConstructor.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | /** 7 | * Returns true if obj is not JSONifiable without losing constructor information. 8 | */ 9 | export function hasConstructor(obj): boolean { 10 | if (typeof obj !== "object" || obj === null) { 11 | return false; 12 | } 13 | 14 | return obj.constructor !== Object && obj.constructor !== Array; 15 | } 16 | -------------------------------------------------------------------------------- /packages/sqrl-common/src/isEmptyObject.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { invariant } from "./invariant"; 7 | 8 | export function isEmptyObject(obj: any): boolean { 9 | invariant(typeof obj === "object", "expected object to be tested"); 10 | for (const key in obj) { 11 | if (obj.hasOwnProperty(key)) { 12 | return false; 13 | } 14 | } 15 | return true; 16 | } 17 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE: -------------------------------------------------------------------------------- 1 | # Problem 2 | 3 | Explain the context and why you're making that change. What is the 4 | problem you're trying to solve? In some cases there is not a problem 5 | and this can be thought of being the motivation for your change. 6 | 7 | # Solution 8 | 9 | Describe the modifications you've done. 10 | 11 | # Result 12 | 13 | What will change as a result of your pull request? Note that sometimes 14 | this section is unnecessary because it is self-explanatory based on 15 | the solution. 16 | -------------------------------------------------------------------------------- /packages/sqrl-cli/__tests__/helpers/runCompile.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { runCli } from "../helpers/runCli"; 7 | 8 | export function runCompile(args: string[] = []) { 9 | return runCli( 10 | [ 11 | "compile", 12 | "--output", 13 | "expr", 14 | __dirname + "/../../../../examples/hello.sqrl", 15 | ...args, 16 | ], 17 | "" 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/scripts/all.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* global hexo */ 4 | 5 | require('../plugins/favicon')({hexo}); 6 | require('../plugins/project-partial')({hexo}); 7 | require('../plugins/search')({hexo}); 8 | require('../plugins/swagger-to-html')({hexo}); 9 | require('../plugins/swagger-ui')({hexo}); 10 | require('../plugins/react')({hexo}); 11 | require('../plugins/react-initial-state')({hexo}); 12 | require('../plugins/support')({hexo}); 13 | require('../plugins/swagger-routes')({hexo}); 14 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/source/style/_doc/index.scss: -------------------------------------------------------------------------------- 1 | @import "dress-code/dist/sass/dress-code"; 2 | 3 | @import "./vars"; 4 | @import "./mixins"; 5 | 6 | // include dc-* selectors after variables to allow customization 7 | @include dc-everything; 8 | 9 | // core 10 | @import "./typography"; 11 | @import "./layout"; 12 | @import "./navigation"; 13 | @import "./content"; 14 | @import "./formatting"; 15 | 16 | // components 17 | @import "./search"; 18 | @import "./swagger-to-html"; 19 | @import "./support"; 20 | -------------------------------------------------------------------------------- /packages/sqrl/__tests__/sqrl/formatDate.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { sqrlTest } from "../helpers/sqrlTest"; 7 | 8 | sqrlTest( 9 | "formatDate works", 10 | ` 11 | LET DayOne := 1479410503328; 12 | LET DayTwo := 1479496903328; 13 | ASSERT formatDate(DayOne) = 'Thursday, November 17th 2016, 7:21:43 pm'; 14 | ASSERT formatDate(DayOne, "YYYY") = '2016'; 15 | ` 16 | ); 17 | -------------------------------------------------------------------------------- /packages/sqrl/src/api/object.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | export { SqrlKey } from "../object/SqrlKey"; 7 | export { SqrlObject } from "../object/SqrlObject"; 8 | import SqrlEntity from "../object/SqrlEntity"; 9 | export { SqrlEntity }; 10 | import SqrlUniqueId from "../object/SqrlUniqueId"; 11 | export { SqrlUniqueId }; 12 | import SqrlSession from "../object/SqrlSession"; 13 | export { SqrlSession }; 14 | -------------------------------------------------------------------------------- /packages/sqrl/__tests__/sqrl/dateAdd.spec.ts: -------------------------------------------------------------------------------- 1 | import { runSqrlTest } from "../../src/simple/runSqrlTest"; 2 | 3 | /** 4 | * Copyright 2018 Twitter, Inc. 5 | * Licensed under the Apache License, Version 2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | */ 8 | 9 | test("dateAdd works", async () => { 10 | await runSqrlTest(` 11 | LET DayOne := "2018-02-12T08:00:00Z"; 12 | ASSERT dateAdd(DayOne, "PT1S") = "2018-02-12T08:00:01.000Z"; 13 | ASSERT dateAdd(DayOne, "P1D") = "2018-02-13T08:00:00.000Z"; 14 | `); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/sqrl/src/jslib/assertNever.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { format } from "util"; 7 | 8 | export function assertNever(test: never, fmt: string, ...args: any[]): never { 9 | // Typescript should make sure this function is never called, based on the first argument 10 | // See: http://www.typescriptlang.org/docs/handbook/advanced-types.html 11 | throw new Error(format(fmt, ...args)); 12 | } 13 | -------------------------------------------------------------------------------- /packages/sqrl/src/jslib/mapToObj.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | // call with an array and func to create an object where the keys are each array item 7 | // and the vals are the result of the func 8 | 9 | export default function mapToObj(arr, valFn, keyFn = (item?) => item) { 10 | return arr.reduce( 11 | (result, item) => Object.assign(result, { [keyFn(item)]: valFn(item) }), 12 | {} 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /packages/sqrl-cli/__tests__/helpers/runRepl.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { runCli } from "./runCli"; 7 | 8 | export async function runRepl(args: string[], code: string) { 9 | const stdout = await runCli( 10 | ["repl", "--config", __dirname + "/../fixed-date-config.json", ...args], 11 | code.trim().replace(/^ */g, "") + "\n" 12 | ); 13 | 14 | return stdout.split(/\n?sqrl> /gm).filter((v) => v); 15 | } 16 | -------------------------------------------------------------------------------- /packages/sqrl/src/helpers/InstanceHelpers.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { SqrlInstance } from "../function/Instance"; 7 | import { registerAllFunctions } from "../function/registerAllFunctions"; 8 | import { FunctionServices } from "../api/execute"; 9 | 10 | export function buildSqrlInstanceForServices(services: FunctionServices) { 11 | const instance = new SqrlInstance(); 12 | registerAllFunctions(instance); 13 | return instance; 14 | } 15 | -------------------------------------------------------------------------------- /packages/sqrl/src/jslib/timeoutPromise.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import bluebird = require("bluebird"); 7 | 8 | // Type return as Promise to aid migration away from bluebird... but actually return a bluebird 9 | // promise for now. 10 | export function timeoutPromise( 11 | promise: Promise, 12 | timeout: number, 13 | reason?: string 14 | ): Promise { 15 | return bluebird.resolve(promise).timeout(timeout, reason) as any; 16 | } 17 | -------------------------------------------------------------------------------- /sqrl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eo pipefail 3 | 4 | root="$(dirname $0)" 5 | pkg="$root/packages/sqrl-cli" 6 | 7 | $root/scripts/ensure-installed 8 | 9 | export NODE_PATH="$pkg/node_modules:$NODE_PATH" 10 | 11 | # We could use ts-node, however it would use the compiled code from everything 12 | # but the `sqrl-cli` project which is rather confusing. Instead just use all 13 | # compiled code only, which makes it clear that you need to `npm run build` in 14 | # order to see changes you've made. 15 | 16 | exec node --max-old-space-size=4096 \ 17 | "$pkg/bin/cli.js" "$@" 18 | -------------------------------------------------------------------------------- /packages/sqrl-text-functions/__tests__/regex.spec.ts: -------------------------------------------------------------------------------- 1 | import { textSqrlTest } from "./helpers/textSqrlTest"; 2 | 3 | /** 4 | * Copyright 2018 Twitter, Inc. 5 | * Licensed under the Apache License, Version 2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | */ 8 | 9 | textSqrlTest( 10 | "works", 11 | ` 12 | LET Phone := "1(212)333-4444"; 13 | ASSERT regexMatch("123", Phone) = null; 14 | ASSERT regexMatch("4444", Phone) = ["4444"]; 15 | ASSERT regexTest("^[\\\\d()-]+$", Phone) = true; 16 | ASSERT regexReplace("\\\\d", "X", Phone) = "X(XXX)XXX-XXXX"; 17 | ` 18 | ); 19 | -------------------------------------------------------------------------------- /packages/wikipedia-diff-stream/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wikipedia-diff-stream", 3 | "version": "0.6.7", 4 | "publishConfig": { 5 | "registry": "https://registry.npmjs.org" 6 | }, 7 | "dependencies": { 8 | "diff-match-patch": "^1.0.4", 9 | "diff-match-patch-node": "^0.9.1", 10 | "eventsource": "^1.0.7", 11 | "request": "^2.88.0", 12 | "request-promise-native": "^1.0.7", 13 | "wtf_wikipedia": "^8.5.1" 14 | }, 15 | "bin": { 16 | "wikipedia-diff-stream": "main.js" 17 | }, 18 | "gitHead": "d6f4789442ac2e1dea7b1f39b87c6db3395fd815" 19 | } 20 | -------------------------------------------------------------------------------- /packages/sqrl-cli/__tests__/doc-tests/entities.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { runRepl } from "../helpers/runRepl"; 7 | 8 | test("works", async () => { 9 | expect( 10 | await runRepl( 11 | [], 12 | ` 13 | LET User := entity('User', '1234') 14 | User='1234' 15 | str(date(User))` 16 | ) 17 | ).toEqual([ 18 | "'1234'", 19 | "'2019-01-02T03:04:56.789Z'", 20 | "'2019-01-02T03:04:56.789Z'", 21 | ]); 22 | }); 23 | -------------------------------------------------------------------------------- /packages/sqrl-cli/__tests__/cli/config.spec.ts: -------------------------------------------------------------------------------- 1 | import { runCompile } from "../helpers/runCompile"; 2 | 3 | /** 4 | * Copyright 2019 Twitter, Inc. 5 | * Licensed under the Apache License, Version 2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | */ 8 | 9 | test("config works", async () => { 10 | // Using the sample config the compile should throw an error 11 | await expect( 12 | runCompile(["--config=" + __dirname + "/no-in-memory-config.json"]) 13 | ).rejects.toThrowError( 14 | "No `redis.address` was configured and`state.allow-in-memory` is false." 15 | ); 16 | }); 17 | -------------------------------------------------------------------------------- /packages/sqrl-common/src/cartesianProductOf.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | /** 7 | * Gets Cartesian product of multiple arrays. 8 | */ 9 | export function cartesianProductOf(args: T[][]): T[][] { 10 | const initial: T[][] = [[]]; 11 | return args.reduce((a, b) => { 12 | const ret: T[][] = []; 13 | a.forEach((a) => { 14 | b.forEach((b) => { 15 | ret.push(a.concat([b])); 16 | }); 17 | }); 18 | return ret; 19 | }, initial); 20 | } 21 | -------------------------------------------------------------------------------- /packages/sqrl/__tests__/sqrl/sort.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { sqrlTest } from "../helpers/sqrlTest"; 7 | 8 | sqrlTest( 9 | "works", 10 | ` 11 | ASSERT sort([]) = []; 12 | 13 | ASSERT sort([1]) = [1]; 14 | ASSERT sort([1, 2]) = [1, 2]; 15 | ASSERT sort([2, 1]) = [1, 2]; 16 | ASSERT sort([1, 10, 2]) = [1, 2, 10]; 17 | 18 | ASSERT sort(["a", "c", "b"]) = ["a", "b", "c"]; 19 | ASSERT sort(["1", "10", "2"]) = ["1", "10", "2"]; 20 | ` 21 | ); 22 | -------------------------------------------------------------------------------- /website/source/stdlib/control.md: -------------------------------------------------------------------------------- 1 | title: Control Functions 2 | --- 3 | 4 | # Control Functions 5 | 6 | ## if 7 | 8 | **if**(condition, true_result, false_result) 9 | 10 | Returns either the true_result or false_result based on the condition 11 | 12 | ## ifNull 13 | 14 | **ifNull**(value, valueIfNull) 15 | 16 | Returns the value, or valueIfNull if it is null 17 | 18 | ## input 19 | 20 | **input**(None) 21 | 22 | Sets the given feature as an input value 23 | 24 | ## wait 25 | 26 | **wait**(feature[, ...]) 27 | 28 | Function that returns once all of the input features have been calculated 29 | 30 | -------------------------------------------------------------------------------- /examples/sqrl-example-functions/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { AT, Instance, Execution } from "sqrl"; 7 | 8 | export function register(instance: Instance) { 9 | instance.register( 10 | // @todo: Add type for name once it's required 11 | async function sayHello(state: Execution, name) { 12 | return "Hello, " + name + "!"; 13 | }, 14 | { 15 | // @todo: Add argument documentation 16 | args: [AT.state, AT.any], 17 | } 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /packages/sqrl/src/platform/DatabaseSet.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { toBufferBE } from "bigint-buffer"; 7 | import { DatabaseSet } from "../api/ctx"; 8 | 9 | export class SimpleDatabaseSet implements DatabaseSet { 10 | constructor(private datasetId: string) { 11 | /* nothing else */ 12 | } 13 | getDatasetId(): string { 14 | return this.datasetId; 15 | } 16 | getDatasetIdBuffer(): Buffer { 17 | return toBufferBE(BigInt(this.getDatasetId()), 8); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/sqrl/__tests__/sqrl/dateDiff.spec.ts: -------------------------------------------------------------------------------- 1 | import { runSqrlTest } from "../../src/simple/runSqrlTest"; 2 | 3 | /** 4 | * Copyright 2018 Twitter, Inc. 5 | * Licensed under the Apache License, Version 2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | */ 8 | 9 | test("dateDiff works", async () => { 10 | await runSqrlTest(` 11 | LET DayOne := '2016-11-17T19:21:43.328Z'; 12 | LET DayTwo := '2016-11-18T19:21:43.328Z'; 13 | ASSERT dateDiff("HOUR", DayOne, DayTwo) = 24; 14 | ASSERT dateDiff("DAY", DayOne, DayTwo) = 1; 15 | ASSERT dateDiff("DAY", DayTwo, DayOne) = -1; 16 | `); 17 | }); 18 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["tslint:latest", "tslint-config-prettier"], 3 | "rules": { 4 | "interface-name": false, 5 | "ordered-imports": false, 6 | "no-shadowed-variable": false, 7 | "max-classes-per-file": false, 8 | "member-access": false, 9 | "no-string-literal": false, 10 | "prefer-conditional-expression": false, 11 | "object-literal-sort-keys": false, 12 | "prefer-object-spread": false, 13 | "member-ordering": false, 14 | "array-type": false, 15 | "no-object-literal-type-assertion": false, 16 | "variable-name": false, 17 | "only-arrow-functions": false 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/.zappr.yaml: -------------------------------------------------------------------------------- 1 | approvals: 2 | minimum: 2 # PR needs at least 2 approval for compliant reasons 3 | pattern: "^(:\\+1:|👍|approved)$" # write a comment to the PR with "approved" or ":+1" 4 | veto: 5 | pattern: "^(:\\-1:|👎|rejected)$" # write a comment to the PR with "rejected" or ":-1" 6 | from: 7 | collaborators: true 8 | commit: 9 | message: 10 | patterns: 11 | # Follow MC commit conventions. 12 | # CONTRIBUTING.md#-git-commit-guidelines 13 | - "^(feat|fix|docs|style|refactor|perf|test|chore)(|\\([a-zA-Z0-9-._]+\\)):.{3,}" 14 | 15 | X-Zalando-Type: code 16 | -------------------------------------------------------------------------------- /packages/sqrl-cli/src/cli/CliError.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | export class CliError extends Error { 7 | readonly suggestion: string | null; 8 | constructor( 9 | message: string, 10 | options: { 11 | suggestion?: string; 12 | } = {} 13 | ) { 14 | super(message); 15 | this.suggestion = options.suggestion || null; 16 | } 17 | } 18 | 19 | export function cliInvariant(condition: boolean, message: string) { 20 | if (!condition) { 21 | throw new CliError(message); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "verbose": true, 3 | "clearMocks": true, 4 | "testRegex": "(/__tests__/.*\\.(test|spec))\\.(js|jsx)$", 5 | "setupFiles": [ 6 | "raf/polyfill", 7 | "./jest.setup.js" 8 | ], 9 | "collectCoverageFrom": [ 10 | "!**/__tests__/*.{js,jsx}", 11 | "lib/browser/**/*.{js,jsx}", 12 | "lib/nodejs/**/*.js", 13 | "!lib/browser/sidebar/index.js" 14 | ], 15 | "testResultsProcessor": "./node_modules/jest-junit", 16 | "coverageDirectory": "target/coverage", 17 | "coverageReporters": [ 18 | "json", 19 | "lcov", 20 | "html", 21 | "cobertura" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/webpack.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const path = require('path'); 3 | 4 | module.exports = { 5 | externals: { 6 | jquery: '$', 7 | lunr: true, 8 | }, 9 | entry: { 10 | 'doc': './lib/browser/index.js', 11 | }, 12 | output: { 13 | path: path.resolve(__dirname, 'source/script'), 14 | filename: '[name].js' 15 | }, 16 | module: { 17 | loaders: [ 18 | { 19 | test: /.jsx?$/, 20 | loader: 'babel-loader', 21 | exclude: /node_modules/, 22 | query: { 23 | presets: ['es2015', 'react'] 24 | } 25 | } 26 | ] 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | branches: 2 | only: 3 | master 4 | language: node_js 5 | env: 6 | global: 7 | - SQRL_TEST_REDIS=localhost:6379 8 | matrix: 9 | include: 10 | - node_js: "node" 11 | - node_js: "12" 12 | if: type = push AND branch = master 13 | - node_js: "14" 14 | if: type = push AND branch = master 15 | services: 16 | - redis-server 17 | install: 18 | - npm ci 19 | script: 20 | - npm run build 21 | - npm run test:integration -- --coverage 22 | - "redis-cli get 'sqrl:test' | grep '^okay'" 23 | after_script: 24 | - "cat coverage/lcov.info | ./node_modules/.bin/coveralls" # sends the coverage report to coveralls 25 | -------------------------------------------------------------------------------- /packages/sqrl/src/api/entity.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | export class EntityId { 7 | constructor(public type: string, public key: string) {} 8 | 9 | getIdString(): string { 10 | return `${this.type}/${this.key}`; 11 | } 12 | } 13 | 14 | export abstract class UniqueId { 15 | abstract getTimeMs(): number; 16 | abstract getRemainder(): number; 17 | abstract getBuffer(): Buffer; 18 | abstract getNumberString(): string; 19 | 20 | getHexString() { 21 | return this.getBuffer().toString("hex"); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/sqrl/src/helpers/ContextHelpers.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { SimpleDatabaseSet } from "../platform/DatabaseSet"; 7 | import { getGlobalLogger } from "../api/log"; 8 | import { SimpleContext } from "../platform/Trace"; 9 | 10 | // With many customers we can create new dataset id's, for now scope it down to 1 11 | export const DEFAULT_DATASET_ID = "1"; 12 | 13 | export function createDefaultContext() { 14 | return new SimpleContext( 15 | new SimpleDatabaseSet(DEFAULT_DATASET_ID), 16 | getGlobalLogger() 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /packages/sqrl-common/src/foreachObject.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | // Similar to mapObject but does not store the results 7 | export function foreachObject( 8 | object: { [key: string]: T }, 9 | callback: (this: U, value: T, key: string, obj: { [key: string]: T }) => void, 10 | context?: U 11 | ): void { 12 | if (!object) { 13 | return; 14 | } 15 | for (const name in object) { 16 | if (Object.prototype.hasOwnProperty.call(object, name)) { 17 | callback.call(context, object[name], name, object); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/sqrl/__tests__/sqrl/clock.spec.ts: -------------------------------------------------------------------------------- 1 | import { runSqrlTest } from "../../src/simple/runSqrlTest"; 2 | 3 | /** 4 | * Copyright 2018 Twitter, Inc. 5 | * Licensed under the Apache License, Version 2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | */ 8 | 9 | test("bad clock throws", async () => { 10 | await expect( 11 | runSqrlTest(` 12 | LET SqrlClock := nowMs(); 13 | `) 14 | ).rejects.toThrowError(/Invalid ISO 8601/i); 15 | 16 | await runSqrlTest(` 17 | LET SqrlClock := '2018-01-01T15:30Z'; 18 | `); 19 | 20 | await expect( 21 | runSqrlTest(` 22 | LET SqrlClock := '2017-01:5'; 23 | `) 24 | ).rejects.toThrowError(/Invalid ISO 8601/i); 25 | }); 26 | -------------------------------------------------------------------------------- /packages/sqrl/src/jslib/foreachObject.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | // Similar to mapObject but does not store the results 7 | export function foreachObject( 8 | object: { [key: string]: T }, 9 | callback: (this: U, value: T, key: string, obj: { [key: string]: T }) => void, 10 | context?: U 11 | ): void { 12 | if (!object) { 13 | return null; 14 | } 15 | for (const name in object) { 16 | if (Object.prototype.hasOwnProperty.call(object, name)) { 17 | callback.call(context, object[name], name, object); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/sqrl-cli/src/jslib/Closeable.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { invariant } from "sqrl-common"; 7 | 8 | interface Closeable { 9 | close(): void; 10 | } 11 | 12 | export class CloseableGroup { 13 | private list: Closeable[] = []; 14 | private closed = false; 15 | 16 | add(obj: Closeable) { 17 | invariant(!this.closed, "The closeable group has already been closed."); 18 | this.list.push(obj); 19 | } 20 | close() { 21 | this.closed = true; 22 | this.list.forEach((o) => o.close()); 23 | this.list = []; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/sqrl-common/__tests__/bufferToHexEncodedAscii.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { bufferToHexEncodedAscii } from "../src/bufferToHexEncodedAscii"; 7 | 8 | test("works", () => { 9 | const hello = Buffer.from("hello"); 10 | expect(bufferToHexEncodedAscii(hello)).toEqual("hello"); 11 | expect( 12 | bufferToHexEncodedAscii(Buffer.concat([Buffer.from([0]), hello])) 13 | ).toEqual("\\x00hello"); 14 | expect( 15 | bufferToHexEncodedAscii(Buffer.from(`one\ntwo\nslash\\slash\n`)) 16 | ).toEqual("one\\x0atwo\\x0aslash\\x5cslash\\x0a"); 17 | }); 18 | -------------------------------------------------------------------------------- /packages/sqrl-common/src/removeIndent.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { invariant } from "./invariant"; 7 | 8 | /** 9 | * Removes the indent on given lines, useful for adding docstrings to formatted code 10 | */ 11 | export function removeIndent(str: string) { 12 | invariant( 13 | str.substring(0, 1) === "\n", 14 | "Expected removeIndent() strings to start with a new line" 15 | ); 16 | const text = str.substring(1).trimRight(); 17 | const indentLength = /^ */.exec(text)[0].length; 18 | return text.replace(new RegExp(`^ {${indentLength}}`, "mg"), ""); 19 | } 20 | -------------------------------------------------------------------------------- /packages/sqrl-cli/__tests__/cli/repl.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { runCli } from "../helpers/runCli"; 7 | 8 | test("repl works", async () => { 9 | const stdout = await runCli( 10 | ["repl"], 11 | ` 12 | LET X := 5; 13 | LET Y := X + 3; 14 | LET Ip := entity('Ip', '1.2.3.4'); 15 | LET Session := sessionize(BY Ip MAX 3 EVERY HOUR); LET Count := count(BY Session LAST HOUR); 16 | ` 17 | .trim() 18 | .replace(/^ */g, "") + "\n" 19 | ); 20 | 21 | expect(stdout).toEqual("sqrl> 5\nsqrl> 8\nsqrl> '1.2.3.4'\nsqrl> 1\nsqrl> "); 22 | }); 23 | -------------------------------------------------------------------------------- /packages/sqrl/src/jslib/chunk.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import invariant from "./invariant"; 7 | 8 | function chunk(arr: T[], size: number): T[][] { 9 | invariant(Array.isArray(arr), "chunk() takes an array"); 10 | invariant(typeof size === "number", "chunk() takes a size"); 11 | invariant(arr.length === 0 || size > 0, "chunk() requires non-zero size"); 12 | 13 | const chunks: T[][] = []; 14 | for (let start = 0; start < arr.length; start += size) { 15 | chunks.push(arr.slice(start, start + size)); 16 | } 17 | return chunks; 18 | } 19 | 20 | export = chunk; 21 | -------------------------------------------------------------------------------- /packages/sqrl/src/jslib/mapRegExpMatches.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import invariant from "./invariant"; 7 | 8 | export function mapRegExpMatches( 9 | regexp: RegExp, 10 | haystack: string, 11 | map: (result: RegExpExecArray) => T 12 | ): T[] { 13 | invariant(regexp.global, "Expected global regexp"); 14 | regexp.lastIndex = 0; 15 | 16 | let match: RegExpExecArray; 17 | const results: T[] = []; 18 | match = regexp.exec(haystack); 19 | while (match) { 20 | results.push(map(match)); 21 | match = regexp.exec(haystack); 22 | } 23 | return results; 24 | } 25 | -------------------------------------------------------------------------------- /website/source/language/labels.md: -------------------------------------------------------------------------------- 1 | title: Labels 2 | --- 3 | 4 | # Labels 5 | 6 | You'll often want to keep a little bit of additional information attached to the various [entities](entities.html) in your system. 7 | 8 | Usually these labels are applied as a result of rules, for example: 9 | 10 | ``` 11 | CREATE RULE SignupFromTor WHERE isTorExitNode(Ip); 12 | WHEN SignupFromTor THEN addLabel(User, "tor_signup"); 13 | ``` 14 | 15 | This will let you later keep track of users that were created via Tor and apply stricter ratelimits. 16 | 17 | _Note_: The `isTorExitNode` function is not included in this package. You can define it yourself though. See [defining functions](../functions/simple.html) to get started! 18 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/layout/_partial/google_analytics.ejs: -------------------------------------------------------------------------------- 1 | <% if (config.theme_config.google_analytics){ %> 2 | 12 | <% } %> 13 | -------------------------------------------------------------------------------- /website/source/stdlib/type.md: -------------------------------------------------------------------------------- 1 | title: Type Functions 2 | --- 3 | 4 | # Type Functions 5 | 6 | ## basic 7 | 8 | **basic**(value) 9 | 10 | Returns the basic representation of the given value 11 | 12 | ## bool 13 | 14 | **bool**(value) 15 | 16 | Returns the boolean value of the given input value 17 | 18 | ## float 19 | 20 | **float**(value) 21 | 22 | Returns the floating point value of the given input value 23 | 24 | ## int 25 | 26 | **int**(value) 27 | 28 | Returns the integer value of the given input value 29 | 30 | ## list 31 | 32 | **list**(value[, ...]) 33 | 34 | Returns a list of the provided values 35 | 36 | ## str 37 | 38 | **str**(value) 39 | 40 | Creates a string representation of the given value 41 | 42 | -------------------------------------------------------------------------------- /packages/sqrl-common/src/bufferToHexEncodedAscii.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | export function bufferToHexEncodedAscii(buffer: Buffer): string { 7 | let output = ""; 8 | const min = " ".charCodeAt(0); 9 | const max = "~".charCodeAt(0); 10 | const escapeCharCode = "\"'\\".split("").map((s) => s.charCodeAt(0)); 11 | 12 | buffer.forEach((chr) => { 13 | if (chr < min || chr > max || escapeCharCode.includes(chr)) { 14 | output += "\\x" + (chr < 16 ? "0" : "") + chr.toString(16); 15 | } else { 16 | output += String.fromCharCode(chr); 17 | } 18 | }); 19 | return output; 20 | } 21 | -------------------------------------------------------------------------------- /packages/sqrl/src/jslib/pendingPromise.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import * as bluebird from "bluebird"; 7 | 8 | export interface PendingPromise { 9 | resolve: (value?: any) => void; 10 | reject: (err: Error) => void; 11 | promise: bluebird; 12 | } 13 | 14 | export default function pendingPromise(): PendingPromise { 15 | let reject: (err: Error) => void; 16 | let resolve: (value: T) => void; 17 | const promise: bluebird = new bluebird((resolve_, reject_) => { 18 | resolve = resolve_; 19 | reject = reject_; 20 | }); 21 | return { resolve, reject, promise }; 22 | } 23 | -------------------------------------------------------------------------------- /website/source/stdlib/math.md: -------------------------------------------------------------------------------- 1 | title: Math Functions 2 | --- 3 | 4 | # Math Functions 5 | 6 | ## abs 7 | 8 | **abs**(number) 9 | 10 | Returns the absolute (non-negative) value of the given number 11 | 12 | ## log10 13 | 14 | **log10**(number) 15 | 16 | Returns the base 10 logarithm of the given number 17 | 18 | ## max 19 | 20 | **max**(number list) 21 | 22 | Returns the maximum value in the list provided 23 | 24 | ## min 25 | 26 | **min**(number list) 27 | 28 | Returns the minimum value in the list provided 29 | 30 | ## round 31 | 32 | **round**(number) 33 | 34 | Returns the rounded value of the given number 35 | 36 | ## sum 37 | 38 | **sum**(number list) 39 | 40 | Returns the sum of the values in the list provided 41 | 42 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/plugins/swagger-routes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getFilter = (hexo) => { 4 | 5 | const swaggerStore = require('../lib/nodejs/swagger-store')({hexo}); 6 | 7 | return () => { 8 | const routes = swaggerStore.getRoutes(); 9 | routes && Object 10 | .keys(routes) 11 | .forEach((route) => { 12 | const data = routes[route]; 13 | 14 | if(data){ 15 | hexo.route.set(route, data); 16 | }else{ 17 | hexo.route.remove(route); 18 | } 19 | }) 20 | } 21 | } 22 | 23 | 24 | module.exports = ({hexo}) => { 25 | const filter = getFilter(hexo); 26 | hexo.extend.filter.register('after_generate', filter); 27 | }; 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "lib": ["es2019", "es2020.bigint", "es2020.string", "es2020.symbol.wellknown"], 7 | "target": "es2019", 8 | "strict": true, 9 | "outDir": "./lib", 10 | "preserveConstEnums": true, 11 | "removeComments": true, 12 | "inlineSourceMap": false, 13 | "typeRoots": ["./node_modules/@types"], 14 | "experimentalDecorators": true, 15 | "noImplicitAny": false, 16 | "noImplicitReturns": true, 17 | "noImplicitThis": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": false, 20 | "strictNullChecks": false 21 | }, 22 | "exclude": ["node_modules"] 23 | } 24 | -------------------------------------------------------------------------------- /examples/wikipedia/features.sqrl: -------------------------------------------------------------------------------- 1 | LET EventData := input(); 2 | LET SqrlClock := jsonValue(EventData, '$.meta.dt'); 3 | 4 | LET Domain := jsonValue(EventData, "$.meta.domain"); 5 | LET Timestamp := jsonValue(EventData, "$.timestamp"); 6 | LET Title := jsonValue(EventData, "$.title"); 7 | LET User := entity("User", jsonValue(EventData, "$.user")); 8 | LET PageUrl := jsonValue(EventData, "$.meta.uri"); 9 | LET AddedContent := jsonValue(EventData, "$.content.added"); 10 | LET OldRev := jsonValue(EventData, "$.revision.old"); 11 | LET NewRev := jsonValue(EventData, "$.revision.new"); 12 | LET DiffUrl := concat( 13 | "https://", Domain, "/w/index.php?", 14 | "title=", escapeURI(Title), "&type=revision&", 15 | "diff=", NewRev, "&oldid=", OldRev 16 | ); -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sqrl-website", 3 | "private": true, 4 | "version": "0.6.4", 5 | "publishConfig": { 6 | "registry": "https://registry.npmjs.org" 7 | }, 8 | "hexo": { 9 | "version": "5.2.0" 10 | }, 11 | "dependencies": { 12 | "hexo": "^5.2.0", 13 | "hexo-cli": "^4.2.0", 14 | "hexo-deployer-git": "^1.0.0", 15 | "hexo-renderer-ejs": "^0.3.1", 16 | "hexo-renderer-less": "^1.0.0", 17 | "hexo-renderer-marked": "^2.0.0", 18 | "hexo-renderer-sass": "^0.4.0", 19 | "hexo-server": "^0.3.3", 20 | "sqrl-hexo-theme-doc": "file:themes/sqrl-hexo-theme-doc" 21 | }, 22 | "scripts": { 23 | "deploy-website": "hexo deploy --generate", 24 | "serve-website": "hexo serve" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/sqrl/src/compile/sqrlSourceArrow.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | export function sqrlSourceArrow(location?): string { 7 | const { start, end, source } = location; 8 | const sourceLines = source.split("\n"); 9 | 10 | let output = ""; 11 | for (let line = start.line; line <= end.line; line++) { 12 | const source = sourceLines[line - 1]; 13 | const left = line > start.line ? 0 : start.column - 1; 14 | const right = line < end.line ? source.length : end.column - 1; 15 | output += source + "\n"; 16 | output += " ".repeat(left) + "^".repeat(right - left) + "\n"; 17 | } 18 | 19 | return output; 20 | } 21 | -------------------------------------------------------------------------------- /examples/sqrl-example-functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sqrl-example-functions", 3 | "version": "0.6.8", 4 | "main": "./lib/index.js", 5 | "publishConfig": { 6 | "registry": "https://registry.npmjs.org" 7 | }, 8 | "author": { 9 | "name": "Josh Yudaken", 10 | "email": "j@yud.co.za" 11 | }, 12 | "license": "GLP-3.0", 13 | "scripts": { 14 | "compile": "tsc -p ./", 15 | "watch": "tsc -watch -p ./" 16 | }, 17 | "peerDependencies": { 18 | "sqrl": ">= 0.0.1 < 1" 19 | }, 20 | "devDependencies": { 21 | "@types/node": "^11.9.5", 22 | "tslint": "^5.19.0", 23 | "typescript": "^3.9.7" 24 | }, 25 | "dependencies": { 26 | "sqrl": "^0.6.8" 27 | }, 28 | "gitHead": "d6f4789442ac2e1dea7b1f39b87c6db3395fd815" 29 | } 30 | -------------------------------------------------------------------------------- /packages/sqrl-load-functions/__tests__/load.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import * as path from "path"; 7 | import { runSqrlTest, LocalFilesystem } from "sqrl"; 8 | import { register } from "../src"; 9 | 10 | test("Loading YAML works", async () => { 11 | const filesystem = new LocalFilesystem(path.join(__dirname, "testdata")); 12 | 13 | await runSqrlTest( 14 | ` 15 | LET MyData := loadYaml("sample.yaml"); 16 | ASSERT attr(MyData, "string") = "hello world"; 17 | `, 18 | { 19 | filesystem, 20 | register: async (instance) => { 21 | await register(instance); 22 | }, 23 | } 24 | ); 25 | }); 26 | -------------------------------------------------------------------------------- /examples/main.sqrl: -------------------------------------------------------------------------------- 1 | LET EventName := input(); 2 | LET RequestIp := input(); 3 | 4 | LET Ip := entity('Ip', RequestIp); 5 | LET IsSignup := EventName IN ["signup", "new_account"]; 6 | LET PreviouslyRateLimited := hasLabel(Ip, "rate_limited"); 7 | 8 | # Limit user to three signups every 30 seconds, but only one if they previously 9 | # hit a ratelimit. 10 | CREATE RULE SignupRateLimit 11 | WHERE IsSignup AND ( 12 | rateLimited(BY Ip MAX 3 EVERY 30 SECONDS WHERE IsSignup) OR 13 | rateLimited(BY Ip MAX 1 EVERY 30 SECONDS WHERE IsSignup AND PreviouslyRateLimited) 14 | ); 15 | 16 | CREATE RULE ActionRateLimit WHERE rateLimited(BY Ip MAX 5 EVERY 30 SECONDS); 17 | 18 | WHEN ActionRateLimit, SignupRateLimit THEN 19 | blockAction(), 20 | addLabel(Ip, "rate_limited"); 21 | -------------------------------------------------------------------------------- /packages/sqrl/src/api/test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { SqrlTest } from "../testing/SqrlTest"; 7 | 8 | export { buildTestInstance } from "../testing/runSqrlTest"; 9 | 10 | export { runSqrlTest } from "../simple/runSqrlTest"; 11 | export { SimpleManipulator } from "../simple/SimpleManipulator"; 12 | export { TestLogger } from "../simple/TestLogger"; 13 | 14 | /** 15 | * A SQRL Executable is the compiled verison of SQRL source files. It can be 16 | * cheaply executed for new events. 17 | */ 18 | export class Test { 19 | constructor( 20 | /** 21 | * @internal 22 | */ 23 | public _wrapped: SqrlTest 24 | ) {} 25 | } 26 | -------------------------------------------------------------------------------- /packages/sqrl-text-functions/__tests__/simhash.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | 7 | import { textSqrlTest } from "./helpers/textSqrlTest"; 8 | 9 | textSqrlTest( 10 | "works", 11 | ` 12 | 13 | LET StoryOne := 'Once there was a horse named one, who was a very good horse indeed and loved to race every single day. This was true until one day there was a terrible accident.'; 14 | LET StoryTwo := 'Once there was a horse named two, who was a very good horse indeed and loved to race every single day. This was true until one day there was a terrible accident.'; 15 | 16 | ASSERT simhash(StoryOne) = "d9410e85"; 17 | ASSERT simhash(StoryTwo) = "d9410685"; 18 | 19 | ` 20 | ); 21 | -------------------------------------------------------------------------------- /packages/sqrl/__tests__/helpers/TestFunctions.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { SqrlInstance } from "../../src/function/Instance"; 7 | import { AstTypes as AT } from "../../src/ast/AstTypes"; 8 | import { SqrlExecutionState } from "../../src/execute/SqrlExecutionState"; 9 | import { Manipulator } from "../../src/api/execute"; 10 | 11 | export function registerTestFunctions(instance: SqrlInstance) { 12 | instance.save( 13 | function getSqrlOutput(state: SqrlExecutionState) { 14 | const manipulator: Manipulator = state.manipulator as any; 15 | return manipulator.getCurrentHumanOutput(); 16 | }, 17 | { args: [AT.state, AT.any], allowNull: true } 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /packages/sqrl-redis-functions/src/addressToHostPort.ts: -------------------------------------------------------------------------------- 1 | import { invariant } from "sqrl-common"; 2 | 3 | /** 4 | * Copyright 2018 Twitter, Inc. 5 | * Licensed under the Apache License, Version 2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | */ 8 | 9 | export function addressToHostPort( 10 | address: string, 11 | defaultPort: number 12 | ): [string, number] { 13 | const split = address.split(":"); 14 | if (split.length === 1) { 15 | return [split[0], defaultPort]; 16 | } else if (split.length === 2) { 17 | const port = parseInt(split[1], 10); 18 | invariant( 19 | port > 0 && "" + port === split[1], 20 | "Invalid address: %s", 21 | address 22 | ); 23 | return [split[0], port]; 24 | } else { 25 | throw new Error("Invalid address: " + address); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/source/style/_doc/content.scss: -------------------------------------------------------------------------------- 1 | // 2 | // content 3 | // 4 | .doc-content { 5 | position: relative; 6 | top: 0; 7 | right: 0; 8 | bottom: 0; 9 | left: 0; 10 | width: auto; 11 | height: auto; 12 | min-height: 100vh; 13 | padding-top: $doc-navbar-height; 14 | padding-bottom: 420px; 15 | z-index: 2; 16 | transition: 0.1s ease-in-out; 17 | 18 | max-width: 80em; 19 | transform: translateX($doc-sidebar-width); 20 | margin-right: $doc-sidebar-width; 21 | 22 | .dc-page { 23 | overflow: auto; 24 | padding-top: 0; 25 | 26 | @media screen and (max-width: $doc-breakpoint) { 27 | padding-top: 0.8rem; 28 | } 29 | } 30 | 31 | @media screen and (max-width: $doc-breakpoint) { 32 | transform: translateX(0); 33 | margin-right: 0; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/sqrl/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | export * from "./api/arg"; 7 | export * from "./api/ast"; 8 | export * from "./api/ctx"; 9 | export * from "./api/config"; 10 | export * from "./api/entity"; 11 | export * from "./api/execute"; 12 | export * from "./api/compile"; 13 | export * from "./api/expr"; 14 | export * from "./api/feature"; 15 | export * from "./api/filesystem"; 16 | export * from "./api/log"; 17 | export * from "./api/node"; 18 | export * from "./api/object"; 19 | export * from "./api/parse"; 20 | export * from "./api/test"; 21 | export * from "./api/when"; 22 | export * from "./api/spec"; 23 | export * from "./api/compile"; 24 | 25 | export { AssertService, sqrlCompare } from "sqrl-common"; 26 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/plugins/swagger-ui.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const createSwaggerUI = require('../lib/nodejs/swagger-ui/index'); 4 | const util = require('../lib/nodejs/hexo-util'); 5 | 6 | module.exports = ({hexo}) => { 7 | const {themeConfig} = util({hexo}); 8 | const {swaggerUITag, swaggerUIProcessor} = createSwaggerUI({hexo}); 9 | 10 | themeConfig({ swagger_ui: createSwaggerUI.DEFAULT_CONFIG }); 11 | 12 | hexo.extend.tag.register('swagger_ui', swaggerUITag, {async: true}); 13 | hexo.extend.tag.register('swagger_ui_advanced', swaggerUITag, {async: true, ends: true}); 14 | 15 | /** 16 | * This funtion is called when any file is processed. 17 | * It is automatically hooked to the watch task and is called if any file is modified. 18 | * */ 19 | hexo.extend.processor.register('*', swaggerUIProcessor); 20 | }; 21 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/zappr.md: -------------------------------------------------------------------------------- 1 | # Zappr 2 | 3 | We use [zappr](https://github.com/zalando/zappr) to enforce commit message patterns, PR specification correctness, etc. 4 | 5 | 6 | ## Zappr Template for Code Repositories 7 | 8 | ```yaml 9 | approvals: 10 | minimum: 2 # PR needs at least 2 approvals 11 | pattern: "^(:\\+1:|👍|approved)$" # write a comment to the PR with "approved" or ":+1" 12 | veto: 13 | pattern: "^(:\\-1:|👎|rejected)$" # write a comment to the PR with "rejected" or ":-1" 14 | from: 15 | orgs: 16 | - zalando-incubator 17 | - zalando 18 | collaborators: true 19 | commit: 20 | message: 21 | patterns: 22 | # follow commit guidelines CONTRIBUTING.md#git-commit-guidelines 23 | - "^(feat|fix|docs|style|refactor|perf|test|chore)(|\\([a-zA-Z0-9-._]+\\)):.{3,}" 24 | ``` 25 | -------------------------------------------------------------------------------- /packages/sqrl-common/src/flatten.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { invariant } from "./invariant"; 7 | 8 | // Flattens an array containing arrays into a single array 9 | // Only performs a single level 10 | 11 | export function flatten(array) { 12 | invariant(Array.isArray(array), "Flatten must be given an array to flatten"); 13 | if (!array.length) { 14 | return array; 15 | } 16 | array.forEach(function (inner) { 17 | invariant( 18 | Array.isArray(inner), 19 | "Every inner element of the array must be an array" 20 | ); 21 | }); 22 | // @NOTE: This could be done without a function but is pretty hairy as it is 23 | return Array.prototype.concat.call.apply(Array.prototype.concat, array); 24 | } 25 | -------------------------------------------------------------------------------- /scripts/clean-pegjs-ts: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | SRC="$1" 3 | 4 | if [ ! -e "$SRC" ] || [[ ! "$SRC" == src* ]]; then 5 | echo "Usage: ./clean-pegjs-ts " >&2 6 | exit 1 7 | fi 8 | 9 | export PATH="$(dirname $0)/../node_modules/.bin:$PATH" 10 | 11 | # @todo: Could use `sed` on linux 12 | GSED="gsed" 13 | 14 | ### 15 | # @NOTE: These are some hacks to remove typescript errors from the generated file 16 | # Please triple check produced diff when using it 17 | for line in `tsc | grep "^$SRC" | grep "'s1' is declared but its value is never read" | cut '-d(' -f2 | cut -d, -f1`; do 18 | $GSED -i "${line}s/let s0, s1;/let s0;/" $SRC 19 | done 20 | 21 | for line in `tsc | grep "^$SRC" | grep "Cannot find name 's1'" | cut '-d(' -f2 | cut -d, -f1 | sort --reverse -n`; do 22 | $GSED -i "${line}d" "$SRC" 23 | done 24 | 25 | prettier --write "$SRC" 26 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/.eslintrc: -------------------------------------------------------------------------------- 1 | extends: 2 | - eslint:recommended 3 | plugins: 4 | - jest 5 | env: 6 | es6: true 7 | jest: true 8 | node: true 9 | rules: 10 | semi: 2 11 | strict: 2 12 | indent: 13 | - error 14 | - 2 15 | quotes: 16 | - 1 17 | - single 18 | - avoid-escape 19 | space-before-function-paren: 20 | - 2 21 | - always 22 | keyword-spacing: 23 | - 2 24 | - before: true 25 | after: true 26 | space-infix-ops: 2 27 | spaced-comment: 28 | - 2 29 | - always 30 | arrow-spacing: 2 31 | no-console: 2 32 | no-empty: 2 33 | eqeqeq: 34 | - 2 35 | - always 36 | no-unused-vars: 2 37 | no-unsafe-negation: 2 38 | prefer-const: 2 39 | no-var: 2 40 | 41 | # Jest 42 | jest/no-disabled-tests: 1 43 | jest/no-focused-tests: 1 44 | jest/no-identical-title: 2 45 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/source/style/_swagger/swagger-ui-v3.scss: -------------------------------------------------------------------------------- 1 | .hexo-swagger-ui-v3 { 2 | // "namespace" required vendors 3 | @import "./swagger-ui-v3-vendors.scss"; 4 | 5 | .swagger-ui { 6 | 7 | .wrapper { 8 | padding: 0; 9 | } 10 | 11 | .information-container .info { 12 | margin: 0; 13 | } 14 | 15 | .info .title { 16 | font-size: 24px; 17 | font-weight: bold; 18 | } 19 | 20 | .info a { 21 | font-weight: 600; 22 | } 23 | 24 | .info .title small { 25 | display: none; 26 | } 27 | 28 | .info .base-url { 29 | padding: 0; 30 | background: transparent; 31 | } 32 | 33 | .scheme-container { 34 | padding-right: 12px; 35 | padding-left: 12px; 36 | } 37 | 38 | .markdown p { 39 | margin-bottom: 0; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/sqrl-common/src/promiseFinally.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | /*** 7 | * This implements https://github.com/tc39/proposal-promise-finally 8 | * Once our minimum Node.JS version meets this bar we can start using it. 9 | */ 10 | 11 | export function promiseFinally( 12 | promise: Promise, 13 | onFinally: () => void 14 | ): Promise { 15 | // We compile with a TypeScript version that doesn't include .finally 16 | if ((promise as any).finally) { 17 | return (promise as any).finally(onFinally); 18 | } 19 | return promise.then( 20 | (res) => Promise.resolve(onFinally()).then(() => res), 21 | (err) => 22 | Promise.resolve(onFinally()).then(() => { 23 | throw err; 24 | }) 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /packages/sqrl-common/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | export * from "./RenderedSpan"; 7 | export * from "./SqrlBoxed"; 8 | export * from "./SqrlObject"; 9 | 10 | export * from "./bufferToHexEncodedAscii"; 11 | export * from "./compare"; 12 | export * from "./emptyFunction"; 13 | export * from "./ensureArray"; 14 | export * from "./flatten"; 15 | export * from "./foreachObject"; 16 | export * from "./hasConstructor"; 17 | export * from "./invariant"; 18 | export * from "./isEmptyObject"; 19 | export * from "./jsonTemplate"; 20 | export * from "./mapObject"; 21 | export * from "./promiseFinally"; 22 | export * from "./range"; 23 | export * from "./removeIndent"; 24 | export * from "./sqrlCartesianProduct"; 25 | export * from "./AssertService"; 26 | -------------------------------------------------------------------------------- /packages/sqrl/src/ast/ArgumentCheck.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { Ast, CallAst } from "../api/ast"; 7 | 8 | export interface ArgumentCheck { 9 | readonly isOptional: boolean; 10 | readonly isRepeated: boolean; 11 | compileTimeCheck(argAst: Ast, functionAst: CallAst); 12 | runtimeChecker?(value: any); 13 | } 14 | 15 | export class StateArgument implements ArgumentCheck { 16 | readonly isRepeated = false; 17 | readonly isOptional = false; 18 | compileTimeCheck() { 19 | /* no check needed. */ 20 | } 21 | } 22 | 23 | export class WhenCauseArgument implements ArgumentCheck { 24 | readonly isRepeated = false; 25 | readonly isOptional = false; 26 | compileTimeCheck() { 27 | /* no check needed. */ 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/sqrl/__tests__/sqrl/wrappers.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import * as SQRL from "../../src/index"; 7 | 8 | test("wrappers work", async () => { 9 | const instance = SQRL.createInstance(); 10 | 11 | instance.registerSync(function world() { 12 | return "world"; 13 | }); 14 | 15 | instance.registerTransform(function excl(state, ast) { 16 | return SQRL.AstBuilder.constant("!"); 17 | }); 18 | 19 | const ctx = SQRL.createSimpleContext(); 20 | const executable = await SQRL.executableFromString( 21 | instance, 22 | "LET X := concat('Hello ', world(), excl());" 23 | ); 24 | 25 | const execution = await executable.execute(ctx); 26 | await expect(execution.fetchValue("X")).resolves.toEqual("Hello world!"); 27 | }); 28 | -------------------------------------------------------------------------------- /packages/sqrl/src/api/ctx.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { Logger, getGlobalLogger } from "./log"; 7 | import { SimpleContext } from "../platform/Trace"; 8 | import { SimpleDatabaseSet } from "../platform/DatabaseSet"; 9 | 10 | export interface DatabaseSet { 11 | /** 12 | * Returns the dataset id as a string 13 | */ 14 | getDatasetId(): string; 15 | 16 | /** 17 | * Returns an eight byte buffer representing the dataset id 18 | */ 19 | getDatasetIdBuffer(): Buffer; 20 | } 21 | 22 | export interface Context extends Logger { 23 | requireDatabaseSet(): DatabaseSet; 24 | } 25 | 26 | export function createSimpleContext(logger?: Logger) { 27 | return new SimpleContext( 28 | new SimpleDatabaseSet("0"), 29 | logger || getGlobalLogger() 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /website/source/functions/custom.md: -------------------------------------------------------------------------------- 1 | title: Custom Functions 2 | --- 3 | 4 | # Custom functions 5 | 6 | ### Example custom function 7 | 8 | Once you're comfortable creating simple functions you can create a function with custom syntax. We use this functionality to provide our counters that have their own syntax. 9 | 10 | You can view our [`sqrl-redis-functions`](https://github.com/twitter/sqrl/tree/master/packages/sqrl-redis-functions) package for examples on how they were defined. 11 | 12 | ``` 13 | registry.registerCustom(function count( 14 | state: CompileState, 15 | ast: CustomCallAst 16 | ): Ast { 17 | /* 18 | In here `ast.source` is the raw source of the argumets to the count() call. 19 | For example count(BY Id) will give you `ast.source === "BY Id"`. 20 | */ 21 | } 22 | ``` 23 | 24 | You are responsible for parsing the features and any values inside the provided argument source. 25 | -------------------------------------------------------------------------------- /website/source/examples/repl.md: -------------------------------------------------------------------------------- 1 | title: REPL 2 | --- 3 | 4 | # REPL 5 | 6 | ### Trying out the REPL 7 | 8 | Once you start getting a feel for SQRL and want to try play around in realtime, a REPL is included 9 | 10 | ``` 11 | $ ./sqrl repl 12 | sqrl> LET ActionData := {"name": "hi", "user_id": "1.2.3.4"}; 13 | { name: 'hi', user_id: '1.2.3.4' } 14 | sqrl> LET ActionName := jsonValue(ActionData, "$.name") 15 | 'hi' 16 | sqrl> LET UserId := jsonValue(ActionData, "$.user_id") 17 | '1234' 18 | sqrl > LET User := entity('User', UserId) 19 | entity { 20 | uniqueId<2019-01-18T03:58:57.834Z@1> 21 | } 22 | sqrl> printSource(ActionName); 23 | function() { 24 | const f0 = () => 25 | bluebird.resolve(functions.attr(this.slots["ActionData"].value(), "name")); 26 | return this.load("ActionData").then(f0); 27 | } 28 | ``` 29 | 30 | ### Run some rules on Wikipedia 31 | 32 | To see a real life use case, check out the [next example](wikipedia.html) 33 | -------------------------------------------------------------------------------- /packages/sqrl/src/platform/Trace.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { AbstractLogger } from "../util/Logger"; 7 | import { DatabaseSet, Context } from "../api/ctx"; 8 | import { Logger, LogProperties } from "../api/log"; 9 | import invariant from "../jslib/invariant"; 10 | 11 | export class SimpleContext extends AbstractLogger implements Context { 12 | constructor(private databaseSet: DatabaseSet, private logger: Logger) { 13 | super(); 14 | } 15 | 16 | log(level: string, props: LogProperties, format: string, ...param: any[]) { 17 | return this.logger.log(level, props, format, ...param); 18 | } 19 | 20 | requireDatabaseSet(): DatabaseSet { 21 | invariant( 22 | this.databaseSet !== null, 23 | "Context databaseSet was not available" 24 | ); 25 | return this.databaseSet; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/source/style/_swagger/swagger-ui-v2.scss: -------------------------------------------------------------------------------- 1 | .hexo-swagger-ui-v2 { 2 | // "namespace" required vendors 3 | @import "./swagger-ui-v2-vendors"; 4 | 5 | $font-size-small: 14px; 6 | $font-weight-big: 800; 7 | $font-weight-small: 300; 8 | 9 | .swagger-wrap.bootstrap { 10 | font-size: $font-size-small; 11 | font-weight: $font-weight-small; 12 | 13 | .api-infos-contact-url, li a { 14 | font-weight: $font-weight-small; 15 | } 16 | .content { 17 | margin-left: initial; 18 | } 19 | .endpoint-actions a { 20 | font-weight: $font-weight-small; 21 | font-size: $font-size-small; 22 | } 23 | .endpoint-heading a { 24 | font-weight: $font-weight-big; 25 | } 26 | 27 | .endpoint-heading > li, .endpoint-actions > li { 28 | margin-bottom: 0; 29 | } 30 | 31 | .api-version .h4 { 32 | font-weight: $font-weight-big; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/sqrl/src/jslib/deepEqual.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | export default function deepEqual(left, right) { 7 | if (left === right) { 8 | return true; 9 | } else if (Array.isArray(left) && Array.isArray(right)) { 10 | return ( 11 | left.length === right.length && 12 | left.every((el, idx) => { 13 | return deepEqual(el, right[idx]); 14 | }) 15 | ); 16 | } else if (typeof left === "object" && typeof right === "object") { 17 | if (left === null || right === null) { 18 | return false; 19 | } 20 | const keys = Object.keys(left).sort(); 21 | return ( 22 | deepEqual(keys, Object.keys(right).sort()) && 23 | keys.every((key?) => { 24 | return deepEqual(left[key], right[key]); 25 | }) 26 | ); 27 | } else { 28 | return false; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /website/_config.yml: -------------------------------------------------------------------------------- 1 | # Hexo Configuration 2 | ## Docs: https://hexo.io/docs/configuration.html 3 | ## Source: https://github.com/hexojs/hexo/ 4 | 5 | # Site 6 | title: SQRL 7 | author: Josh Yudaken 8 | timezone: UTC 9 | 10 | # URL 11 | ## If your site is put in a subdirectory, set url as 'http://yoursite.com/child' and root as '/child/' 12 | url: https://twitter.github.io/sqrl/ 13 | root: /sqrl/ 14 | 15 | # Writing 16 | relative_link: false 17 | 18 | # Extensions 19 | ## Plugins: https://hexo.io/plugins/ 20 | ## Themes: https://hexo.io/themes/ 21 | theme: sqrl-hexo-theme-doc 22 | 23 | theme_config: 24 | favicon: images/favicon.ico 25 | 26 | ignore: 27 | # development only: ignore sub node_modules when `npm link hexo-theme-doc` 28 | - "**/node_modules/**/*node_modules" 29 | 30 | deploy: 31 | type: git 32 | repo: git@github.com:twitter/sqrl 33 | branch: gh-pages-staging 34 | message: "Site updated: {{ now('YYYY-MM-DD HH:mm:ss') }}" 35 | ignore_hidden: false 36 | -------------------------------------------------------------------------------- /packages/sqrl-common/src/range.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { emptyFunction } from "./emptyFunction"; 7 | import { invariant } from "./invariant"; 8 | 9 | export function range(n: number): number[]; 10 | export function range(n: number, valueFactory: (n: number) => T): T[]; 11 | export function range(n, valueFactory?) { 12 | invariant(typeof n === "number", "expected range(, [factory])"); 13 | valueFactory = valueFactory || ((v) => v); 14 | return rangeFromTo(0, n, valueFactory); 15 | } 16 | 17 | export function rangeFromTo( 18 | from, 19 | to, 20 | valueFactory = emptyFunction.thatReturnsArgument 21 | ) { 22 | invariant( 23 | typeof from === "number" && typeof to === "number", 24 | "expected range.fromTo(, , [factory])" 25 | ); 26 | return Array.from(Array(to - from), (_, i) => valueFactory(i + from)); 27 | } 28 | -------------------------------------------------------------------------------- /packages/sqrl-cli/src/spanToShell.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { RenderedSpan } from "sqrl-common"; 7 | import chalk from "chalk"; 8 | 9 | const classColor = { 10 | type: "magentaBright", 11 | "type:syntax": "magenta", 12 | value: "blueBright", 13 | separator: "blue", 14 | "value:json": "gray", 15 | }; 16 | 17 | export function spanToShell(span: RenderedSpan): string { 18 | let color: string = null; 19 | for (let idx = 0; idx < span.class.length; idx++) { 20 | const joined = span.class.slice(0, idx + 1).join(":"); 21 | color = classColor[joined] || color; 22 | } 23 | 24 | let rv: string; 25 | if (span.children) { 26 | rv = span.children.map((child) => spanToShell(child)).join(""); 27 | } else { 28 | rv = span.text; 29 | } 30 | if (color) { 31 | return chalk[color](rv); 32 | } else { 33 | return rv; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/sqrl/src/object/SqrlDateTime.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { SqrlObject } from "./SqrlObject"; 7 | import { RenderedSpan } from "sqrl-common"; 8 | import { mkSpan } from "./span"; 9 | 10 | export default class SqrlDateTime extends SqrlObject { 11 | isoString: string; 12 | 13 | constructor(public timeMs: number) { 14 | super(); 15 | this.isoString = new Date(this.timeMs).toISOString(); 16 | } 17 | 18 | render(): RenderedSpan { 19 | return mkSpan("type:date", [ 20 | mkSpan("type:name", "date"), 21 | mkSpan("type:syntax", "<"), 22 | mkSpan("value:date", this.isoString), 23 | mkSpan("type:syntax", ">"), 24 | ]); 25 | } 26 | 27 | getData() { 28 | return this.isoString; 29 | } 30 | getBasicValue() { 31 | return this.isoString; 32 | } 33 | tryGetTimeMs() { 34 | return this.timeMs; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /website/source/stdlib/data.md: -------------------------------------------------------------------------------- 1 | title: Data Functions 2 | --- 3 | 4 | # Data Functions 5 | 6 | ## attr 7 | 8 | **attr**(object, key) 9 | 10 | Returns the given attribute off the data 11 | 12 | ## createMap 13 | 14 | **createMap**(key, value, (key, value)...) 15 | 16 | Create a map given the key, value pairs 17 | 18 | ## hasAttr 19 | 20 | **hasAttr**(object, key) 21 | 22 | Returns true if the given attribute is set on the data 23 | 24 | ## jsonParse 25 | 26 | **jsonParse**(string) 27 | 28 | Parses the provided JSON encoded string 29 | 30 | ## jsonPath 31 | 32 | **jsonPath**(object, path string) 33 | 34 | Returns the values matching the given JSONPath query 35 | 36 | ## jsonValue 37 | 38 | **jsonValue**(object, path string) 39 | 40 | Returns the value at the given path in the JSON object 41 | 42 | ## keys 43 | 44 | **keys**(object) 45 | 46 | Returns a list of all the keys in the given object 47 | 48 | ## mergeMaps 49 | 50 | **mergeMaps**(map, map...) 51 | 52 | Merges the given maps together 53 | 54 | -------------------------------------------------------------------------------- /packages/sqrl-text-functions/__tests__/charGrams.spec.ts: -------------------------------------------------------------------------------- 1 | import { textSqrlTest } from "./helpers/textSqrlTest"; 2 | 3 | /** 4 | * Copyright 2018 Twitter, Inc. 5 | * Licensed under the Apache License, Version 2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | */ 8 | 9 | textSqrlTest( 10 | "charGrams", 11 | ` 12 | LET ActorEmail := "abc123def"; 13 | LET SimpleString := "abcdefghi"; 14 | LET CharTriGrams := charGrams(SimpleString, 3); 15 | LET CharBiGrams := charGrams(SimpleString, 2); 16 | LET CharGrams := charGrams(SimpleString, 1); 17 | 18 | ASSERT CharTriGrams = [ 19 | "abc", 20 | "bcd", 21 | "cde", 22 | "def", 23 | "efg", 24 | "fgh", 25 | "ghi" 26 | ]; 27 | 28 | ASSERT CharBiGrams = [ 29 | "ab", 30 | "bc", 31 | "cd", 32 | "de", 33 | "ef", 34 | "fg", 35 | "gh", 36 | "hi" 37 | ]; 38 | 39 | ASSERT CharGrams = [ 40 | "a", 41 | "b", 42 | "c", 43 | "d", 44 | "e", 45 | "f", 46 | "g", 47 | "h", 48 | "i" 49 | ]; 50 | ` 51 | ); 52 | -------------------------------------------------------------------------------- /website/source/language/entities.md: -------------------------------------------------------------------------------- 1 | title: Entities 2 | --- 3 | 4 | # Entities 5 | 6 | Entities are a core concept of **SQRL**. They represent a single entity that we might want to label, count against, or track in any way. They are represented by the string `/`. For example `Ip/1.2.3.4`, Email/josh@example.com, or `User/1234`. 7 | 8 | The first time we see an entity, they are assigned a unique id based on the current time. Creating an entity in the SQRL repl is as easy as running: 9 | 10 | ``` 11 | $ ./sqrl repl 12 | sqrl> LET User := entity('User', '1234') 13 | entity { 14 | uniqueId<2019-03-01T06:28:44.925Z@1> 15 | } 16 | '1234' 17 | sqrl> User='1234' 18 | true 19 | sqrl> str(date(User)) 20 | '2019-03-01T06:28:44.925Z' 21 | ``` 22 | 23 | The value of the `entity()` is the same as the string key, so in the example above `User="1234"` **however** they also have additional properties such as creation time. Running `date(User)` above would give you the timestamp it was created: '2019-03-01T06:28:44.925Z' 24 | -------------------------------------------------------------------------------- /packages/sqrl-redis-functions/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { registerCountFunctions } from "./CountFunctions"; 7 | import { registerCountUniqueFunctions } from "./CountUniqueFunctions"; 8 | import { registerRateLimitFunctions } from "./RateLimitFunctions"; 9 | import { registerLabelFunctions } from "./LabelFunctions"; 10 | import { RedisServices } from "./ServiceHelpers"; 11 | import { Instance } from "sqrl"; 12 | import { registerEntityFunctions } from "./EntityFunctions"; 13 | 14 | export function register(instance: Instance) { 15 | const services = new RedisServices(instance.getConfig()); 16 | 17 | registerCountFunctions(instance, services.count); 18 | registerCountUniqueFunctions(instance, services.countUnique); 19 | registerEntityFunctions(instance, services.uniqueId); 20 | registerLabelFunctions(instance, services.label); 21 | registerRateLimitFunctions(instance, services.rateLimit); 22 | } 23 | -------------------------------------------------------------------------------- /packages/sqrl/src/browser/LocalFilesystem.ts: -------------------------------------------------------------------------------- 1 | import { Filesystem } from "../api/filesystem"; 2 | 3 | export class LocalFilesystem extends Filesystem { 4 | constructor() { 5 | super(); 6 | throw new Error("LocalFilesystem is not supported in the browser."); 7 | } 8 | 9 | tryList(path: string): string[] { 10 | throw new Error("LocalFilesystem is not supported in the browser."); 11 | } 12 | 13 | tryRead(filename: string): Buffer { 14 | throw new Error("LocalFilesystem is not supported in the browser."); 15 | } 16 | } 17 | 18 | export function pathDirname(filePath: string) { 19 | throw new Error("Not implemented"); 20 | } 21 | export function pathBasename(filePath: string) { 22 | throw new Error("Not implemented"); 23 | } 24 | export function pathJoin(paths: string[]) { 25 | return paths.join("/"); 26 | } 27 | 28 | export function splitPath( 29 | filePath: string 30 | ): { 31 | dirname: string; 32 | basename: string; 33 | } { 34 | throw new Error("Filesystem functionality is not available in the browser."); 35 | } 36 | -------------------------------------------------------------------------------- /packages/sqrl/src/compile/sqrlErrorWrap.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import invariant from "../jslib/invariant"; 7 | import { SqrlCompileError, SqrlParseErrorOptions } from "../api/parse"; 8 | 9 | export default function sqrlErrorWrap( 10 | options: SqrlParseErrorOptions, 11 | callback: () => T 12 | ): T { 13 | invariant( 14 | typeof callback === "function", 15 | "Expected a two functions for sqrlErrorWrap" 16 | ); 17 | try { 18 | return callback(); 19 | } catch (err) { 20 | if (err instanceof SqrlCompileError) { 21 | err.update(options); 22 | } 23 | throw err; 24 | } 25 | } 26 | 27 | export function asyncSqrlErrorWrap( 28 | options: SqrlParseErrorOptions, 29 | callback: () => Promise 30 | ): Promise { 31 | return callback().catch((err) => { 32 | if (err instanceof SqrlCompileError) { 33 | err.update(options); 34 | } 35 | throw err; 36 | }); 37 | } 38 | -------------------------------------------------------------------------------- /website/source/examples/wikipedia.md: -------------------------------------------------------------------------------- 1 | title: Wikipedia 2 | --- 3 | 4 | # Wikipedia 5 | 6 | Once you get a little further, we have a demonstration that looks for a set of bad words on wikipedia. 7 | 8 | ``` 9 | git clone git@github.com:twitter/sqrl 10 | cd sqrl/examples/wikipedia 11 | npx wikipedia-diff-stream en.wikipedia.org | sqrl run main.sqrl --stream=EventData --only-blocked 12 | 13 | ... 14 | ✗ 2018-11-15 11:25 action was blocked. 15 | ↳ [UsedBadWords]: Matched pattern shit: this is all bullshit 16 | Page: https://en.wikipedia.org/wiki/List_of_synthetic_polymers 17 | Diff: https://en.wikipedia.org/w/index.php?title=List%20of%20synthetic%20polymers&type=revision&diff=868997967&oldid=868800716 18 | Count by user: 3 () 19 | ``` 20 | 21 | If you do run this example you will see **a lot** of false positives. A simple list of bad words 22 | does not make an effective spam filter. The tools provided by SQRL should allow you to combine 23 | separate counters, rate limits, text filters and logic in order to greatly reduce the false 24 | positive rate. -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/banner.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const pkg = require('./package.json'); 5 | const path = require('path'); 6 | 7 | const banner = ` /*! 8 | * ${pkg.name} - ${pkg.version} 9 | * Copyright (c) see LICENSE at ${pkg.homepage}/blob/master/LICENSE 10 | */ 11 | `; 12 | 13 | const files = [ 14 | 'source/style/doc.css', 15 | 'source/style/swagger-ui-v2.css', 16 | 'source/style/swagger-ui-v3.css', 17 | 'source/script/doc.js' 18 | ]; 19 | 20 | files.forEach(writeBanner(banner)); 21 | 22 | function writeBanner (banner) { 23 | return function (filePath) { 24 | const fileExt = path.extname(filePath); 25 | const absFilePath = path.resolve(__dirname, filePath); 26 | let content = fs.readFileSync(absFilePath, 'utf8'); 27 | content = banner + content; 28 | if (fileExt === '.css') { 29 | content = content.replace(/@charset "UTF-8";/g, ''); 30 | content = '@charset "UTF-8";\n' + content; 31 | } 32 | fs.writeFileSync(absFilePath, content); 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /packages/sqrl-text-functions/__tests__/helpers/textSqrlTest.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { register as registerTextFunctions } from "../../src"; 7 | import { register as registerLoadFunctions } from "sqrl-load-functions"; 8 | 9 | import { buildTestInstance, runSqrlTest, Filesystem } from "sqrl"; 10 | 11 | interface Options { 12 | filesystem?: Filesystem; 13 | } 14 | 15 | export async function runTextSqrlTest(sqrl: string, options: Options) { 16 | const instance = await buildTestInstance(); 17 | 18 | return runSqrlTest(sqrl, { 19 | instance, 20 | register: async (instance) => { 21 | await registerLoadFunctions(instance); 22 | await registerTextFunctions(instance); 23 | }, 24 | ...options, 25 | }); 26 | } 27 | 28 | export function textSqrlTest( 29 | description: string, 30 | sqrl: string, 31 | options: Options = {} 32 | ) { 33 | test(description, () => runTextSqrlTest(sqrl, options)); 34 | } 35 | -------------------------------------------------------------------------------- /packages/sqrl/src/browser/JsExecutionContext.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | 7 | import { SqrlInstance } from "../function/Instance"; 8 | import bluebird = require("bluebird"); 9 | 10 | export type JsCallback = () => Promise; 11 | 12 | export class JsExecutionContext { 13 | private context: any; 14 | private cache: { [js: string]: JsCallback } = {}; 15 | 16 | constructor(instance: SqrlInstance) { 17 | this.context = { 18 | console, 19 | functions: instance.functions, 20 | bluebird, 21 | }; 22 | } 23 | 24 | compileSlotJs(js: string): JsCallback { 25 | if (!this.cache[js]) { 26 | const script = `(function({console,functions,bluebird}){return ${js};})`; 27 | this.cache[js] = eval(script)(this.context); 28 | } 29 | return this.cache[js]; 30 | } 31 | 32 | compileSlots(slotJs: string[]): JsCallback[] { 33 | return slotJs.map((js) => this.compileSlotJs(js)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible Node.js debug attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Debug file", 11 | "program": "${workspaceRoot}/lib/${fileBasenameNoExtension}.js", 12 | "cwd": "${workspaceRoot}", 13 | "sourceMaps": true, 14 | "smartStep": true, 15 | "preLaunchTask": "build", 16 | "outFiles": [ 17 | "${workspaceRoot}/lib/*.js" 18 | ] 19 | }, 20 | { 21 | "type": "node", 22 | "request": "launch", 23 | "name": "Debug test", 24 | "program": "${workspaceRoot}/node_modules/jest/bin/jest.js", 25 | "args": [ 26 | "--findRelatedTests", 27 | "${relativeFile}", 28 | "--env", 29 | "jest-environment-node-debug" 30 | ], 31 | "cwd": "${workspaceRoot}" 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /packages/sqrl-redis-functions/__tests__/include.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | // tslint:disable:no-submodule-imports (@TODO) 7 | import { buildRedisTestInstance } from "./helpers/runSqrl"; 8 | import { executableFromFilesystem, VirtualFilesystem } from "sqrl"; 9 | import "jest-extended"; 10 | 11 | test("works with counts", async () => { 12 | const instance = await buildRedisTestInstance(); 13 | const executable = await executableFromFilesystem( 14 | instance, 15 | new VirtualFilesystem({ 16 | "x.sqrl": ` 17 | LET Count := count(BY Ip); 18 | `, 19 | "main.sqrl": ` 20 | LET Action := input(); 21 | LET Ip := input(); 22 | INCLUDE "x.sqrl" WHERE Action="x"; 23 | `, 24 | }) 25 | ); 26 | 27 | const sourcePrinter = executable.getSourcePrinter(); 28 | // Make sure the counter is depending on Action="x" 29 | expect(sourcePrinter.getSourceForSlotName("Count")).toInclude( 30 | 'bool(Action="x":01)' 31 | ); 32 | }); 33 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/plugins/react-initial-state.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = ({hexo}) => { 4 | const allowedProperties = { 5 | config: [ 6 | 'root', 7 | 'theme', 8 | 'theme_config', 9 | 'time_format', 10 | 'timezone' 11 | ], 12 | page: [ 13 | 'path', 14 | 'title', 15 | 'support' 16 | ] 17 | }; 18 | 19 | const filter = (source, allowedValues) => { 20 | return Object 21 | .keys(source) 22 | .filter(key => allowedValues.includes(key)) 23 | .reduce((obj, key) => { 24 | obj[key] = source[key]; 25 | return obj; 26 | }, {}); 27 | }; 28 | 29 | hexo.extend.filter.register('template_locals', function (locals){ 30 | 31 | const data = locals.site.data; 32 | 33 | const page = filter(locals.page, allowedProperties.page); 34 | 35 | const config = filter(locals.config, allowedProperties.config); 36 | 37 | locals.initial_state = { 38 | page, 39 | data, 40 | config 41 | }; 42 | 43 | return locals; 44 | }, 20); 45 | }; 46 | -------------------------------------------------------------------------------- /packages/sqrl-redis-functions/__tests__/helpers/runSqrl.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { 7 | runSqrlTest as runLibSqrl, 8 | buildTestInstance, 9 | Instance, 10 | Logger, 11 | } from "sqrl"; 12 | import { register } from "../../src"; 13 | 14 | export async function buildRedisTestInstance( 15 | options: { 16 | fixedDate?: string; 17 | } = {} 18 | ) { 19 | const instance = await buildTestInstance({ 20 | config: { 21 | "testing.fixed-date": options.fixedDate, 22 | }, 23 | }); 24 | register(instance); 25 | return instance; 26 | } 27 | 28 | export async function runSqrl( 29 | sqrl: string, 30 | options: { 31 | instance?: Instance; 32 | logger?: Logger; 33 | fixedDate?: string; 34 | } = {} 35 | ) { 36 | return runLibSqrl(sqrl, { 37 | instance: 38 | options.instance || 39 | (await buildRedisTestInstance({ 40 | fixedDate: options.fixedDate, 41 | })), 42 | logger: options.logger, 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/source/style/_doc/search.scss: -------------------------------------------------------------------------------- 1 | .doc-search-form { 2 | max-width: 186px; 3 | 4 | &__input[type="search"] { 5 | width: 100%; 6 | box-sizing: border-box; 7 | } 8 | 9 | &__input:focus + &__btn { 10 | color: $dc-blue30; 11 | cursor: default; 12 | } 13 | 14 | &__btn { 15 | &:hover { 16 | color: $dc-gray40; 17 | cursor: default; 18 | } 19 | } 20 | } 21 | 22 | .doc-search-results { 23 | &__title { 24 | margin-top: 0; 25 | line-height: 3.4rem; 26 | 27 | &__query { 28 | font-weight: 500; 29 | } 30 | } 31 | 32 | &__list { 33 | list-style: none; 34 | padding: 0; 35 | 36 | &__item { 37 | display: block; 38 | } 39 | 40 | &__link { 41 | display: inline-block; 42 | font-weight: 500; 43 | } 44 | 45 | &__score-divider { 46 | display: inline-block; 47 | padding: 0 0.8rem; 48 | color: $dc-gray70; 49 | } 50 | 51 | &__score { 52 | color: $dc-gray50; 53 | } 54 | } 55 | 56 | .doc-highlight { 57 | font-weight: 500; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /packages/sqrl/src/node/JsExecutionContext.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { SqrlInstance } from "../function/Instance"; 7 | import bluebird = require("bluebird"); 8 | 9 | import vm = require("vm"); 10 | 11 | export type JsCallback = () => Promise; 12 | 13 | export class JsExecutionContext { 14 | private sandbox: vm.Context; 15 | private cache: { [js: string]: JsCallback } = {}; 16 | 17 | constructor(instance: SqrlInstance) { 18 | this.sandbox = vm.createContext({ 19 | console, 20 | functions: instance.functions, 21 | bluebird, 22 | }); 23 | } 24 | 25 | compileSlotJs(js: string): JsCallback { 26 | if (!this.cache[js]) { 27 | const script = `(function(){return ${js};})()`; 28 | this.cache[js] = new vm.Script(script).runInContext(this.sandbox); 29 | } 30 | return this.cache[js]; 31 | } 32 | 33 | compileSlots(slotJs: string[]): JsCallback[] { 34 | return slotJs.map((js) => this.compileSlotJs(js)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/wikipedia-diff-stream/README.md: -------------------------------------------------------------------------------- 1 | # `wikipedia-diff-stream` package 2 | 3 | This package includes some sample code to fetch live changes happening on Wikipedia and output them including the diff of what changed. 4 | 5 | It is only intended as a demo and samples the stream very heavily. 6 | 7 | # SQRL documentation 8 | 9 | Please see [the website](https://twitter.github.io/sqrl) for full **SQRL** documentation 10 | 11 | ## Support 12 | 13 | Create a [new issue](https://github.com/twitter/sqrl/issues/new) on GitHub. 14 | 15 | ## Contributing 16 | 17 | We feel that a welcoming community is important and we ask that you follow Twitter's 18 | [Open Source Code of Conduct](https://github.com/twitter/code-of-conduct/blob/master/code-of-conduct.md) 19 | in all interactions with the community. 20 | 21 | ## License 22 | 23 | Copyright 2018 Twitter, Inc. 24 | 25 | Licensed under the Apache License, Version 2.0: https://www.apache.org/licenses/LICENSE-2.0 26 | 27 | ## Security Issues 28 | 29 | Please report sensitive security issues via Twitter's bug-bounty program (https://hackerone.com/twitter) rather than GitHub. 30 | -------------------------------------------------------------------------------- /packages/sqrl-redis-functions/__tests__/label.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { runSqrl } from "./helpers/runSqrl"; 7 | 8 | test("Labeling works", async () => { 9 | await runSqrl(` 10 | LET SqrlMutate := true; 11 | LET Ip := entity("Ip", "1.2.3.4"); 12 | 13 | addLabel(Ip, "mylabel") WHERE false; 14 | EXECUTE; 15 | ASSERT NOT hasLabel(Ip, "mylabel"); 16 | 17 | addLabel(Ip, "mylabel"); 18 | EXECUTE; 19 | ASSERT hasLabel(Ip, "mylabel"); # After write 20 | 21 | 22 | removeLabel(Ip, "mylabel"); 23 | EXECUTE; 24 | 25 | ASSERT NOT hasLabel(Ip, "mylabel"); 26 | `); 27 | }); 28 | 29 | test("WHEN works", async () => { 30 | await runSqrl(` 31 | LET A := false; 32 | LET B := false; 33 | LET Ip := entity("Ip", "1.2.3.4"); 34 | CREATE RULE X WHERE A; 35 | WHEN X THEN addLabel(Ip, "bad"); 36 | EXECUTE; 37 | 38 | ASSERT NOT hasLabel(Ip, "bad"); 39 | LET A := true; 40 | EXECUTE; 41 | 42 | ASSERT hasLabel(Ip, "bad"); 43 | `); 44 | }); 45 | -------------------------------------------------------------------------------- /website/source/stdlib/list.md: -------------------------------------------------------------------------------- 1 | title: List Functions 2 | --- 3 | 4 | # List Functions 5 | 6 | ## concat 7 | 8 | **concat**(value, value...) 9 | 10 | Concatenates the given arguments (strings or lists) together 11 | 12 | ## dedupe 13 | 14 | **dedupe**(list) 15 | 16 | Removes duplicate entries from a list 17 | 18 | ## filter 19 | 20 | **filter**(list) 21 | 22 | Removes any falsy values from the given list 23 | 24 | ## first 25 | 26 | **first**(list) 27 | 28 | Returns the first item in the provided list 29 | 30 | ## flatten 31 | 32 | **flatten**(list) 33 | 34 | Reduces multiple levels of lists into a single flat list 35 | 36 | ## index 37 | 38 | **index**(list, index) 39 | 40 | Returns the item at the specified index in a list 41 | 42 | ## join 43 | 44 | **join**(list, string) 45 | 46 | Joins the provided list together using a string 47 | 48 | ## last 49 | 50 | **last**(list) 51 | 52 | Returns the last item in the provided list 53 | 54 | ## length 55 | 56 | **length**(list) 57 | 58 | Returns the length of a provided list 59 | 60 | ## sort 61 | 62 | **sort**(list) 63 | 64 | Returns the provided list in sorted order 65 | 66 | -------------------------------------------------------------------------------- /packages/sqrl/src/jslib/murmurhashJson.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | 7 | import stringify = require("fast-stable-stringify"); 8 | import * as murmurJs from "murmurhash3.js"; 9 | 10 | export function murmurhashJsonHexSync(data: any): string { 11 | const inputBuffer = Buffer.from(stringify(data), "utf8"); 12 | return murmurhashHexSync(inputBuffer); 13 | } 14 | 15 | export async function murmurhashJsonHex(data: any): Promise { 16 | return murmurhashJsonHexSync(data); 17 | } 18 | export async function murmurhashJsonBuffer(data: any): Promise { 19 | const inputBuffer = Buffer.from(stringify(data), "utf8"); 20 | return murmurhashSync(inputBuffer); 21 | } 22 | export async function murmurhashJson(data: any): Promise { 23 | return murmurhashJsonBuffer(data); 24 | } 25 | 26 | export function murmurhashHexSync(data: Buffer): string { 27 | return murmurhashSync(data).toString("hex"); 28 | } 29 | export function murmurhashSync(data: Buffer): Buffer { 30 | return Buffer.from(murmurJs.x64.hash128(data)); 31 | } 32 | -------------------------------------------------------------------------------- /website/source/deployment/queue.md: -------------------------------------------------------------------------------- 1 | title: Queue consumer 2 | --- 3 | 4 | # Deploying SQRL as a queue consumer 5 | 6 | If you're already using a queue such as [Kafka](https://kafka.apache.org), [Amazon SQS](https://aws.amazon.com/sqs/) or one of hundreds of similar products you can link SQRL directly to that. 7 | 8 | Running off a queue your SQRL execution is [asynchronous](async.html) — meaning the event is not blocked waiting for the result of the execution. This means you can afford to do a much deeper analysis without holding up the client waiting to post their comment or signup. These executions can take as long as they want, though the average we've seen is five to thirty seconds. 9 | 10 | Many queueing systems, like Kafka, function best when your messages are very quick to process and can't divvy up work effectively given the long runtimes SQRL often sees. The solution we found at Smyte was to have a separate consumer (we called the "Action Scheduler") that reads a batch of messages from Kafka and divides them among a number of worker machines. This scheduler is then responsible for committing the batch of results to Kafka, and handling timeouts where appropriate. -------------------------------------------------------------------------------- /packages/sqrl-redis-functions/__tests__/helpers/services.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { RedisCountService } from "../../src/services/RedisCountService"; 7 | import { RedisApproxCountUniqueService } from "../../src/services/RedisApproxCountUnique"; 8 | import { RedisLabelService } from "../../src/services/RedisLabelService"; 9 | import { MockRedisService } from "../../src/mocks/MockRedisService"; 10 | import { RedisRateLimit } from "../../src/services/RedisRateLimit"; 11 | import { RedisServices } from "../../src"; 12 | import { RedisUniqueIdService } from "../../src/services/RedisUniqueId"; 13 | 14 | export function buildServices(): RedisServices { 15 | const redis = new MockRedisService(); 16 | return { 17 | count: new RedisCountService(redis, "count~"), 18 | countUnique: new RedisApproxCountUniqueService(redis, "countUnique~"), 19 | rateLimit: new RedisRateLimit(redis, "ratelimit~"), 20 | label: new RedisLabelService(redis, "label~"), 21 | uniqueId: new RedisUniqueIdService(redis, "uniqueId~"), 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/plugins/search.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const generator = require('../lib/nodejs/search/generator'); 4 | const util = require('../lib/nodejs/hexo-util'); 5 | 6 | const DEFAULT_CONFIG = { route: '/lunr.json' }; 7 | 8 | module.exports = ({hexo}) => { 9 | const {themeConfig} = util({hexo}); 10 | hexo.extend.generator.register('search', createGeneratorFn({hexo, themeConfig})); 11 | }; 12 | 13 | function createGeneratorFn ({hexo, themeConfig}) { 14 | const cmd = hexo.env.args._ && hexo.env.args._.length ? hexo.env.args._[0] : null; 15 | 16 | // hexo commands that should activate the generator 17 | const cmds = [ 18 | 'generate', 19 | 'server', 20 | 'deploy', 21 | 'g', 22 | 's', 23 | 'd' 24 | ]; 25 | 26 | // hexo commands that should activate the generator in background mode 27 | const bgCmds = [ 28 | 'server', 29 | 's' 30 | ]; 31 | 32 | const skip = cmds.indexOf(cmd) === -1 && typeof hexo.env.args._ !== 'undefined'; 33 | const background = bgCmds.indexOf(cmd) > -1; 34 | 35 | themeConfig({ search: { skip, background, route: DEFAULT_CONFIG.route } }); 36 | 37 | return generator({hexo}); 38 | } 39 | -------------------------------------------------------------------------------- /packages/sqrl/__tests__/sqrl/test.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { SqrlInstance } from "../../src/function/Instance"; 7 | import { registerAllFunctions } from "../../src/function/registerAllFunctions"; 8 | import { SqrlTest } from "../../src/testing/SqrlTest"; 9 | import { JestAssertService } from "sqrl-test-utils"; 10 | import { SimpleDatabaseSet } from "../../src/platform/DatabaseSet"; 11 | import { SimpleContext } from "../../src/platform/Trace"; 12 | import { getGlobalLogger } from "../../src/api/log"; 13 | 14 | test("Basic test works", async () => { 15 | const instance = new SqrlInstance(); 16 | registerAllFunctions(instance, { 17 | assert: new JestAssertService(), 18 | }); 19 | 20 | const test = new SqrlTest(instance, {}); 21 | const trace = new SimpleContext( 22 | new SimpleDatabaseSet("0"), 23 | getGlobalLogger() 24 | ); 25 | await test.run( 26 | trace, 27 | ` 28 | LET X := 5 + 1; 29 | LET Y := X * 3; 30 | ASSERT Y = 18; 31 | ASSERT Y + 1 = 19; 32 | ASSERT Y = 19 - 1; 33 | ` 34 | ); 35 | expect.assertions(3); 36 | }); 37 | -------------------------------------------------------------------------------- /website/source/packages/sqrl-text-functions.md: -------------------------------------------------------------------------------- 1 | title: sqrl-text-functions 2 | --- 3 | 4 | # sqrl-text-functions 5 | 6 | Functions for doing analysis on text. 7 | 8 | ## charGrams 9 | 10 | **charGrams**(text, size) 11 | 12 | Returns all the chargrams of a given size from the text 13 | 14 | ## normalizeEmail 15 | 16 | **normalizeEmail**(email) 17 | 18 | Returns the normalized form of the given email address 19 | 20 | ## patternMatches 21 | 22 | **patternMatches**(filename, text) 23 | 24 | Match a list of patterns in the given file against the provided text 25 | 26 | ## regexMatch 27 | 28 | **regexMatch**(regex, string) 29 | 30 | Returns the matches of the given regular expression against the string 31 | 32 | ## regexReplace 33 | 34 | **regexReplace**(regex, replacement, string) 35 | 36 | Replaces each match of the given regular expression in the string 37 | 38 | ## regexTest 39 | 40 | **regexTest**(regex, string) 41 | 42 | Returns true if the given regular expression matches the string 43 | 44 | ## sha256 45 | 46 | **sha256**(value) 47 | 48 | Returns the sha256 hash of the given value as hex 49 | 50 | ## simhash 51 | 52 | **simhash**(text) 53 | 54 | Return the simhash of the given text 55 | 56 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | group: deprecated-2017Q4 4 | 5 | cache: 6 | directories: 7 | - node_modules 8 | 9 | language: node_js 10 | 11 | node_js: 12 | - '7' 13 | 14 | addons: 15 | chrome: stable 16 | 17 | before_script: 18 | - npm link 19 | 20 | # prepare e2e test environment 21 | - "export DISPLAY=:99.0" 22 | - "sh -e /etc/init.d/xvfb start" # the starting the virtual X frame buffer: Xvfb process 23 | 24 | - git clone --depth=50 --branch=gh-pages-source https://github.com/zalando-incubator/hexo-theme-doc ../hexo-theme-doc-site 25 | - cd ../hexo-theme-doc-site && npm install -q && npm link hexo-theme-doc 26 | - cd $TRAVIS_BUILD_DIR 27 | 28 | script: 29 | # quality checks, unit test and compiling fe artifacts 30 | - npm run lint -s 31 | - npm run lint:report -s 32 | - npm run test:coverage -s 33 | - npm run compile -s 34 | 35 | # run e2e tests 36 | - cd ../hexo-theme-doc-site && npm run generate 37 | - npm run serve &>/dev/null & 38 | - sleep 5 # give server some time to start 39 | - npm test 40 | - cd $TRAVIS_BUILD_DIR 41 | 42 | after_success: 43 | cat target/coverage/lcov.info | ./node_modules/.bin/codacy-coverage 44 | -------------------------------------------------------------------------------- /packages/sqrl/src/function/EntityFunctions.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { StdlibRegistry } from "./Instance"; 7 | 8 | import { AstTypes as AT } from "../ast/AstTypes"; 9 | import { SqrlExecutionState } from "../execute/SqrlExecutionState"; 10 | import SqrlEntity from "../object/SqrlEntity"; 11 | 12 | export function registerEntityFunctions(instance: StdlibRegistry) { 13 | instance.save( 14 | function uniqueId(state: SqrlExecutionState, uniqueId: SqrlEntity) { 15 | return uniqueId.getNumberString(); 16 | }, 17 | { 18 | allowSqrlObjects: true, 19 | args: [AT.state, AT.any.sqrlEntity], 20 | argstring: "entity", 21 | docstring: "Returns the unique id of the entity as a string", 22 | } 23 | ); 24 | 25 | instance.save( 26 | function entityId(state: SqrlExecutionState, entity: SqrlEntity) { 27 | return entity.entityId.getIdString(); 28 | }, 29 | { 30 | allowSqrlObjects: true, 31 | args: [AT.state, AT.any.sqrlEntity], 32 | argstring: "entity", 33 | docstring: "Returns the entity id of the entity", 34 | } 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /packages/sqrl/__tests__/registry/args.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { createInstance, AT, ArgumentCheck } from "../../src/index"; 7 | 8 | test("throw with out of order optional", async () => { 9 | function tryRegister(args: ArgumentCheck[]) { 10 | const instance = createInstance(); 11 | instance.registerSync( 12 | function hello() { 13 | return "world"; 14 | }, 15 | { 16 | args, 17 | } 18 | ); 19 | } 20 | 21 | tryRegister([AT.any]); 22 | tryRegister([AT.any, AT.any.repeated]); 23 | tryRegister([AT.any, AT.any.optional]); 24 | tryRegister([AT.any, AT.any.optional, AT.any.repeated.optional]); 25 | 26 | expect(() => tryRegister([AT.any.optional, AT.any])).toThrow( 27 | "A non-optional argument cannot follow an optional one" 28 | ); 29 | expect(() => tryRegister([AT.any.optional, AT.any.repeated])).toThrow( 30 | "A non-optional argument cannot follow an optional one" 31 | ); 32 | expect(() => tryRegister([AT.any.repeated, AT.any.repeated])).toThrow( 33 | "Repeated arguments are only valid as the final argument" 34 | ); 35 | }); 36 | -------------------------------------------------------------------------------- /packages/sqrl-cli/__tests__/cli/server.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { runCli } from "../helpers/runCli"; 7 | import * as request from "request-promise-native"; 8 | import { examplePath } from "../helpers/examplePath"; 9 | 10 | test("works", async () => { 11 | let ranTests = false; 12 | 13 | await runCli(["serve", examplePath("hello.sqrl"), "--port=0"], "", { 14 | serverWaitCallback: async ({ server }) => { 15 | const address = server.address(); 16 | if (typeof address === "string") { 17 | throw new Error("Expected server to listen on a port"); 18 | } 19 | 20 | const res = await request({ 21 | method: "post", 22 | uri: `http://127.0.0.1:${address.port}/run?features=Text`, 23 | json: true, 24 | body: { 25 | Name: "SQRL Server Test", 26 | }, 27 | }); 28 | 29 | expect(res.features.Text).toEqual("Hello, SQRL Server Test!"); 30 | ranTests = true; 31 | }, 32 | }); 33 | 34 | // The callback logic above is a little tricky, so make sure we actually ran the tests 35 | expect(ranTests).toBeTruthy(); 36 | }); 37 | -------------------------------------------------------------------------------- /packages/sqrl/src/simple/SimpleManipulator.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { FeatureMap, Manipulator, ManipulatorCallback } from "../api/execute"; 7 | import { SqrlKey } from "../object/SqrlKey"; 8 | 9 | export class SimpleManipulator extends Manipulator { 10 | public sqrlKeys: Set = new Set(); 11 | 12 | readonly loggedErrors: Error[] = []; 13 | public loggedFeatures: FeatureMap = {}; 14 | public logged: string[] = []; 15 | 16 | getCurrentHumanOutput() { 17 | return {}; 18 | } 19 | 20 | trackSqrlKey(key: SqrlKey): void { 21 | this.sqrlKeys.add(key.getDebugString()); 22 | } 23 | 24 | log(message: string) { 25 | this.logged.push(message); 26 | } 27 | 28 | logError(err: Error): void { 29 | this.loggedErrors.push(err); 30 | } 31 | throwFirstError() { 32 | if (this.loggedErrors.length) { 33 | throw this.loggedErrors[0]; 34 | } 35 | } 36 | 37 | async mutate(ctx): Promise { 38 | await Promise.all(this.callbacks.map((cb) => cb(ctx))); 39 | } 40 | private callbacks: ManipulatorCallback[] = []; 41 | addCallback(cb: ManipulatorCallback) { 42 | this.callbacks.push(cb); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/sqrl-cli-functions/src/bufferHumanJson.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { bufferToHexEncodedAscii, mapObject } from "sqrl-common"; 7 | 8 | function friendlyBuffer(buf) { 9 | try { 10 | const text = buf.toString("utf-8"); 11 | return bufferToHexEncodedAscii(text); 12 | } catch (err) { 13 | return `0x${buf.toString("hex")}`; 14 | } 15 | } 16 | 17 | function deepFriendlyMap(obj) { 18 | if (typeof obj === "bigint") { 19 | return obj.toString(); 20 | } else if (obj instanceof Buffer) { 21 | return friendlyBuffer(obj); 22 | } else if (Array.isArray(obj)) { 23 | return obj.map(deepFriendlyMap); 24 | } else if (typeof obj === "object" && obj !== null) { 25 | return mapObject(obj, deepFriendlyMap); 26 | } else { 27 | return obj; 28 | } 29 | } 30 | 31 | /** 32 | * Convert a given buffer into something that is human readable. 33 | * This process is not necessarily reversible and is meant for convenience 34 | * only. 35 | */ 36 | export function bufferHumanJson(msg: Buffer): any { 37 | try { 38 | return JSON.parse(msg.toString("utf-8")); 39 | } catch (err) { 40 | return deepFriendlyMap(msg); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/sqrl-cli/src/cli/WatchedFilesystem.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { Filesystem, LocalFilesystem } from "sqrl"; 7 | import * as chokidar from "chokidar"; 8 | import { join } from "path"; 9 | 10 | export class WatchedFilesystem extends Filesystem { 11 | private watcher: chokidar.FSWatcher; 12 | private fs: LocalFilesystem; 13 | constructor(private pwd: string) { 14 | super(); 15 | this.fs = new LocalFilesystem(pwd); 16 | this.watcher = chokidar.watch([], { 17 | ignoreInitial: true, 18 | persistent: false, 19 | 20 | // @todo: This is not ideal, but on OSX changes are not picked up without it. 21 | usePolling: true, 22 | }); 23 | } 24 | 25 | // Expose a super-simple `on('change')` handler for now 26 | on(event: "change", callback: () => void) { 27 | this.watcher.on("all", callback); 28 | } 29 | private watch(path: string) { 30 | this.watcher.add(join(this.pwd, path)); 31 | } 32 | 33 | tryList(path: string) { 34 | this.watch(path); 35 | return this.fs.tryList(path); 36 | } 37 | tryRead(path: string) { 38 | this.watch(path); 39 | return this.fs.tryRead(path); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/sqrl/src/object/span.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { RenderedSpan } from "sqrl-common"; 7 | 8 | export function mkSpan( 9 | classStr: string, 10 | contents: string | RenderedSpan[] 11 | ): RenderedSpan { 12 | const classes = classStr.split(":"); 13 | if (typeof contents === "string") { 14 | return { 15 | class: classes, 16 | text: contents, 17 | }; 18 | } else { 19 | return { 20 | class: classes, 21 | children: contents, 22 | }; 23 | } 24 | } 25 | 26 | function indentRecurse(span: RenderedSpan, indentString: string) { 27 | if (span.children) { 28 | return { 29 | ...span, 30 | children: span.children.map((child) => 31 | indentRecurse(child, indentString) 32 | ), 33 | }; 34 | } else { 35 | return { 36 | ...span, 37 | text: span.text.replace(/\n/g, `\n${indentString}`), 38 | }; 39 | } 40 | } 41 | 42 | export function indentSpan(span: RenderedSpan, spaces: number): RenderedSpan { 43 | const indentString = " ".repeat(spaces); 44 | return mkSpan("", [ 45 | mkSpan("", indentString), 46 | indentRecurse(span, indentString), 47 | ]); 48 | } 49 | -------------------------------------------------------------------------------- /packages/sqrl/__tests__/sqrl/bool.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { sqrlTest } from "../helpers/sqrlTest"; 7 | 8 | // Check both with/without delay to check compiler optimizations 9 | for (const delayed of [false, true]) { 10 | sqrlTest( 11 | "handles null correctly" + (delayed ? " async" : ""), 12 | ` 13 | LET T := ${delayed ? "delayMs(100, true)" : "true"}; 14 | LET F := ${delayed ? "delayMs(100, false)" : "false"}; 15 | LET N := ${delayed ? "delayMs(100, null)" : "null"}; 16 | 17 | ASSERT repr(N) = "null"; 18 | ASSERT repr(NOT N) = "null"; 19 | ASSERT repr(N OR N) = "null"; 20 | ASSERT repr(F OR N) = "null"; 21 | ASSERT repr(N OR F) = "null"; 22 | ASSERT repr(N AND N) = "null"; 23 | ASSERT repr(N AND T) = "null"; 24 | ASSERT repr(T AND N) = "null"; 25 | ASSERT repr(NOT (N OR N)) = "null"; 26 | ASSERT repr(NOT (N OR F)) = "null"; 27 | ASSERT repr(NOT (N AND N)) = "null"; 28 | ASSERT repr(NOT (N AND T)) = "null"; 29 | ASSERT repr(N OR T) = "true"; 30 | ASSERT repr(T OR N) = "true"; 31 | ASSERT repr(F AND N) = "false"; 32 | ASSERT repr(N AND F) = "false"; 33 | ASSERT repr(NOT (N OR T)) = "false"; 34 | ASSERT repr(NOT (N AND F)) = "true"; 35 | 36 | ` 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /packages/sqrl/src/node/LocalFilesystem.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import { readFileSync, readdirSync } from "fs"; 3 | import { Filesystem } from "../api/filesystem"; 4 | 5 | export class LocalFilesystem extends Filesystem { 6 | constructor(private pwd: string) { 7 | super(); 8 | } 9 | 10 | tryList(dirPath: string) { 11 | try { 12 | return readdirSync(path.join(this.pwd, dirPath)).filter((filename) => 13 | filename.endsWith(".sqrl") 14 | ); 15 | } catch (err) { 16 | if (err.code === "ENOENT") { 17 | return null; 18 | } 19 | throw err; 20 | } 21 | } 22 | 23 | tryRead(filename: string) { 24 | const filePath = path.resolve(this.pwd, filename); 25 | return readFileSync(filePath); 26 | } 27 | } 28 | 29 | export function pathDirname(filePath: string) { 30 | return path.dirname(filePath); 31 | } 32 | export function pathBasename(filePath: string) { 33 | return path.basename(filePath); 34 | } 35 | export function pathJoin(...paths: string[]) { 36 | return path.join(...paths); 37 | } 38 | 39 | export function splitPath( 40 | filePath: string 41 | ): { 42 | dirname: string; 43 | basename: string; 44 | } { 45 | return { 46 | dirname: path.dirname(filePath), 47 | basename: path.basename(filePath), 48 | }; 49 | } 50 | -------------------------------------------------------------------------------- /packages/sqrl-cli/__tests__/helpers/runCli.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { Writable, Readable } from "stream"; 7 | import { invariant } from "sqrl-common"; 8 | import { cliMain, CliMainOptions } from "../../src/cli/CliMain"; 9 | import { CloseableGroup } from "../../src/jslib/Closeable"; 10 | import { parseArgs } from "../../src/cli/CliArgs"; 11 | 12 | class StringBuffer extends Writable { 13 | public string: string = ""; 14 | _write(chunk: Buffer, enc, cb) { 15 | invariant(chunk instanceof Buffer, "expected buffer writes"); 16 | this.string += chunk.toString("utf-8"); 17 | cb(); 18 | } 19 | } 20 | 21 | export async function runCli( 22 | argv: string[], 23 | stdinString?: string, 24 | options: CliMainOptions = {} 25 | ) { 26 | const closeables = new CloseableGroup(); 27 | const stdin = new Readable(); 28 | stdin.push(stdinString); 29 | stdin.push(null); 30 | const stdout = new StringBuffer(); 31 | 32 | const args = parseArgs(argv); 33 | try { 34 | await cliMain(args, closeables, { 35 | ...options, 36 | stdin, 37 | stdout, 38 | }); 39 | } finally { 40 | closeables.close(); 41 | } 42 | 43 | return stdout.string; 44 | } 45 | -------------------------------------------------------------------------------- /packages/sqrl-text-functions/__tests__/pattern.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { VirtualFilesystem } from "sqrl"; 7 | import { textSqrlTest } from "./helpers/textSqrlTest"; 8 | 9 | textSqrlTest( 10 | "works", 11 | ` 12 | LET Text := ["hello world"]; 13 | ASSERT patternMatches('BlacklistedKeywords.txt', Text) = []; # no hello 14 | 15 | LET Text := ["hello world badkeyword"]; 16 | ASSERT patternMatches('BlacklistedKeywords.txt', Text) = ["badkeyword"]; 17 | 18 | LET Text := ["hello world baaaaaaaad21"]; 19 | ASSERT patternMatches('./BlacklistedKeywords.txt', Text) = ["baaaaaaaad2"]; 20 | 21 | LET Foo := "nothing to see here"; 22 | ASSERT patternMatches('./BlacklistedKeywords.txt', Foo) = []; 23 | 24 | LET Foo := "it has badkeyword"; 25 | ASSERT patternMatches('./BlacklistedKeywords.txt', Foo) = ["badkeyword"]; 26 | 27 | LET Bar := ["This badkeyword thing is baaad20, or event bad21. It's a badkeywording thing"]; 28 | ASSERT patternMatches('./BlacklistedKeywords.txt', Bar) = [ 29 | "badkeyword", "baaad2", "bad2" 30 | ]; 31 | 32 | `, 33 | { 34 | filesystem: new VirtualFilesystem({ 35 | "BlacklistedKeywords.txt": "badkeyword\n" + "/ba+d2/\n", 36 | }), 37 | } 38 | ); 39 | -------------------------------------------------------------------------------- /website/source/stdlib/date-time.md: -------------------------------------------------------------------------------- 1 | title: Date-time Functions 2 | --- 3 | 4 | # Date-time Functions 5 | 6 | ## date 7 | 8 | **date**(value) 9 | 10 | Convert the given object or ISO8601 string to a date 11 | 12 | ## dateAdd 13 | 14 | **dateAdd**(date, duration) 15 | 16 | Add a given duration (ISO8601 format) to the given date 17 | 18 | ## dateDiff 19 | 20 | **dateDiff**(unit, start, end?) 21 | 22 | Returns the difference between the two dates in the given unit (millisecond, second, minute, hour, day, week) 23 | 24 | ## dateFromMs 25 | 26 | **dateFromMs**(value) 27 | 28 | Converts a count of milliseconds since the unix epoch to a date 29 | 30 | ## delayMs 31 | 32 | **delayMs**(ms, value) 33 | 34 | Returns the given value after delaying for the specified number of milliseconds 35 | 36 | ## formatDate 37 | 38 | **formatDate**(date, format) 39 | 40 | Format a given date according to a given format (see https://momentjs.com/docs/#/displaying/format/) 41 | 42 | ## now 43 | 44 | **now**(None) 45 | 46 | Returns the current time as an ISO 8601 string 47 | 48 | ## nowMs 49 | 50 | **nowMs**(None) 51 | 52 | Returns the current time as a count of milliseconds since the unix epoch 53 | 54 | ## timeMs 55 | 56 | **timeMs**(date) 57 | 58 | Returns the count of milliseconds since the unix epoch for the provided value 59 | 60 | -------------------------------------------------------------------------------- /website/source/deployment/async.md: -------------------------------------------------------------------------------- 1 | title: Sync and Async 2 | --- 3 | 4 | # Sync and Async 5 | 6 | We recommend most production deployments to have **two** rule sets. 7 | 8 | ### The sync ruleset 9 | 10 | The **sync** ruleset is used to affect live actions. It is evaluated while the user is still waiting for a response, and as such needs to be *fast*. Exactly how fast on what you want your response times to be. A social networks might want less than *100ms*, but in the case of payments even *5000ms* is acceptable for improved accuracy. Results of rules firing here might mean blocked signups, displaying captchas or phone verification. 11 | 12 | For this if your application is written in Node.js you could just `require('sqrl')` directly. For other applications SQRL comes with an out of the box [server deployment](server.html) that you can use as a micro-service. 13 | 14 | ### The async ruleset 15 | 16 | A slower **async** ruleset is used especially when the required response time of the **sync** ruleset is very low. These rules scan events *after* they have happened and do more complicated analysis. The actions that may be taken here are putting the content up for manual review, and adding labels that the **sync** ruleset may use to block future actions. 17 | 18 | Most async implementations use a [message queue deployment](queue.html) -------------------------------------------------------------------------------- /packages/sqrl/src/api/log.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { ConsoleLogger, AbstractLogger } from "../util/Logger"; 7 | 8 | export interface LogProperties { 9 | [key: string]: any; 10 | } 11 | 12 | export interface Logger { 13 | log(level: string, props: LogProperties, format: string, ...param: any[]); 14 | trace(props: LogProperties, format: string, ...param: any[]); 15 | debug(props: LogProperties, format: string, ...param: any[]); 16 | info(props: LogProperties, format: string, ...param: any[]); 17 | warn(props: LogProperties, format: string, ...param: any[]); 18 | error(props: LogProperties, format: string, ...param: any[]); 19 | fatal(props: LogProperties, format: string, ...param: any[]); 20 | } 21 | 22 | let currentLogger = new ConsoleLogger(); 23 | export class GlobalLogger extends AbstractLogger { 24 | log(level: string, props: LogProperties, format: string, ...param: any[]) { 25 | return currentLogger.log(level, props, format, ...param); 26 | } 27 | } 28 | 29 | const globalLogger = new GlobalLogger(); 30 | export function setGlobalLogger(newLogger: Logger) { 31 | currentLogger = newLogger; 32 | } 33 | export function getGlobalLogger() { 34 | return globalLogger; 35 | } 36 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/source/style/_doc/layout.scss: -------------------------------------------------------------------------------- 1 | body { 2 | font-size: 16px; 3 | overflow-y: auto; 4 | overflow-x: hidden; 5 | 6 | &.doc-navigation--is-collapsed { 7 | @media screen and (min-width: $doc-breakpoint) { 8 | .doc-navbar { 9 | width: $doc-navbar-width-collapsed; 10 | overflow: hidden; 11 | border-bottom: 1px solid $doc-navbar-background-color; 12 | } 13 | 14 | .doc-sidebar { 15 | transform: translateX(-($doc-sidebar-width - $doc-navbar-width-collapsed)); 16 | } 17 | 18 | .doc-content { 19 | transform: translateX($doc-sidebar-width-collapsed); 20 | margin-right: $doc-sidebar-width-collapsed; 21 | } 22 | 23 | .doc-navbar__logo__text, 24 | .doc-sidebar-content, 25 | .doc-navbar__sidebar-close { 26 | display: none; 27 | } 28 | 29 | .doc-sidebar__vertical-menu { 30 | display: block; 31 | } 32 | } 33 | } 34 | 35 | &.doc-sidebar--is-visible { 36 | @media screen and (max-width: $doc-breakpoint) { 37 | .doc-sidebar { 38 | transform: translateX(0); 39 | } 40 | 41 | .doc-content { 42 | transform: translateX($doc-sidebar-width); 43 | } 44 | } 45 | } 46 | } 47 | 48 | .doc-sidebar__vertical-menu { 49 | display: none; 50 | } 51 | -------------------------------------------------------------------------------- /website/source/packages/sqrl-cli-functions.md: -------------------------------------------------------------------------------- 1 | title: sqrl-cli-functions 2 | --- 3 | 4 | # sqrl-cli-functions 5 | 6 | These functions are intended for use with the sqrl cli. At a later stage they may be ported to a separate package. 7 | 8 | ## allSource 9 | 10 | **allSource**(None) 11 | 12 | Returns all of the source code for this execution 13 | 14 | ## blockAction 15 | 16 | **blockAction**(None) 17 | 18 | Mark the current action as blocked 19 | 20 | ## featureSource 21 | 22 | **featureSource**(feature) 23 | 24 | Returns the source code for the given feature 25 | 26 | ## log 27 | 28 | **log**(format string, value...) 29 | 30 | Logs a message using sprintf style formatting 31 | 32 | ## logFeature 33 | 34 | **logFeature**(feature) 35 | 36 | Logs the given feature and its value 37 | 38 | ## printAllSource 39 | 40 | **printAllSource**(None) 41 | 42 | Prints the SQRL execution source 43 | 44 | ## printSource 45 | 46 | **printSource**(feature) 47 | 48 | Prints the SQRL source of the given feature 49 | 50 | ## source 51 | 52 | **source**(feature) 53 | 54 | Returns the source code of the given feature 55 | 56 | ## wasBlocked 57 | 58 | **wasBlocked**(None) 59 | 60 | Check if the current action was marked as blocked 61 | 62 | ## whitelistAction 63 | 64 | **whitelistAction**(None) 65 | 66 | Mark the current action as whitelisted 67 | 68 | -------------------------------------------------------------------------------- /website/source/language/textpattern.md: -------------------------------------------------------------------------------- 1 | title: Text Patterns 2 | --- 3 | 4 | # Text Patterns 5 | 6 | As new spam campaigns emerge and existing ones change, it is important to be able to quickly update content rules. 7 | 8 | Here's an example of a rule that blocks any comments using a blacklisted keyword. 9 | 10 | ``` 11 | CREATE RULE UsedBlacklistedKeyword WHERE patternMatches("BlacklistedKeywords.txt", Text); 12 | ``` 13 | 14 | The patternMatches returns back an array of extracted matches or an empty array if none are found (empty arrays evaluate to false in SQRL). Often times you would want to use the extracted matches in the rule's reason. We can easily do that by updating the code to the following. 15 | 16 | ``` 17 | LET BlacklistedKeywordMatches := patternMatches("BlacklistedKeywords.txt", Text); 18 | CREATE RULE UsedBlacklistedKeyword WHERE BlacklistedKeywordMatches 19 | WITH REASON "Text used blacklisted keywords: ${BlacklistedKeywordMatches}"; 20 | ``` 21 | 22 | ### Maintaining blacklists 23 | 24 | This feature is also useful for maintaining blacklists, in this example of email addresses: 25 | 26 | ``` 27 | LET BlacklistedEmailMatches := patternMatches("BlacklistedEmail.txt", ActorEmail); 28 | CREATE RULE UsedBlacklistedEmail WHERE BlacklistedEmailMatches 29 | WITH REASON "Email contained blacklisted patterns: ${BlacklistedEmailMatches}"; 30 | ``` -------------------------------------------------------------------------------- /packages/sqrl-cli/src/cli/readJsonFile.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { readFile, readFileSync } from "fs"; 7 | import { promisify } from "util"; 8 | import { CliError } from "./CliError"; 9 | 10 | const readFileAsync = promisify(readFile); 11 | 12 | export async function readJsonFile(path: string) { 13 | let data: Buffer; 14 | try { 15 | data = await readFileAsync(path); 16 | } catch (err) { 17 | if (err.code === "ENOENT") { 18 | throw new CliError("Could not find file: " + path); 19 | } else { 20 | throw err; 21 | } 22 | } 23 | 24 | try { 25 | return JSON.parse(data.toString("utf-8")); 26 | } catch (err) { 27 | throw new CliError("File did not contain JSON-encoded data: " + path); 28 | } 29 | } 30 | 31 | export function readJsonFileSync(path: string) { 32 | let data: Buffer; 33 | try { 34 | data = readFileSync(path); 35 | } catch (err) { 36 | if (err.code === "ENOENT") { 37 | throw new CliError("Could not find file: " + path); 38 | } else { 39 | throw err; 40 | } 41 | } 42 | 43 | try { 44 | return JSON.parse(data.toString("utf-8")); 45 | } catch (err) { 46 | throw new CliError("File did not contain JSON-encoded data: " + path); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/sqrl-common/src/sqrlCartesianProduct.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { cartesianProductOf } from "./cartesianProductOf"; 7 | import { invariant } from "./invariant"; 8 | import { SqrlObject } from "./SqrlObject"; 9 | 10 | /** 11 | * This returns a safe cartesian product of a number of different SQRL values 12 | */ 13 | export function sqrlCartesianProduct( 14 | featureValues?: [T | T[]][], 15 | options: { 16 | maxArrays?: number; 17 | } = {} 18 | ): T[][] { 19 | const hasEmpty: boolean = featureValues.some((v) => { 20 | return !SqrlObject.isTruthy(v); 21 | }); 22 | 23 | if (hasEmpty || featureValues.length === 0) { 24 | return []; 25 | } 26 | 27 | let arrayCount: number = 0; 28 | const valueArrays: any[][] = featureValues.map((featureValue) => { 29 | if (Array.isArray(featureValue)) { 30 | arrayCount += 1; 31 | return featureValue.filter((i) => i); 32 | } else if (featureValue) { 33 | return [featureValue]; 34 | } 35 | return []; 36 | }); 37 | 38 | invariant( 39 | !options.maxArrays || arrayCount <= options.maxArrays, 40 | `Exceeded sqrl maximum array count:: ${arrayCount} of ${options.maxArrays}` 41 | ); 42 | 43 | return cartesianProductOf(valueArrays); 44 | } 45 | -------------------------------------------------------------------------------- /packages/sqrl-redis-functions/src/Services.ts: -------------------------------------------------------------------------------- 1 | import { Context, SqrlKey } from "sqrl"; 2 | import { LabelService } from "./LabelFunctions"; 3 | import { RateLimitService } from "./RateLimitFunctions"; 4 | import { UniqueIdService } from "./services/RedisUniqueId"; 5 | 6 | export interface CountService { 7 | fetch( 8 | ctx: Context, 9 | at: number, 10 | keys: SqrlKey[], 11 | windowMs: number | null 12 | ): Promise; 13 | bump( 14 | ctx: Context, 15 | at: number, 16 | keys: SqrlKey[], 17 | windowMs: number | null, 18 | by: number 19 | ): Promise; 20 | } 21 | 22 | export interface CountUniqueService { 23 | bump( 24 | ctx: Context, 25 | props: { 26 | at: number; 27 | key: SqrlKey; 28 | sortedHashes: string[]; 29 | windowMs: number; 30 | } 31 | ): void; 32 | fetchHashes( 33 | ctx: Context, 34 | props: { keys: SqrlKey[]; windowStartMs: number } 35 | ): Promise; 36 | fetchCounts( 37 | ctx: Context, 38 | props: { 39 | keys: SqrlKey[]; 40 | at: number; 41 | windowMs: number; 42 | addHashes: string[]; 43 | } 44 | ): Promise; 45 | } 46 | 47 | export interface Services { 48 | count: CountService; 49 | countUnique: CountUniqueService; 50 | label: LabelService; 51 | rateLimit: RateLimitService; 52 | uniqueId: UniqueIdService; 53 | } 54 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/plugins/react.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // NOTE: "node-jsx" package is deprecated but it's only one 4 | // that is working correctly without going crazy with presets and babel 5 | // the correct solution should be: 6 | // 7 | // ``` 8 | // require('babel-register')({ 9 | // 'presets': ['react', 'es2015'] 10 | // }); 11 | // ``` 12 | // 13 | // But for "yet" unknown reasons it works just when you `npm link` the package but not when 14 | // you install it in a project with the usual `npm install`... ¯\_(ツ)_/¯ 15 | // 16 | 17 | 18 | require('node-jsx').install(); 19 | 20 | const React = require('react'); 21 | const ReactDOM = require('react-dom/server'); 22 | const {Navigation} = require('../lib/browser/navigation/containers.jsx'); 23 | const components = { 24 | Navigation 25 | }; 26 | 27 | module.exports = ({hexo}) => { 28 | 29 | /** 30 | * "Server-side render" a React component 31 | * @param {String} componentName - The componentName 32 | * @param {Object} [props={}] - injected props 33 | * @return {string} 34 | */ 35 | function reactComponent (componentName, props = {}) { 36 | const Component = components[componentName]; 37 | const componentFactory = React.createFactory(Component); 38 | return ReactDOM.renderToString(componentFactory(props)); 39 | } 40 | 41 | hexo.extend.helper.register('react_component', reactComponent); 42 | }; 43 | -------------------------------------------------------------------------------- /packages/sqrl/src/function/TimeFunctions.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { StdlibRegistry } from "./Instance"; 7 | import { AstTypes as AT } from "../ast/AstTypes"; 8 | import bluebird = require("bluebird"); 9 | 10 | export function registerTimeFunctions(instance: StdlibRegistry) { 11 | instance.save( 12 | function now() { 13 | return new Date().toISOString(); 14 | }, 15 | { 16 | args: [], 17 | safe: true, 18 | argstring: "", 19 | docstring: "Returns the current time as an ISO 8601 string", 20 | } 21 | ); 22 | 23 | instance.save( 24 | function nowMs() { 25 | return Date.now(); 26 | }, 27 | { 28 | args: [], 29 | safe: true, 30 | argstring: "", 31 | docstring: 32 | "Returns the current time as a count of milliseconds since the unix epoch", 33 | } 34 | ); 35 | 36 | instance.save( 37 | function delayMs(state, ms, value) { 38 | return bluebird.delay(ms).thenReturn(value); 39 | }, 40 | { 41 | allowNull: true, 42 | args: [AT.state, AT.any, AT.any], 43 | async: true, 44 | argstring: "ms, value", 45 | docstring: 46 | "Returns the given value after delaying for the specified number of milliseconds", 47 | } 48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "taskName": "Run current file", 8 | "command": "ts-node ${relativeFile}", 9 | "type": "shell", 10 | "problemMatcher": [] 11 | }, 12 | { 13 | "type": "npm", 14 | "label": "clean", 15 | "script": "clean", 16 | "problemMatcher": [] 17 | }, 18 | { 19 | "type": "npm", 20 | "label": "build", 21 | "script": "build", 22 | "group": { 23 | "kind": "build", 24 | "isDefault": true 25 | }, 26 | "problemMatcher":[] 27 | }, 28 | { 29 | "type": "npm", 30 | "label": "format", 31 | "script": "format", 32 | "problemMatcher": [] 33 | }, 34 | { 35 | "type": "npm", 36 | "label": "coverage", 37 | "script": "coverage", 38 | "problemMatcher": [] 39 | }, 40 | { 41 | "type": "npm", 42 | "label": "test", 43 | "script": "test", 44 | "group": { 45 | "kind": "test", 46 | "isDefault": true 47 | }, 48 | "problemMatcher": [] 49 | }, 50 | { 51 | "type": "npm", 52 | "label": "lint", 53 | "script": "lint", 54 | "problemMatcher": [ 55 | "$eslint-stylish" 56 | ] 57 | } 58 | ] 59 | } 60 | -------------------------------------------------------------------------------- /packages/sqrl/__tests__/parse/__snapshots__/customSqrl.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`works with example arguments 1`] = `"Expected \\"!=\\", \\"%\\", \\"*\\", \\"+\\", \\"-\\", \\"/\\", \\"<\\", \\"<=\\", \\"=\\", \\">\\", \\">=\\", \\"CONTAINS\\", \\"IN\\", \\"IS NOT\\", \\"IS\\", end of input, or whitespace but \\")\\" found."`; 4 | 5 | exports[`works with example arguments 2`] = `"Expected \\"!=\\", \\"%\\", \\"*\\", \\"+\\", \\"-\\", \\"/\\", \\"<\\", \\"<=\\", \\"=\\", \\">\\", \\">=\\", \\"CONTAINS\\", \\"IN\\", \\"IS NOT\\", \\"IS\\", end of input, or whitespace but \\"(\\" found."`; 6 | 7 | exports[`works with example arguments 3`] = `"Expected \\"!=\\", \\"%\\", \\"*\\", \\"+\\", \\"-\\", \\"/\\", \\"<\\", \\"<=\\", \\"=\\", \\">\\", \\">=\\", \\"AND\\", \\"CONTAINS\\", \\"IN\\", \\"IS NOT\\", \\"IS\\", \\"OR\\", or end of input but \\")\\" found."`; 8 | 9 | exports[`works with example arguments 4`] = `"Expected \\"!=\\", \\"%\\", \\"*\\", \\"+\\", \\"-\\", \\"/\\", \\"<\\", \\"<=\\", \\"=\\", \\">\\", \\">=\\", \\"CONTAINS\\", \\"IN\\", \\"IS NOT\\", \\"IS\\", end of input, or whitespace but \\")\\" found."`; 10 | 11 | exports[`works with example arguments 5`] = `"Expected \\"!=\\", \\"%\\", \\"*\\", \\"+\\", \\"-\\", \\"/\\", \\"<\\", \\"<=\\", \\"=\\", \\">\\", \\">=\\", \\"CONTAINS\\", \\"IN\\", \\"IS NOT\\", \\"IS\\", end of input, or whitespace but \\"\`\\" found."`; 12 | -------------------------------------------------------------------------------- /packages/sqrl/src/api/spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | export interface RuleSpec { 7 | name: string; 8 | reason: string; 9 | features: string[]; 10 | } 11 | export interface RuleSpecMap { 12 | [ruleName: string]: RuleSpec; 13 | } 14 | 15 | export interface FeatureDefinitionLocation { 16 | line: number; 17 | offset: number; 18 | column: number; 19 | } 20 | 21 | export interface FeatureDefinition { 22 | start: FeatureDefinitionLocation; 23 | end: FeatureDefinitionLocation; 24 | filename: string; 25 | source: string; 26 | description?: string; 27 | includedWhere?: string; 28 | features: (string | null)[]; 29 | } 30 | 31 | export interface FeatureDoc { 32 | name: string; 33 | definitions: FeatureDefinition[]; 34 | cost: number; 35 | recursiveCost: number; 36 | } 37 | 38 | export interface FeatureDocMap { 39 | [name: string]: FeatureDoc; 40 | } 41 | 42 | /** 43 | * Specification to load a compiled SQRL file into the JavaScript runtime. 44 | * The optional fields are not required for the runtime, but are used elsewhere. 45 | */ 46 | export interface ExecutableSpec { 47 | rules: RuleSpecMap; 48 | features?: FeatureDocMap; 49 | usedFiles: string[]; 50 | 51 | slotNames: string[]; 52 | slotJs: string[]; 53 | slotCosts: number[]; 54 | slotRecursiveCosts: number[]; 55 | } 56 | -------------------------------------------------------------------------------- /packages/sqrl/src/simple/TestLogger.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { AbstractLogger } from "../util/Logger"; 7 | import { LogProperties } from "../api/log"; 8 | import * as util from "util"; 9 | 10 | export class TestLogger extends AbstractLogger { 11 | readonly messages: { 12 | msg: string; 13 | level: string; 14 | }[] = []; 15 | 16 | countErrors() { 17 | let errorCount = 0; 18 | for (const { level } of this.messages) { 19 | if (level === "trace" || level === "debug" || level === "info") { 20 | continue; 21 | } 22 | errorCount += 1; 23 | } 24 | return errorCount; 25 | } 26 | 27 | popLatest(props: { level: string }) { 28 | const { level } = props; 29 | for (let idx = this.messages.length - 1; idx >= 0; idx--) { 30 | if (this.messages[idx].level === level) { 31 | return this.messages.splice(idx, 1)[0]; 32 | } 33 | } 34 | throw new Error("No log line found"); 35 | } 36 | 37 | log(level: string, props: LogProperties, format: string, ...param: any[]) { 38 | const message = util.format(format, ...param); 39 | // tslint:disable-next-line 40 | this.messages.push({ 41 | msg: message, 42 | level, 43 | }); 44 | // tslint:disable-next-line:no-console 45 | console.error(message); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/sqrl/__tests__/sqrl/comments.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { statementsFromString } from "../../src/helpers/CompileHelpers"; 7 | 8 | test("comments work in any position", async () => { 9 | expect(statementsFromString("")).toEqual([]); 10 | expect(statementsFromString(`\n`)).toEqual([]); 11 | expect(statementsFromString(`#`)).toEqual([]); 12 | expect(statementsFromString(`#\n`)).toEqual([]); 13 | expect(statementsFromString(`\n#`)).toEqual([]); 14 | expect(statementsFromString(`\n#\n`)).toEqual([]); 15 | 16 | expect(statementsFromString(`--`)).toEqual([]); 17 | expect(statementsFromString(`--\n`)).toEqual([]); 18 | expect(statementsFromString(`\n--`)).toEqual([]); 19 | expect(statementsFromString(`\n--\n`)).toEqual([]); 20 | 21 | expect( 22 | statementsFromString(` 23 | let Okay := true; 24 | # let bla bla!! 25 | `) 26 | ).toHaveLength(1); 27 | 28 | expect( 29 | statementsFromString(` 30 | let AlsoOkay := false; 31 | # 32 | let Okay := true;`) 33 | ).toHaveLength(2); 34 | 35 | expect( 36 | statementsFromString(` 37 | let AlsoOkay := false; 38 | -- bam bam 39 | let Okay := true;`) 40 | ).toHaveLength(2); 41 | 42 | expect( 43 | statementsFromString(` 44 | let Okay := true; 45 | #`) 46 | ).toHaveLength(1); 47 | }); 48 | -------------------------------------------------------------------------------- /packages/sqrl/__tests__/sqrl/args.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { AstTypes as AT } from "../../src/ast/AstTypes"; 7 | import { runSqrlTest } from "../../src"; 8 | 9 | test("runtime checks", async () => { 10 | AT.any.string.runtimeChecker("hey hey"); 11 | expect(AT.any.string.runtimeChecker(67)).toEqual( 12 | "Expected type string but was number" 13 | ); 14 | 15 | AT.any.number.runtimeChecker(67); 16 | expect(AT.any.number.runtimeChecker("hey hey")).toEqual( 17 | "Expected type number but was string" 18 | ); 19 | }); 20 | 21 | test("tests inside sqrl", async () => { 22 | await expect(runSqrlTest("LET V := concat();")).rejects.toThrow( 23 | /Expected at least 2 arguments but got 0/ 24 | ); 25 | await expect(runSqrlTest("LET V := concat(1);")).rejects.toThrow( 26 | /Expected at least 2 arguments but got 1/ 27 | ); 28 | await expect(runSqrlTest("LET V := concat(1,2);")).resolves.toBeTruthy(); 29 | await expect(runSqrlTest("LET V := concat(1,2,3);")).resolves.toBeTruthy(); 30 | 31 | await expect(runSqrlTest("LET V := log10();")).rejects.toThrow( 32 | /Expected 1 argument but got 0./ 33 | ); 34 | await expect(runSqrlTest("LET V := log10(1);")).resolves.toBeTruthy(); 35 | await expect(runSqrlTest("LET V := log10(1,2);")).rejects.toThrow( 36 | /Expected 1 argument but got 2./ 37 | ); 38 | }); 39 | -------------------------------------------------------------------------------- /packages/sqrl/src/compile/SqrlCompile.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { Ast } from "../ast/Ast"; 7 | import { 8 | SqrlParserState, 9 | SqrlParserOptions, 10 | SqrlSerialized, 11 | } from "./SqrlParserState"; 12 | import { parseSqrlFiles, parseSqrl } from "../parser/SqrlParse"; 13 | import { labelerPushStatement } from "./compileLabelerStatements"; 14 | import { Context } from "../api/ctx"; 15 | 16 | const SQRL_MAIN = "main.sqrl"; 17 | 18 | export function createParserState( 19 | ctx: Context, 20 | props: { 21 | sourceFiles: { [filename: string]: string }; 22 | mainSqrl?: string; 23 | parserOptions: SqrlParserOptions; 24 | serialized?: SqrlSerialized; 25 | } 26 | ): SqrlParserState { 27 | const fileAst = parseSqrlFiles(props.sourceFiles); 28 | 29 | let mainAst: Ast = fileAst[SQRL_MAIN]; 30 | if (props.mainSqrl) { 31 | mainAst = parseSqrl(props.mainSqrl); 32 | } 33 | 34 | return new SqrlParserState( 35 | { 36 | statements: mainAst.statements, 37 | ...props.parserOptions, 38 | }, 39 | props.serialized || null 40 | ); 41 | } 42 | 43 | export function compileParserStateAst(parserState: SqrlParserState) { 44 | parserState.setPushStatement((ast) => labelerPushStatement(parserState, ast)); 45 | parserState.statements.forEach((stmt) => { 46 | parserState.pushStatement(stmt); 47 | }); 48 | } 49 | -------------------------------------------------------------------------------- /examples/sqrl-example-functions/README.md: -------------------------------------------------------------------------------- 1 | # [SQRL](https://twitter.github.io/sqrl/) · [![GitHub license](https://img.shields.io/badge/license-Apache%202-blue.svg)](https://github.com/twitter/sqrl/blob/master/LICENSE) [![npm version](https://img.shields.io/npm/v/sqrl.svg?style=flat)](https://www.npmjs.com/package/sqrl) [![Build Status](https://travis-ci.org/twitter/sqrl.svg?branch=master)](https://travis-ci.org/twitter/sqrl.svg?branch=master) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/twitter/sqrl/blob/master/CONTRIBUTING.md) 2 | 3 | # `sqrl-example-functions` package 4 | 5 | This package implements a simple function as an example of how to start your own SQRL functions package. 6 | 7 | ## sayHello(name) 8 | 9 | This returns a string that says hello to the given name. 10 | 11 | # SQRL documentation 12 | 13 | Please see [the website](https://twitter.github.io/sqrl) for full **SQRL** documentation 14 | 15 | ## Support 16 | 17 | Create a [new issue](https://github.com/twitter/sqrl/issues/new) on GitHub. 18 | 19 | ## Contributing 20 | 21 | We feel that a welcoming community is important and we ask that you follow Twitter's 22 | [Open Source Code of Conduct](https://github.com/twitter/code-of-conduct/blob/master/code-of-conduct.md) 23 | in all interactions with the community. 24 | 25 | ## License 26 | 27 | Copyright 2019 Twitter, Inc. 28 | 29 | Licensed under the Apache License, Version 2.0: https://www.apache.org/licenses/LICENSE-2.0 30 | -------------------------------------------------------------------------------- /packages/sqrl/src/object/SqrlUniqueId.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { SqrlObject } from "./SqrlObject"; 7 | import { mkSpan } from "./span"; 8 | import { UniqueId } from "../api/entity"; 9 | 10 | export default class SqrlUniqueId extends SqrlObject { 11 | constructor(private uniqueId: UniqueId) { 12 | super(); 13 | } 14 | 15 | getUniqueId(): UniqueId { 16 | return this.uniqueId; 17 | } 18 | getBuffer() { 19 | return this.uniqueId.getBuffer(); 20 | } 21 | tryGetTimeMs() { 22 | return this.uniqueId.getTimeMs(); 23 | } 24 | getRemainder() { 25 | return this.uniqueId.getRemainder(); 26 | } 27 | getNumberString() { 28 | return this.uniqueId.getNumberString(); 29 | } 30 | 31 | getData() { 32 | return { 33 | time: new Date(this.uniqueId.getTimeMs()).toISOString(), 34 | remaining: this.uniqueId.getRemainder(), 35 | }; 36 | } 37 | 38 | render() { 39 | return mkSpan("type:uniqueId", [ 40 | mkSpan("type:name", "uniqueId"), 41 | mkSpan("type:syntax", "<"), 42 | mkSpan("value:time", new Date(this.uniqueId.getTimeMs()).toISOString()), 43 | mkSpan("value:separator", "@"), 44 | mkSpan("value:remainder", this.uniqueId.getRemainder().toString()), 45 | mkSpan("type:syntax", ">"), 46 | ]); 47 | } 48 | 49 | getBasicValue() { 50 | return this.uniqueId.getHexString(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /website/scripts/emoji.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Twitter, Inc. 2 | // Licensed under the Apache License, Version 2.0 3 | // http://www.apache.org/licenses/LICENSE-2.0 4 | 5 | const cheerio = require("cheerio"); 6 | const emojis = require("../github_emojis"); 7 | const util = require("hexo-util"); 8 | 9 | function replaceEmojis(content) { 10 | return content.replace(/:([a-z_]+):/g, (match, emoji) => { 11 | if (!emojis[emoji]) { 12 | return match; 13 | } 14 | return ``; 17 | }); 18 | } 19 | 20 | hexo.extend.filter.register("after_post_render", data => { 21 | const $ = cheerio.load(data.content); 22 | 23 | function recurse(node) { 24 | for (const child of node.children || []) { 25 | if (child.type === "text") { 26 | const content = replaceEmojis(child.data); 27 | 28 | // If we replace a text child, make sure that we scan this node again for more emojis 29 | if (content !== child.data) { 30 | $(child).replaceWith($.parseHTML(content)); 31 | return recurse(node); 32 | } 33 | } else if ( 34 | child.type === "tag" && 35 | child.name !== "pre" && 36 | child.name !== "code" 37 | ) { 38 | recurse(child); 39 | } 40 | } 41 | } 42 | 43 | Array.from($.root().children()).forEach(child => recurse(child)); 44 | 45 | data.content = $.root().html(); 46 | 47 | return data; 48 | }); 49 | -------------------------------------------------------------------------------- /website/source/functions/simple.md: -------------------------------------------------------------------------------- 1 | title: Simple Functions 2 | --- 3 | 4 | # Defining simple functions 5 | 6 | ### Creating a package that uses SQRL 7 | 8 | We recommend developing SQRL functions using [TypeScript](https://www.typescriptlang.org). Getting set up with TypeScript is out of scope for this tutorial, but as a prerequisite you should be comfortable running TypeScript code on the command line. 9 | 10 | First install the `sqrl` package. We'll also install the `sqrl-cli` package which makes it easy to create a command line application to test with. 11 | 12 | ``` 13 | npm install --save sqrl sqrl-cli 14 | ``` 15 | 16 | Once the packages are installed you can create a basic registration function 17 | 18 | ``` 19 | import {Instance, Execution, AT } from "sqrl" 20 | 21 | function registerFunctions(instance: Instance) => { 22 | instance.register(async function sayHello(state: Execution, name) { 23 | return 'Hello, ' + name + '!'; 24 | }, { 25 | args: [AT.state, AT.any] 26 | }) 27 | } 28 | ``` 29 | 30 | Finally `sqrl-cli` exposes a run method that makes testing this out far easier: 31 | ``` 32 | import { run } from "sqrl-cli"; 33 | run({ registerFunctions }); 34 | ``` 35 | 36 | Now if you run your TypeScript file, you should get the standard SQRL command line interface with your function included. We use [ts-node](https://github.com/TypeStrong/ts-node) so that you don't have to recompile each time. 37 | 38 | ``` 39 | $ ts-node src/cli.ts repl 40 | sqrl> sayHello("Josh") 41 | 'Hello, Josh!' 42 | ``` -------------------------------------------------------------------------------- /packages/sqrl/__tests__/ast/AstTypes.spec.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { AstTypes as AT } from "../../src/ast/AstTypes"; 7 | import SqrlAst from "../../src/ast/SqrlAst"; 8 | 9 | const func = "myCoolFunction"; 10 | 11 | test("works", async () => { 12 | const types = [AT.constant.string, AT.feature, AT.any, AT.any.optional]; 13 | 14 | AT.compileTypesInvariant( 15 | SqrlAst.call(func, [ 16 | SqrlAst.constant("hello"), 17 | SqrlAst.feature("Joe"), 18 | SqrlAst.call("anything", []), 19 | SqrlAst.constant("optional"), 20 | ]), 21 | types 22 | ); 23 | 24 | AT.compileTypesInvariant( 25 | SqrlAst.call(func, [ 26 | SqrlAst.constant("hello"), 27 | SqrlAst.feature("Joe"), 28 | SqrlAst.call("anything", []), 29 | ]), 30 | types 31 | ); 32 | 33 | expect(() => 34 | AT.compileTypesInvariant( 35 | SqrlAst.call(func, [SqrlAst.constant("hello"), SqrlAst.feature("Joe")]), 36 | types 37 | ) 38 | ).toThrow( 39 | "Argument count to call of myCoolFunction did not match. Expected 3 to 4 arguments but got 2." 40 | ); 41 | 42 | expect(() => 43 | AT.compileTypesInvariant( 44 | SqrlAst.call(func, [ 45 | SqrlAst.include("x"), 46 | SqrlAst.include("y"), 47 | SqrlAst.include("z"), 48 | ]), 49 | types 50 | ) 51 | ).toThrowError(/Expected argument to be a constant string/); 52 | }); 53 | -------------------------------------------------------------------------------- /packages/sqrl-common/README.md: -------------------------------------------------------------------------------- 1 | # [SQRL](https://twitter.github.io/sqrl/) · [![GitHub license](https://img.shields.io/badge/license-Apache%202-blue.svg)](https://github.com/twitter/sqrl/blob/master/LICENSE) [![npm version](https://img.shields.io/npm/v/sqrl.svg?style=flat)](https://www.npmjs.com/package/sqrl) [![Build Status](https://travis-ci.org/twitter/sqrl.svg?branch=master)](https://travis-ci.org/twitter/sqrl.svg?branch=master) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/twitter/sqrl/blob/master/CONTRIBUTING.md) 2 | 3 | # `sqrl-common` package 4 | 5 | This package includes some shared code for all SQRL packages. You should not need to install this package yourself. 6 | 7 | # SQRL documentation 8 | 9 | Please see [the website](https://twitter.github.io/sqrl) for full **SQRL** documentation 10 | 11 | ## Support 12 | 13 | Create a [new issue](https://github.com/twitter/sqrl/issues/new) on GitHub. 14 | 15 | ## Contributing 16 | 17 | We feel that a welcoming community is important and we ask that you follow Twitter's 18 | [Open Source Code of Conduct](https://github.com/twitter/code-of-conduct/blob/master/code-of-conduct.md) 19 | in all interactions with the community. 20 | 21 | ## License 22 | 23 | Copyright 2018 Twitter, Inc. 24 | 25 | Licensed under the Apache License, Version 2.0: https://www.apache.org/licenses/LICENSE-2.0 26 | 27 | ## Security Issues 28 | 29 | Please report sensitive security issues via Twitter's bug-bounty program (https://hackerone.com/twitter) rather than GitHub. 30 | -------------------------------------------------------------------------------- /packages/sqrl-cli/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | // tslint:disable:no-console 7 | 8 | import { cliMain, getCliOutput } from "./cli/CliMain"; 9 | import { promiseFinally } from "sqrl-common"; 10 | import { CloseableGroup } from "./jslib/Closeable"; 11 | import { Instance } from "sqrl"; 12 | import { CliOutput } from "./cli/CliOutput"; 13 | import { CliError } from "./cli/CliError"; 14 | import { parseArgs, CliArgs } from "./cli/CliArgs"; 15 | 16 | export { CliArgs }; 17 | export { parseArgs }; 18 | export { cliMain }; 19 | 20 | export function run( 21 | options: { 22 | register?: (instance: Instance) => Promise; 23 | } = {} 24 | ) { 25 | const closeables = new CloseableGroup(); 26 | let exitCode = 1; 27 | let output: CliOutput; 28 | 29 | const args = parseArgs(); 30 | 31 | // Ensure errors inside getCliOutput() get handled neatly. 32 | try { 33 | output = getCliOutput(args); 34 | } catch (err) { 35 | if (err instanceof CliError) { 36 | console.error("Error: " + err.message); 37 | process.exit(1); 38 | } else { 39 | throw err; 40 | } 41 | } 42 | 43 | promiseFinally( 44 | cliMain(args, closeables, { register: options.register, output }) 45 | .then(() => { 46 | exitCode = 0; 47 | }) 48 | .catch((err) => { 49 | output.error(err); 50 | }), 51 | () => { 52 | closeables.close(); 53 | process.exit(exitCode); 54 | } 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /website/themes/sqrl-hexo-theme-doc/layout/layout.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%- config.title %> | <%- page.title %> 7 | 8 | 9 | 10 | <%- project_partial('head_start') %> 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | <%- project_partial('head_end') %> 23 | 24 | 25 | 26 | 27 | 28 | 29 | <%- partial('_partial/navigation') -%> 30 | <%- body %> 31 | <%- partial('_partial/google_analytics') %> 32 | 33 | <%- project_partial('footer_start') %> 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | <%- project_partial('footer_end') %> 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /packages/sqrl-redis-functions/src/parser/sqrlRedis.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Twitter, Inc. 3 | * Licensed under the Apache License, Version 2.0 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | import { FeatureAst, Ast } from "sqrl"; 7 | 8 | interface TimespanCustom { 9 | type: 10 | | "dayOverDay" 11 | | "dayOverWeek" 12 | | "dayOverFullWeek" 13 | | "weekOverWeek" 14 | | "previousLastDay" 15 | | "previousLastWeek" 16 | | "dayWeekAgo" 17 | | "total"; 18 | } 19 | interface TimespanMs { 20 | type: "duration"; 21 | durationMs: number; 22 | } 23 | export type Timespan = TimespanCustom | TimespanMs; 24 | 25 | export interface AliasedFeature { 26 | feature: FeatureAst; 27 | alias: string; 28 | } 29 | 30 | export interface CountArguments { 31 | features: AliasedFeature[]; 32 | sumFeature: FeatureAst | null; 33 | timespan: Timespan; 34 | where: Ast; 35 | } 36 | 37 | export interface TrendingArguments { 38 | features: AliasedFeature[]; 39 | minEvents: number; 40 | timespan: Timespan; 41 | where: Ast; 42 | } 43 | 44 | export interface CountUniqueArguments { 45 | uniques: AliasedFeature[]; 46 | groups: AliasedFeature[]; 47 | setOperation: { 48 | operation: string; 49 | features: AliasedFeature[]; 50 | }; 51 | where: Ast; 52 | windowMs: number | null; 53 | beforeAction: boolean; 54 | } 55 | 56 | export interface RateLimitArguments { 57 | features: FeatureAst[]; 58 | maxAmount: number; 59 | refillTimeMs: number; 60 | refillAmount: number; 61 | tokenAmount: Ast; 62 | strict: boolean; 63 | where: Ast; 64 | } 65 | -------------------------------------------------------------------------------- /packages/sqrl-test-utils/README.md: -------------------------------------------------------------------------------- 1 | # [SQRL](https://twitter.github.io/sqrl/) · [![GitHub license](https://img.shields.io/badge/license-Apache%202-blue.svg)](https://github.com/twitter/sqrl/blob/master/LICENSE) [![npm version](https://img.shields.io/npm/v/sqrl.svg?style=flat)](https://www.npmjs.com/package/sqrl) [![Build Status](https://travis-ci.org/twitter/sqrl.svg?branch=master)](https://travis-ci.org/twitter/sqrl.svg?branch=master) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/twitter/sqrl/blob/master/CONTRIBUTING.md) 2 | 3 | # `sqrl-test-utils` package 4 | 5 | This package includes some shared code for testing SQRL, but is not required for production deployments. 6 | 7 | You should not need to install this package yourself. 8 | 9 | # SQRL documentation 10 | 11 | Please see [the website](https://twitter.github.io/sqrl) for full **SQRL** documentation 12 | 13 | ## Support 14 | 15 | Create a [new issue](https://github.com/twitter/sqrl/issues/new) on GitHub. 16 | 17 | ## Contributing 18 | 19 | We feel that a welcoming community is important and we ask that you follow Twitter's 20 | [Open Source Code of Conduct](https://github.com/twitter/code-of-conduct/blob/master/code-of-conduct.md) 21 | in all interactions with the community. 22 | 23 | ## License 24 | 25 | Copyright 2018 Twitter, Inc. 26 | 27 | Licensed under the Apache License, Version 2.0: https://www.apache.org/licenses/LICENSE-2.0 28 | 29 | ## Security Issues 30 | 31 | Please report sensitive security issues via Twitter's bug-bounty program (https://hackerone.com/twitter) rather than GitHub. 32 | -------------------------------------------------------------------------------- /website/source/examples/redis.md: -------------------------------------------------------------------------------- 1 | title: Redis example 2 | --- 3 | 4 | # Redis example 5 | 6 | ### Connecting to a Redis database 7 | 8 | By default SQRL will run in an in-memory only mode, which means state is not persisted between executions. For convenience a Redis implementation of most counters is included out of the box. 9 | 10 | For this to work you should be running a local redis server, if you are not the easiest way to start one up is with the [Docker](https://www.docker.com/) command `docker run -d -p 6379:6379 redis` 11 | 12 | ``` 13 | $ export SQRL_REDIS=127.0.0.1:6379 14 | 15 | $ cat > ratelimit.sqrl 16 | LET Ip := input(); 17 | LET Remaining := rateLimit(BY Ip MAX 2 EVERY 30 SECONDS); 18 | 19 | CREATE RULE BlockedByRateLimit WHERE Remaining = 0; 20 | WHEN BlockedByRateLimit THEN blockAction(); 21 | 22 | # Add an environment variable, could also use the `--redis=<>` option 23 | $ export SQRL_REDIS=localhost:6379 24 | 25 | $ sqrl run ratelimit.sqrl -s 'RequestIp="1.2.3.5"' BlockedByRateLimit Remaining 26 | ✓ 2019-01-14 15:46 action was allowed. 27 | BlockedByRateLimit=false 28 | Remaining=2 29 | 30 | $ sqrl run ratelimit.sqrl -s 'RequestIp="1.2.3.5"' BlockedByRateLimit Remaining 31 | ✓ 2019-01-14 15:46 action was allowed. 32 | BlockedByRateLimit=false 33 | Remaining=1 34 | 35 | $ sqrl run ratelimit.sqrl -s 'RequestIp="1.2.3.5"' BlockedByRateLimit Remaining 36 | ✗ 2019-01-14 15:46 action was blocked. 37 | ↳ [BlockedByRateLimit] 38 | BlockedByRateLimit=true 39 | Remaining=0 40 | ``` 41 | 42 | ### Try out a REPL 43 | 44 | For some information on our REPL, check out the [next example](repl.html) 45 | --------------------------------------------------------------------------------