├── .devcontainer ├── devcontainer.json └── docker-compose.yml ├── .gitignore ├── .gitmodules ├── Dockerfile ├── Makefile ├── README.md ├── docker-compose-twilio.yml ├── docker-compose-volumes.yml ├── docker-compose.yml ├── get_schema ├── package-lock.json ├── package.json ├── src │ ├── get_schema.ts │ ├── index.ts │ └── schema.ts └── tsconfig.json ├── magic_demo ├── Makefile ├── package-lock.json ├── package.json ├── src │ ├── orm-demo.ts │ ├── orm.ts │ ├── parse.ts │ ├── phone-a-friend-demo.ts │ ├── phone-a-friend.ts │ ├── z3-demo.ts │ └── z3.ts └── tsconfig.json ├── paper ├── oracle-types.bib ├── oracle-types.ltx ├── oracle-types.pdf ├── phone-a-friend.eps ├── phone-a-friend.svg └── txt-message.png ├── scripts ├── .emacs ├── demo.sh ├── init.sh ├── populate.sh └── setup-emacs.el ├── twilio_client ├── package-lock.json ├── package.json ├── src │ └── index.ts └── tsconfig.json └── twilio_server ├── package.json └── server.js /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // If you want to run as a non-root user in the container, see .devcontainer/docker-compose.yml. 2 | { 3 | "name": "Existing Docker Compose (Extend)", 4 | 5 | // Update the 'dockerComposeFile' list if you have more compose files or use different names. 6 | // The .devcontainer/docker-compose.yml file contains any overrides you need/want to make. 7 | "dockerComposeFile": [ 8 | "../docker-compose.yml", 9 | "docker-compose.yml" 10 | ], 11 | 12 | // The 'service' property is the name of the service for the container that VS Code should 13 | // use. Update this value and .devcontainer/docker-compose.yml to the real service name. 14 | "service": "app", 15 | 16 | // The optional 'workspaceFolder' property is the path VS Code should open by default when 17 | // connected. This is typically a file mount in .devcontainer/docker-compose.yml 18 | "workspaceFolder": "/workspace", 19 | 20 | // Set *default* container specific settings.json values on container create. 21 | "settings": { 22 | "terminal.integrated.shell.linux": null 23 | }, 24 | 25 | // Add the IDs of extensions you want installed when the container is created. 26 | "extensions": [] 27 | 28 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 29 | // "forwardPorts": [], 30 | 31 | // Uncomment the next line if you want start specific services in your Docker Compose config. 32 | // "runServices": [], 33 | 34 | // Uncomment the next line if you want to keep your containers running after VS Code shuts down. 35 | // "shutdownAction": "none", 36 | 37 | // Uncomment the next line to run commands after the container is created - for example installing curl. 38 | // "postCreateCommand": "apt-get update && apt-get install -y curl", 39 | 40 | // Uncomment to connect as a non-root user if you've added one. See https://aka.ms/vscode-remote/containers/non-root. 41 | // "remoteUser": "vscode" 42 | } 43 | -------------------------------------------------------------------------------- /.devcontainer/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | # Update this to the name of the service you want to work with in your docker-compose.yml file 4 | app: 5 | # If you want add a non-root user to your Dockerfile, you can use the "remoteUser" 6 | # property in devcontainer.json to cause VS Code its sub-processes (terminals, tasks, 7 | # debugging) to execute as the user. Uncomment the next line if you want the entire 8 | # container to run as this user instead. Note that, on Linux, you may need to 9 | # ensure the UID and GID of the container user you create matches your local user. 10 | # See https://aka.ms/vscode-remote/containers/non-root for details. 11 | # 12 | # user: vscode 13 | 14 | # Uncomment if you want to override the service's Dockerfile to one in the .devcontainer 15 | # folder. Note that the path of the Dockerfile and context is relative to the *primary* 16 | # docker-compose.yml file (the first in the devcontainer.json "dockerComposeFile" 17 | # array). The sample below assumes your primary file is in the root of your project. 18 | # 19 | # build: 20 | # context: . 21 | # dockerfile: .devcontainer/Dockerfile 22 | 23 | volumes: 24 | # Update this to wherever you want VS Code to mount the folder of your project 25 | - .:/workspace:cached 26 | 27 | # Uncomment the next line to use Docker from inside the container. See https://aka.ms/vscode-remote/samples/docker-from-docker-compose for details. 28 | # - /var/run/docker.sock:/var/run/docker.sock 29 | 30 | # Uncomment the next four lines if you will use a ptrace-based debugger like C++, Go, and Rust. 31 | # cap_add: 32 | # - SYS_PTRACE 33 | # security_opt: 34 | # - seccomp:unconfined 35 | 36 | # Overrides default command so things don't shut down after the process ends. 37 | command: /bin/sh -c "while sleep 1000; do :; done" 38 | 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | out 3 | *~ 4 | *.aux 5 | *.log 6 | *.bbl 7 | *.blg 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "TypeScript"] 2 | path = TypeScript 3 | url = https://github.com/bovik-labs/TypeScript 4 | branch = add-intrinsic 5 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:stretch-slim 2 | RUN apt-get update 3 | RUN apt-get install -y emacs25 4 | RUN apt-get install -y git 5 | RUN apt-get install -y curl 6 | RUN mkdir -p /usr/share/man/man1/ 7 | RUN mkdir -p /usr/share/man/man7/ 8 | RUN apt-get install -y postgresql-client-9.6 9 | RUN apt-get install -y make 10 | RUN apt-get install -y z3 11 | 12 | SHELL ["/bin/bash", "--login", "-c"] 13 | RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash 14 | RUN nvm install 15.4.0 15 | 16 | RUN mkdir /tapeworm 17 | COPY TypeScript/built/local /tapeworm/TypeScript 18 | COPY get_schema /tapeworm/get_schema 19 | COPY twilio_client /tapeworm/twilio_client 20 | COPY scripts /tapeworm/scripts 21 | COPY magic_demo /tapeworm/magic_demo 22 | RUN cd /tapeworm/magic_demo && npm install -y 23 | COPY scripts/.emacs /root/.emacs 24 | RUN /tapeworm/scripts/init.sh 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | DOCKER-COMPOSE := docker-compose -f docker-compose.yml 2 | 3 | ifeq ($(VOLUMES), 1) 4 | DOCKER-COMPOSE := ${DOCKER-COMPOSE} -f docker-compose-volumes.yml 5 | endif 6 | 7 | ifneq ($(TWILIO_JSON),) 8 | DOCKER-COMPOSE := ${DOCKER-COMPOSE} -f docker-compose-twilio.yml 9 | endif 10 | 11 | # These targets meant to be used from host: 12 | TypeScript/built/local/tsc.js: 13 | git submodule init 14 | git submodule update --depth 10 15 | cd TypeScript && npm install -y 16 | cd TypeScript && npx gulp local 17 | 18 | get_schema/node_modules: 19 | cd get_schema && npm install -y 20 | 21 | get_schema/out/get_schema.js: get_schema/src/get_schema.ts get_schema/node_modules 22 | cd get_schema && npx tsc 23 | 24 | get_schema/out/index.js: get_schema/src/index.ts get_schema/node_modules 25 | cd get_schema && npx tsc 26 | 27 | twilio_client/node_modules: 28 | cd twilio_client && npm install -y 29 | 30 | twilio_client/out/index.js: twilio_client/src/index.ts twilio_client/node_modules 31 | cd twilio_client && npx tsc 32 | 33 | docker-build-up: TypeScript/built/local/tsc.js get_schema/out/index.js get_schema/out/get_schema.js twilio_client/out/index.js 34 | $(DOCKER-COMPOSE) up --build -d app 35 | 36 | docker-up: TypeScript/built/local/tsc.js get_schema/out/index.js get_schema/out/get_schema.js twilio_client/out/index.js 37 | $(DOCKER-COMPOSE) up -d app 38 | 39 | docker-down: 40 | $(DOCKER-COMPOSE) down 41 | 42 | docker-ssh: 43 | $(DOCKER-COMPOSE) run app bash 44 | 45 | demo-emacs: 46 | $(DOCKER-COMPOSE) run --name tapeworm_demo app /tapeworm/scripts/demo.sh 47 | docker rm tapeworm_demo 48 | 49 | demo-ssh: 50 | docker exec -it tapeworm_demo bash 51 | 52 | talk/node_modules: 53 | cd talk && npm install -y 54 | 55 | run-talk: talk/node_modules 56 | cd talk && npx reveal-md slides.md -w --theme sky 57 | 58 | clean: 59 | docker system prune 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Oracle Types 2 | ============ 3 | 4 | Oracle types are a proposed feature to the typescript compiler that 5 | allows programmer-defined extensions to the type system. 6 | 7 | Read more about them in [our paper](paper/oracle-types.pdf) to appear 8 | in [SIGBOVIK'21](http://sigbovik.org/2021/), or watch 9 | the [presentation](https://www.youtube.com/watch?v=ADPpyFnD-ac). 10 | 11 | To set up the demo environment (requires `docker` and 12 | `docker-compose`) do 13 | 14 | ``` 15 | $ make docker-build-up # this may take a minute 16 | $ make demo-emacs # starts emacs in docker container 17 | ``` 18 | 19 | This will open up a copy of emacs inside the docker container, viewing 20 | a typescript file. At present, it should have no type errors. If it 21 | does, it may be just because the database hasn't come up yet --- try 22 | waiting a couple seconds, make a trivial change, and resaving. 23 | 24 | To run the `orm-demo.ts` script, cd in the container to `/tapeworm/magic_demo` and run 25 | ``` 26 | make run 27 | ``` 28 | 29 | TODO: make the emacs initialization script wait for the database to be 30 | fully up. 31 | 32 | To edit the database schema live, we can do 33 | ``` 34 | $ make demo-ssh # ssh into same container as running emacs 35 | # psql # connect to database 36 | > ALTER TABLE users ADD COLUMN frequent_flyer_miles integer; -- create new column 37 | ``` 38 | 39 | Now the ORM should deliver another accessor `frequent_flyer_miles` in 40 | tab-completion in the `Users` model object. 41 | 42 | When developing sans emacs: 43 | 44 | ``` 45 | make docker-build-up 46 | make docker-ssh 47 | node ../TypeScript/tsc.js 48 | ``` 49 | 50 | For all docker commands in the makefile, set the environment variable 51 | `VOLUMES=1` if you want to include the volume declarations in 52 | `docker-compose-volumes.yml`. 53 | 54 | Twilio Demo 55 | ----------- 56 | 57 | To get the SMS parts of the "mobile-first interactive typechecking" 58 | demo working, replace the invocation of `docker-build-up` above with 59 | 60 | export TWILIO_JSON=/path/to/some/file/like/twilio.json 61 | make docker-build-up 62 | 63 | `twilio.json` should look like 64 | 65 | ``` 66 | { 67 | "TWILIO_ACCOUNT_ID":"...", 68 | "TWILIO_PHONE_NUMBER_SRC":"+15554443333", 69 | "TWILIO_PHONE_NUMBER_DST":"+15554443333", 70 | "TWILIO_RELAY_SERVER":"host:port", 71 | "TWILIO_AUTH_TOKEN":"..." 72 | } 73 | ``` 74 | 75 | Project Structure 76 | ----------------- 77 | 78 | - `Makefile`: some convenience commands for `docker-compose` lifecycle management 79 | 80 | - `get_schema/`: A nodejs script that a type-level shell can call to 81 | get schema information out of. Presently, it takes two arguments, 82 | `database` (the host for pg to connect to) and the `table` name, 83 | and returns a little bit of JSON that describes the table's 84 | columns. 85 | 86 | Also includes a file `schema.ts` that `magic_demo/orm.ts` uses as a 87 | library. 88 | 89 | The important thing distinction I'm trying to maintain between this 90 | directory and `magic_demo/` is that we should be able to 91 | conveniently compile everything in `get_schema/` on the host, 92 | without needing a special copy of `tsc`. 93 | 94 | - `magic_demo/`: Some demos meant to be run inside docker to show 95 | off the applications of oracle types. 96 | 97 | - `scripts/`: some configuration scripts used by the docker environment 98 | 99 | - `TypeScript/`: the typescript compiler checked out at a branch that includes 100 | the "advanced type-level computation feature". 101 | -------------------------------------------------------------------------------- /docker-compose-twilio.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | app: 4 | volumes: 5 | - ${TWILIO_JSON}:/tapeworm/twilio.json 6 | -------------------------------------------------------------------------------- /docker-compose-volumes.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | app: 4 | volumes: 5 | - ./magic_demo:/tapeworm/magic_demo 6 | - ./get_schema:/tapeworm/get_schema 7 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | app: 4 | build: . 5 | depends_on: 6 | - database 7 | command: /tapeworm/scripts/populate.sh 8 | environment: 9 | DB_HOST: database 10 | POSTGRES_USER: postgres 11 | POSTGRES_DB: postgres 12 | database: 13 | image: postgres:9.6 14 | environment: 15 | POSTGRES_USER: postgres 16 | POSTGRES_DB: postgres 17 | POSTGRES_PASSWORD: glorpglop 18 | 19 | -------------------------------------------------------------------------------- /get_schema/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "get_schema", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "version": "1.0.0", 9 | "license": "ISC", 10 | "dependencies": { 11 | "pg": "^8.5.1" 12 | }, 13 | "devDependencies": { 14 | "@types/node": "^14.14.13", 15 | "@types/pg": "^7.14.7", 16 | "typescript": "^4.1.3" 17 | } 18 | }, 19 | "node_modules/@types/node": { 20 | "version": "14.14.16", 21 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.16.tgz", 22 | "integrity": "sha512-naXYePhweTi+BMv11TgioE2/FXU4fSl29HAH1ffxVciNsH3rYXjNP2yM8wqmSm7jS20gM8TIklKiTen+1iVncw==", 23 | "dev": true 24 | }, 25 | "node_modules/@types/pg": { 26 | "version": "7.14.7", 27 | "resolved": "https://registry.npmjs.org/@types/pg/-/pg-7.14.7.tgz", 28 | "integrity": "sha512-ZnMOUidTP6Lwsb0bxHL6PVIL1lVC2CYNQWlA79kQ6nn0rK1/ynvkyN1wsR9pVZaP4WcCNioKT/2aU5UuLIQy2w==", 29 | "dev": true, 30 | "dependencies": { 31 | "@types/node": "*", 32 | "@types/pg-types": "*" 33 | } 34 | }, 35 | "node_modules/@types/pg-types": { 36 | "version": "1.11.5", 37 | "resolved": "https://registry.npmjs.org/@types/pg-types/-/pg-types-1.11.5.tgz", 38 | "integrity": "sha512-L8ogeT6vDzT1vxlW3KITTCt+BVXXVkLXfZ/XNm6UqbcJgxf+KPO7yjWx7dQQE8RW07KopL10x2gNMs41+IkMGQ==", 39 | "dev": true 40 | }, 41 | "node_modules/buffer-writer": { 42 | "version": "2.0.0", 43 | "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", 44 | "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", 45 | "engines": { 46 | "node": ">=4" 47 | } 48 | }, 49 | "node_modules/inherits": { 50 | "version": "2.0.4", 51 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 52 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 53 | }, 54 | "node_modules/packet-reader": { 55 | "version": "1.0.0", 56 | "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", 57 | "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" 58 | }, 59 | "node_modules/pg": { 60 | "version": "8.5.1", 61 | "resolved": "https://registry.npmjs.org/pg/-/pg-8.5.1.tgz", 62 | "integrity": "sha512-9wm3yX9lCfjvA98ybCyw2pADUivyNWT/yIP4ZcDVpMN0og70BUWYEGXPCTAQdGTAqnytfRADb7NERrY1qxhIqw==", 63 | "dependencies": { 64 | "buffer-writer": "2.0.0", 65 | "packet-reader": "1.0.0", 66 | "pg-connection-string": "^2.4.0", 67 | "pg-pool": "^3.2.2", 68 | "pg-protocol": "^1.4.0", 69 | "pg-types": "^2.1.0", 70 | "pgpass": "1.x" 71 | }, 72 | "engines": { 73 | "node": ">= 8.0.0" 74 | } 75 | }, 76 | "node_modules/pg-connection-string": { 77 | "version": "2.4.0", 78 | "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.4.0.tgz", 79 | "integrity": "sha512-3iBXuv7XKvxeMrIgym7njT+HlZkwZqqGX4Bu9cci8xHZNT+Um1gWKqCsAzcC0d95rcKMU5WBg6YRUcHyV0HZKQ==" 80 | }, 81 | "node_modules/pg-int8": { 82 | "version": "1.0.1", 83 | "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", 84 | "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", 85 | "engines": { 86 | "node": ">=4.0.0" 87 | } 88 | }, 89 | "node_modules/pg-pool": { 90 | "version": "3.2.2", 91 | "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.2.2.tgz", 92 | "integrity": "sha512-ORJoFxAlmmros8igi608iVEbQNNZlp89diFVx6yV5v+ehmpMY9sK6QgpmgoXbmkNaBAx8cOOZh9g80kJv1ooyA==" 93 | }, 94 | "node_modules/pg-protocol": { 95 | "version": "1.4.0", 96 | "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.4.0.tgz", 97 | "integrity": "sha512-El+aXWcwG/8wuFICMQjM5ZSAm6OWiJicFdNYo+VY3QP+8vI4SvLIWVe51PppTzMhikUJR+PsyIFKqfdXPz/yxA==" 98 | }, 99 | "node_modules/pg-types": { 100 | "version": "2.2.0", 101 | "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", 102 | "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", 103 | "dependencies": { 104 | "pg-int8": "1.0.1", 105 | "postgres-array": "~2.0.0", 106 | "postgres-bytea": "~1.0.0", 107 | "postgres-date": "~1.0.4", 108 | "postgres-interval": "^1.1.0" 109 | }, 110 | "engines": { 111 | "node": ">=4" 112 | } 113 | }, 114 | "node_modules/pgpass": { 115 | "version": "1.0.4", 116 | "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.4.tgz", 117 | "integrity": "sha512-YmuA56alyBq7M59vxVBfPJrGSozru8QAdoNlWuW3cz8l+UX3cWge0vTvjKhsSHSJpo3Bom8/Mm6hf0TR5GY0+w==", 118 | "dependencies": { 119 | "split2": "^3.1.1" 120 | } 121 | }, 122 | "node_modules/postgres-array": { 123 | "version": "2.0.0", 124 | "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", 125 | "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", 126 | "engines": { 127 | "node": ">=4" 128 | } 129 | }, 130 | "node_modules/postgres-bytea": { 131 | "version": "1.0.0", 132 | "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", 133 | "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=", 134 | "engines": { 135 | "node": ">=0.10.0" 136 | } 137 | }, 138 | "node_modules/postgres-date": { 139 | "version": "1.0.7", 140 | "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", 141 | "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", 142 | "engines": { 143 | "node": ">=0.10.0" 144 | } 145 | }, 146 | "node_modules/postgres-interval": { 147 | "version": "1.2.0", 148 | "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", 149 | "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", 150 | "dependencies": { 151 | "xtend": "^4.0.0" 152 | }, 153 | "engines": { 154 | "node": ">=0.10.0" 155 | } 156 | }, 157 | "node_modules/readable-stream": { 158 | "version": "3.6.0", 159 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 160 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 161 | "dependencies": { 162 | "inherits": "^2.0.3", 163 | "string_decoder": "^1.1.1", 164 | "util-deprecate": "^1.0.1" 165 | }, 166 | "engines": { 167 | "node": ">= 6" 168 | } 169 | }, 170 | "node_modules/safe-buffer": { 171 | "version": "5.2.1", 172 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 173 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 174 | }, 175 | "node_modules/split2": { 176 | "version": "3.2.2", 177 | "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", 178 | "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", 179 | "dependencies": { 180 | "readable-stream": "^3.0.0" 181 | } 182 | }, 183 | "node_modules/string_decoder": { 184 | "version": "1.3.0", 185 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 186 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 187 | "dependencies": { 188 | "safe-buffer": "~5.2.0" 189 | } 190 | }, 191 | "node_modules/typescript": { 192 | "version": "4.1.3", 193 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", 194 | "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", 195 | "dev": true, 196 | "bin": { 197 | "tsc": "bin/tsc", 198 | "tsserver": "bin/tsserver" 199 | }, 200 | "engines": { 201 | "node": ">=4.2.0" 202 | } 203 | }, 204 | "node_modules/util-deprecate": { 205 | "version": "1.0.2", 206 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 207 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 208 | }, 209 | "node_modules/xtend": { 210 | "version": "4.0.2", 211 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 212 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", 213 | "engines": { 214 | "node": ">=0.4" 215 | } 216 | } 217 | }, 218 | "dependencies": { 219 | "@types/node": { 220 | "version": "14.14.16", 221 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.16.tgz", 222 | "integrity": "sha512-naXYePhweTi+BMv11TgioE2/FXU4fSl29HAH1ffxVciNsH3rYXjNP2yM8wqmSm7jS20gM8TIklKiTen+1iVncw==", 223 | "dev": true 224 | }, 225 | "@types/pg": { 226 | "version": "7.14.7", 227 | "resolved": "https://registry.npmjs.org/@types/pg/-/pg-7.14.7.tgz", 228 | "integrity": "sha512-ZnMOUidTP6Lwsb0bxHL6PVIL1lVC2CYNQWlA79kQ6nn0rK1/ynvkyN1wsR9pVZaP4WcCNioKT/2aU5UuLIQy2w==", 229 | "dev": true, 230 | "requires": { 231 | "@types/node": "*", 232 | "@types/pg-types": "*" 233 | } 234 | }, 235 | "@types/pg-types": { 236 | "version": "1.11.5", 237 | "resolved": "https://registry.npmjs.org/@types/pg-types/-/pg-types-1.11.5.tgz", 238 | "integrity": "sha512-L8ogeT6vDzT1vxlW3KITTCt+BVXXVkLXfZ/XNm6UqbcJgxf+KPO7yjWx7dQQE8RW07KopL10x2gNMs41+IkMGQ==", 239 | "dev": true 240 | }, 241 | "buffer-writer": { 242 | "version": "2.0.0", 243 | "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", 244 | "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" 245 | }, 246 | "inherits": { 247 | "version": "2.0.4", 248 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 249 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 250 | }, 251 | "packet-reader": { 252 | "version": "1.0.0", 253 | "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", 254 | "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" 255 | }, 256 | "pg": { 257 | "version": "8.5.1", 258 | "resolved": "https://registry.npmjs.org/pg/-/pg-8.5.1.tgz", 259 | "integrity": "sha512-9wm3yX9lCfjvA98ybCyw2pADUivyNWT/yIP4ZcDVpMN0og70BUWYEGXPCTAQdGTAqnytfRADb7NERrY1qxhIqw==", 260 | "requires": { 261 | "buffer-writer": "2.0.0", 262 | "packet-reader": "1.0.0", 263 | "pg-connection-string": "^2.4.0", 264 | "pg-pool": "^3.2.2", 265 | "pg-protocol": "^1.4.0", 266 | "pg-types": "^2.1.0", 267 | "pgpass": "1.x" 268 | } 269 | }, 270 | "pg-connection-string": { 271 | "version": "2.4.0", 272 | "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.4.0.tgz", 273 | "integrity": "sha512-3iBXuv7XKvxeMrIgym7njT+HlZkwZqqGX4Bu9cci8xHZNT+Um1gWKqCsAzcC0d95rcKMU5WBg6YRUcHyV0HZKQ==" 274 | }, 275 | "pg-int8": { 276 | "version": "1.0.1", 277 | "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", 278 | "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" 279 | }, 280 | "pg-pool": { 281 | "version": "3.2.2", 282 | "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.2.2.tgz", 283 | "integrity": "sha512-ORJoFxAlmmros8igi608iVEbQNNZlp89diFVx6yV5v+ehmpMY9sK6QgpmgoXbmkNaBAx8cOOZh9g80kJv1ooyA==" 284 | }, 285 | "pg-protocol": { 286 | "version": "1.4.0", 287 | "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.4.0.tgz", 288 | "integrity": "sha512-El+aXWcwG/8wuFICMQjM5ZSAm6OWiJicFdNYo+VY3QP+8vI4SvLIWVe51PppTzMhikUJR+PsyIFKqfdXPz/yxA==" 289 | }, 290 | "pg-types": { 291 | "version": "2.2.0", 292 | "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", 293 | "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", 294 | "requires": { 295 | "pg-int8": "1.0.1", 296 | "postgres-array": "~2.0.0", 297 | "postgres-bytea": "~1.0.0", 298 | "postgres-date": "~1.0.4", 299 | "postgres-interval": "^1.1.0" 300 | } 301 | }, 302 | "pgpass": { 303 | "version": "1.0.4", 304 | "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.4.tgz", 305 | "integrity": "sha512-YmuA56alyBq7M59vxVBfPJrGSozru8QAdoNlWuW3cz8l+UX3cWge0vTvjKhsSHSJpo3Bom8/Mm6hf0TR5GY0+w==", 306 | "requires": { 307 | "split2": "^3.1.1" 308 | } 309 | }, 310 | "postgres-array": { 311 | "version": "2.0.0", 312 | "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", 313 | "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" 314 | }, 315 | "postgres-bytea": { 316 | "version": "1.0.0", 317 | "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", 318 | "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=" 319 | }, 320 | "postgres-date": { 321 | "version": "1.0.7", 322 | "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", 323 | "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==" 324 | }, 325 | "postgres-interval": { 326 | "version": "1.2.0", 327 | "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", 328 | "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", 329 | "requires": { 330 | "xtend": "^4.0.0" 331 | } 332 | }, 333 | "readable-stream": { 334 | "version": "3.6.0", 335 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 336 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 337 | "requires": { 338 | "inherits": "^2.0.3", 339 | "string_decoder": "^1.1.1", 340 | "util-deprecate": "^1.0.1" 341 | } 342 | }, 343 | "safe-buffer": { 344 | "version": "5.2.1", 345 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 346 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 347 | }, 348 | "split2": { 349 | "version": "3.2.2", 350 | "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", 351 | "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", 352 | "requires": { 353 | "readable-stream": "^3.0.0" 354 | } 355 | }, 356 | "string_decoder": { 357 | "version": "1.3.0", 358 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 359 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 360 | "requires": { 361 | "safe-buffer": "~5.2.0" 362 | } 363 | }, 364 | "typescript": { 365 | "version": "4.1.3", 366 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", 367 | "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", 368 | "dev": true 369 | }, 370 | "util-deprecate": { 371 | "version": "1.0.2", 372 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 373 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 374 | }, 375 | "xtend": { 376 | "version": "4.0.2", 377 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 378 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" 379 | } 380 | } 381 | } 382 | -------------------------------------------------------------------------------- /get_schema/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "get_schema", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "@types/node": "^14.14.13", 14 | "@types/pg": "^7.14.7", 15 | "typescript": "^4.1.3" 16 | }, 17 | "dependencies": { 18 | "pg": "^8.5.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /get_schema/src/get_schema.ts: -------------------------------------------------------------------------------- 1 | // node /tapeworm/get_schema/out/get_schema.js database 5432 postgres postgres 2 | 3 | import { Conn, get_schema, get_client } from './schema'; 4 | 5 | async function main() { 6 | const args = process.argv.slice(2); // chop off node and get_schema.js 7 | 8 | const conn: Conn = { 9 | host: args[0], 10 | port: Number(args[1]), 11 | user: args[2], 12 | db: args[3] 13 | }; 14 | 15 | const schema = await get_schema(get_client(conn)); 16 | console.log(JSON.stringify(schema)); 17 | process.exit(0); 18 | } 19 | 20 | main().catch(console.error); 21 | -------------------------------------------------------------------------------- /get_schema/src/index.ts: -------------------------------------------------------------------------------- 1 | const args = process.argv.slice(2); // chop off node and index.ts 2 | 3 | import { Client } from 'pg'; 4 | const client = new Client({ 5 | host: args[0], 6 | port: Number(args[1]), 7 | user: args[2], 8 | database: args[3] 9 | }); 10 | 11 | async function main() { 12 | await client.connect(); 13 | const query = `select column_name, data_type, is_nullable from information_schema.columns where table_schema = 'public' and table_name = '${args[4]}'`; 14 | const res = await client.query(query); 15 | console.log(JSON.stringify({rows: res.rows, args})); 16 | await client.end() 17 | } 18 | 19 | main().catch(console.error).then((x) => console.error('done')); 20 | -------------------------------------------------------------------------------- /get_schema/src/schema.ts: -------------------------------------------------------------------------------- 1 | import { Client } from 'pg'; 2 | 3 | // A type representing postgres connection information 4 | export type Conn = { 5 | db: string, 6 | user: string, 7 | host: string, 8 | port: number 9 | }; 10 | 11 | export type Stub = { target_table: TGT }; 12 | export type ForeignTable = { tp: 'foreign', target_col: string, target_table: string }; 13 | 14 | export type Column = { 15 | column_name: string, 16 | data_type: 'integer' | 'text' | ForeignTable, 17 | is_nullable: 'YES' | 'NO', 18 | }; 19 | 20 | export type TableSchema = { 21 | cols: Column[] 22 | } 23 | 24 | export type TableSchemas = { 25 | [name: string]: TableSchema 26 | } 27 | 28 | type ForeignKeyConstraint = { 29 | source_table: string, 30 | source_col: string, 31 | target_table: string, 32 | target_col: string 33 | }; 34 | 35 | export type TableForeignKeyMap = { 36 | [source_col: string]: 37 | { target_table: string, target_col: string } 38 | }; 39 | 40 | export type ForeignKeyMap = { 41 | [source_table: string]: TableForeignKeyMap 42 | }; 43 | 44 | export type DbSchema = { 45 | table_schemas: TableSchemas, 46 | } 47 | 48 | export type DbClient = Client; 49 | 50 | export function get_client(conn: Conn): DbClient { 51 | return new Client({ 52 | host: conn.host, 53 | port: conn.port, 54 | user: conn.user, 55 | database: conn.db 56 | }); 57 | } 58 | 59 | const foreign_key_query = ` 60 | SELECT 61 | tc.table_name as source_table, 62 | kcu.column_name AS source_col, 63 | ccu.table_name as target_table, 64 | ccu.column_name as target_col 65 | FROM 66 | information_schema.table_constraints as tc 67 | JOIN information_schema.key_column_usage as kcu 68 | ON tc.table_schema = kcu.table_schema 69 | AND tc.table_name = kcu.table_name 70 | AND tc.constraint_name = kcu.constraint_name 71 | JOIN information_schema.constraint_column_usage as ccu 72 | ON tc.table_schema = ccu.table_schema 73 | AND tc.constraint_name = ccu.constraint_name 74 | WHERE 75 | tc.constraint_schema = 'public' 76 | AND tc.constraint_type = 'FOREIGN KEY'; 77 | `; 78 | 79 | const column_type_query = ` 80 | SELECT table_name, column_name, data_type, is_nullable 81 | FROM information_schema.columns 82 | WHERE table_schema = 'public' 83 | `; 84 | 85 | async function get_column_types(client: Client): Promise { 86 | const res = await client.query(column_type_query); 87 | const table_schemas: TableSchemas = {}; 88 | res.rows.forEach((row: any) => { 89 | const { table_name, column_name, data_type, is_nullable } = row; 90 | table_schemas[table_name] ||= { cols: [] }; 91 | table_schemas[table_name].cols.push({ column_name, data_type, is_nullable }); 92 | }); 93 | return table_schemas; 94 | } 95 | 96 | async function get_foreign_keys(client: Client): Promise { 97 | return (await client.query(foreign_key_query)).rows; 98 | } 99 | 100 | export async function get_schema(client: Client): Promise { 101 | await client.connect(); 102 | 103 | const table_schemas = await get_column_types(client); 104 | const fkeys = await get_foreign_keys(client); 105 | 106 | fkeys.forEach(row => { 107 | table_schemas[row.source_table].cols.forEach(col => { 108 | if (col.column_name == row.source_col) 109 | col.data_type = { tp: 'foreign', target_col: row.target_col, target_table: row.target_table }; 110 | }); 111 | }); 112 | 113 | return { table_schemas }; 114 | } 115 | -------------------------------------------------------------------------------- /get_schema/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "out", 4 | "target": "es2017", 5 | "module": "commonjs", 6 | "lib": [ "es2017.object", "dom", "es6" ], 7 | "sourceMap": true, 8 | "noFallthroughCasesInSwitch": true, 9 | "noImplicitAny": true, 10 | "strictNullChecks": true, 11 | "strictPropertyInitialization": true, 12 | "esModuleInterop": true 13 | }, 14 | "include": ["src/**/*.ts"] 15 | } 16 | -------------------------------------------------------------------------------- /magic_demo/Makefile: -------------------------------------------------------------------------------- 1 | compile: 2 | node ../TypeScript/tsc.js 3 | 4 | run: 5 | node out/magic_demo/src/orm-demo.js 6 | -------------------------------------------------------------------------------- /magic_demo/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tapeworm", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "tapeworm", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "pg": "^8.5.1", 13 | "pg-format": "^1.0.4", 14 | "sequelize": "^6.5.0" 15 | }, 16 | "devDependencies": { 17 | "@types/node": "^14.14.13", 18 | "@types/pg-format": "^1.0.1", 19 | "@types/validator": "^13.1.3", 20 | "typescript": "^4.1.3" 21 | } 22 | }, 23 | "node_modules/@types/node": { 24 | "version": "14.14.13", 25 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.13.tgz", 26 | "integrity": "sha512-vbxr0VZ8exFMMAjCW8rJwaya0dMCDyYW2ZRdTyjtrCvJoENMpdUHOT/eTzvgyA5ZnqRZ/sI0NwqAxNHKYokLJQ==" 27 | }, 28 | "node_modules/@types/pg-format": { 29 | "version": "1.0.1", 30 | "resolved": "https://registry.npmjs.org/@types/pg-format/-/pg-format-1.0.1.tgz", 31 | "integrity": "sha512-TC073gk+fvWGenuE7sDEjmt33HJkNhKJ9uLRgP9O9KQGSUaN1hA7Snbr63EU9/aE1X4Zz0+1BXhSBdZCYFWXog==", 32 | "dev": true 33 | }, 34 | "node_modules/@types/validator": { 35 | "version": "13.1.3", 36 | "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.1.3.tgz", 37 | "integrity": "sha512-DaOWN1zf7j+8nHhqXhIgNmS+ltAC53NXqGxYuBhWqWgqolRhddKzfZU814lkHQSTG0IUfQxU7Cg0gb8fFWo2mA==", 38 | "dev": true 39 | }, 40 | "node_modules/any-promise": { 41 | "version": "1.3.0", 42 | "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", 43 | "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" 44 | }, 45 | "node_modules/buffer-writer": { 46 | "version": "2.0.0", 47 | "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", 48 | "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", 49 | "engines": { 50 | "node": ">=4" 51 | } 52 | }, 53 | "node_modules/debug": { 54 | "version": "4.3.1", 55 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", 56 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", 57 | "dependencies": { 58 | "ms": "2.1.2" 59 | }, 60 | "engines": { 61 | "node": ">=6.0" 62 | }, 63 | "peerDependenciesMeta": { 64 | "supports-color": { 65 | "optional": true 66 | } 67 | } 68 | }, 69 | "node_modules/dottie": { 70 | "version": "2.0.2", 71 | "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.2.tgz", 72 | "integrity": "sha512-fmrwR04lsniq/uSr8yikThDTrM7epXHBAAjH9TbeH3rEA8tdCO7mRzB9hdmdGyJCxF8KERo9CITcm3kGuoyMhg==" 73 | }, 74 | "node_modules/inflection": { 75 | "version": "1.12.0", 76 | "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz", 77 | "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY=", 78 | "engines": [ 79 | "node >= 0.4.0" 80 | ] 81 | }, 82 | "node_modules/inherits": { 83 | "version": "2.0.4", 84 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 85 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 86 | }, 87 | "node_modules/lodash": { 88 | "version": "4.17.20", 89 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", 90 | "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" 91 | }, 92 | "node_modules/lru-cache": { 93 | "version": "6.0.0", 94 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 95 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 96 | "dependencies": { 97 | "yallist": "^4.0.0" 98 | }, 99 | "engines": { 100 | "node": ">=10" 101 | } 102 | }, 103 | "node_modules/moment": { 104 | "version": "2.29.1", 105 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", 106 | "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", 107 | "engines": { 108 | "node": "*" 109 | } 110 | }, 111 | "node_modules/moment-timezone": { 112 | "version": "0.5.32", 113 | "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.32.tgz", 114 | "integrity": "sha512-Z8QNyuQHQAmWucp8Knmgei8YNo28aLjJq6Ma+jy1ZSpSk5nyfRT8xgUbSQvD2+2UajISfenndwvFuH3NGS+nvA==", 115 | "dependencies": { 116 | "moment": ">= 2.9.0" 117 | }, 118 | "engines": { 119 | "node": "*" 120 | } 121 | }, 122 | "node_modules/ms": { 123 | "version": "2.1.2", 124 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 125 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 126 | }, 127 | "node_modules/packet-reader": { 128 | "version": "1.0.0", 129 | "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", 130 | "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" 131 | }, 132 | "node_modules/pg": { 133 | "version": "8.5.1", 134 | "resolved": "https://registry.npmjs.org/pg/-/pg-8.5.1.tgz", 135 | "integrity": "sha512-9wm3yX9lCfjvA98ybCyw2pADUivyNWT/yIP4ZcDVpMN0og70BUWYEGXPCTAQdGTAqnytfRADb7NERrY1qxhIqw==", 136 | "dependencies": { 137 | "buffer-writer": "2.0.0", 138 | "packet-reader": "1.0.0", 139 | "pg-connection-string": "^2.4.0", 140 | "pg-pool": "^3.2.2", 141 | "pg-protocol": "^1.4.0", 142 | "pg-types": "^2.1.0", 143 | "pgpass": "1.x" 144 | }, 145 | "engines": { 146 | "node": ">= 8.0.0" 147 | }, 148 | "peerDependencies": { 149 | "pg-native": ">=2.0.0" 150 | }, 151 | "peerDependenciesMeta": { 152 | "pg-native": { 153 | "optional": true 154 | } 155 | } 156 | }, 157 | "node_modules/pg-connection-string": { 158 | "version": "2.4.0", 159 | "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.4.0.tgz", 160 | "integrity": "sha512-3iBXuv7XKvxeMrIgym7njT+HlZkwZqqGX4Bu9cci8xHZNT+Um1gWKqCsAzcC0d95rcKMU5WBg6YRUcHyV0HZKQ==" 161 | }, 162 | "node_modules/pg-format": { 163 | "version": "1.0.4", 164 | "resolved": "https://registry.npmjs.org/pg-format/-/pg-format-1.0.4.tgz", 165 | "integrity": "sha1-J3NCNsKtP05QZJFaWTNOIAQKgo4=", 166 | "engines": { 167 | "node": ">=4.0" 168 | } 169 | }, 170 | "node_modules/pg-int8": { 171 | "version": "1.0.1", 172 | "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", 173 | "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", 174 | "engines": { 175 | "node": ">=4.0.0" 176 | } 177 | }, 178 | "node_modules/pg-pool": { 179 | "version": "3.2.2", 180 | "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.2.2.tgz", 181 | "integrity": "sha512-ORJoFxAlmmros8igi608iVEbQNNZlp89diFVx6yV5v+ehmpMY9sK6QgpmgoXbmkNaBAx8cOOZh9g80kJv1ooyA==", 182 | "peerDependencies": { 183 | "pg": ">=8.0" 184 | } 185 | }, 186 | "node_modules/pg-protocol": { 187 | "version": "1.4.0", 188 | "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.4.0.tgz", 189 | "integrity": "sha512-El+aXWcwG/8wuFICMQjM5ZSAm6OWiJicFdNYo+VY3QP+8vI4SvLIWVe51PppTzMhikUJR+PsyIFKqfdXPz/yxA==" 190 | }, 191 | "node_modules/pg-types": { 192 | "version": "2.2.0", 193 | "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", 194 | "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", 195 | "dependencies": { 196 | "pg-int8": "1.0.1", 197 | "postgres-array": "~2.0.0", 198 | "postgres-bytea": "~1.0.0", 199 | "postgres-date": "~1.0.4", 200 | "postgres-interval": "^1.1.0" 201 | }, 202 | "engines": { 203 | "node": ">=4" 204 | } 205 | }, 206 | "node_modules/pgpass": { 207 | "version": "1.0.4", 208 | "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.4.tgz", 209 | "integrity": "sha512-YmuA56alyBq7M59vxVBfPJrGSozru8QAdoNlWuW3cz8l+UX3cWge0vTvjKhsSHSJpo3Bom8/Mm6hf0TR5GY0+w==", 210 | "dependencies": { 211 | "split2": "^3.1.1" 212 | } 213 | }, 214 | "node_modules/postgres-array": { 215 | "version": "2.0.0", 216 | "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", 217 | "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", 218 | "engines": { 219 | "node": ">=4" 220 | } 221 | }, 222 | "node_modules/postgres-bytea": { 223 | "version": "1.0.0", 224 | "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", 225 | "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=", 226 | "engines": { 227 | "node": ">=0.10.0" 228 | } 229 | }, 230 | "node_modules/postgres-date": { 231 | "version": "1.0.7", 232 | "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", 233 | "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", 234 | "engines": { 235 | "node": ">=0.10.0" 236 | } 237 | }, 238 | "node_modules/postgres-interval": { 239 | "version": "1.2.0", 240 | "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", 241 | "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", 242 | "dependencies": { 243 | "xtend": "^4.0.0" 244 | }, 245 | "engines": { 246 | "node": ">=0.10.0" 247 | } 248 | }, 249 | "node_modules/readable-stream": { 250 | "version": "3.6.0", 251 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 252 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 253 | "dependencies": { 254 | "inherits": "^2.0.3", 255 | "string_decoder": "^1.1.1", 256 | "util-deprecate": "^1.0.1" 257 | }, 258 | "engines": { 259 | "node": ">= 6" 260 | } 261 | }, 262 | "node_modules/retry-as-promised": { 263 | "version": "3.2.0", 264 | "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-3.2.0.tgz", 265 | "integrity": "sha512-CybGs60B7oYU/qSQ6kuaFmRd9sTZ6oXSc0toqePvV74Ac6/IFZSI1ReFQmtCN+uvW1Mtqdwpvt/LGOiCBAY2Mg==", 266 | "dependencies": { 267 | "any-promise": "^1.3.0" 268 | } 269 | }, 270 | "node_modules/safe-buffer": { 271 | "version": "5.2.1", 272 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 273 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 274 | "funding": [ 275 | { 276 | "type": "github", 277 | "url": "https://github.com/sponsors/feross" 278 | }, 279 | { 280 | "type": "patreon", 281 | "url": "https://www.patreon.com/feross" 282 | }, 283 | { 284 | "type": "consulting", 285 | "url": "https://feross.org/support" 286 | } 287 | ] 288 | }, 289 | "node_modules/semver": { 290 | "version": "7.3.4", 291 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", 292 | "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", 293 | "dependencies": { 294 | "lru-cache": "^6.0.0" 295 | }, 296 | "bin": { 297 | "semver": "bin/semver.js" 298 | }, 299 | "engines": { 300 | "node": ">=10" 301 | } 302 | }, 303 | "node_modules/sequelize": { 304 | "version": "6.5.0", 305 | "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.5.0.tgz", 306 | "integrity": "sha512-owBt8fnzVy8E1OvyCyfCdVk7OOLyPVrBCMEf+CvRReC5oCyo+UqeXCtwaex9L6LM9ifZ1i3TG3sFeM5MgLK0CQ==", 307 | "license": "MIT", 308 | "dependencies": { 309 | "debug": "^4.1.1", 310 | "dottie": "^2.0.0", 311 | "inflection": "1.12.0", 312 | "lodash": "^4.17.20", 313 | "moment": "^2.26.0", 314 | "moment-timezone": "^0.5.31", 315 | "retry-as-promised": "^3.2.0", 316 | "semver": "^7.3.2", 317 | "sequelize-pool": "^6.0.0", 318 | "toposort-class": "^1.0.1", 319 | "uuid": "^8.1.0", 320 | "validator": "^10.11.0", 321 | "wkx": "^0.5.0" 322 | }, 323 | "engines": { 324 | "node": ">=10.0.0" 325 | }, 326 | "peerDependenciesMeta": { 327 | "mariadb": { 328 | "optional": true 329 | }, 330 | "mysql2": { 331 | "optional": true 332 | }, 333 | "pg": { 334 | "optional": true 335 | }, 336 | "pg-hstore": { 337 | "optional": true 338 | }, 339 | "sqlite3": { 340 | "optional": true 341 | }, 342 | "tedious": { 343 | "optional": true 344 | } 345 | } 346 | }, 347 | "node_modules/sequelize-pool": { 348 | "version": "6.1.0", 349 | "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-6.1.0.tgz", 350 | "integrity": "sha512-4YwEw3ZgK/tY/so+GfnSgXkdwIJJ1I32uZJztIEgZeAO6HMgj64OzySbWLgxj+tXhZCJnzRfkY9gINw8Ft8ZMg==", 351 | "engines": { 352 | "node": ">= 10.0.0" 353 | } 354 | }, 355 | "node_modules/split2": { 356 | "version": "3.2.2", 357 | "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", 358 | "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", 359 | "dependencies": { 360 | "readable-stream": "^3.0.0" 361 | } 362 | }, 363 | "node_modules/string_decoder": { 364 | "version": "1.3.0", 365 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 366 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 367 | "dependencies": { 368 | "safe-buffer": "~5.2.0" 369 | } 370 | }, 371 | "node_modules/toposort-class": { 372 | "version": "1.0.1", 373 | "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", 374 | "integrity": "sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg=" 375 | }, 376 | "node_modules/typescript": { 377 | "version": "4.1.3", 378 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", 379 | "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", 380 | "dev": true, 381 | "bin": { 382 | "tsc": "bin/tsc", 383 | "tsserver": "bin/tsserver" 384 | }, 385 | "engines": { 386 | "node": ">=4.2.0" 387 | } 388 | }, 389 | "node_modules/util-deprecate": { 390 | "version": "1.0.2", 391 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 392 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 393 | }, 394 | "node_modules/uuid": { 395 | "version": "8.3.2", 396 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", 397 | "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", 398 | "bin": { 399 | "uuid": "dist/bin/uuid" 400 | } 401 | }, 402 | "node_modules/validator": { 403 | "version": "10.11.0", 404 | "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz", 405 | "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==", 406 | "engines": { 407 | "node": ">= 0.10" 408 | } 409 | }, 410 | "node_modules/wkx": { 411 | "version": "0.5.0", 412 | "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", 413 | "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", 414 | "dependencies": { 415 | "@types/node": "*" 416 | } 417 | }, 418 | "node_modules/xtend": { 419 | "version": "4.0.2", 420 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 421 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", 422 | "engines": { 423 | "node": ">=0.4" 424 | } 425 | }, 426 | "node_modules/yallist": { 427 | "version": "4.0.0", 428 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 429 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 430 | } 431 | }, 432 | "dependencies": { 433 | "@types/node": { 434 | "version": "14.14.13", 435 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.13.tgz", 436 | "integrity": "sha512-vbxr0VZ8exFMMAjCW8rJwaya0dMCDyYW2ZRdTyjtrCvJoENMpdUHOT/eTzvgyA5ZnqRZ/sI0NwqAxNHKYokLJQ==" 437 | }, 438 | "@types/pg-format": { 439 | "version": "1.0.1", 440 | "resolved": "https://registry.npmjs.org/@types/pg-format/-/pg-format-1.0.1.tgz", 441 | "integrity": "sha512-TC073gk+fvWGenuE7sDEjmt33HJkNhKJ9uLRgP9O9KQGSUaN1hA7Snbr63EU9/aE1X4Zz0+1BXhSBdZCYFWXog==", 442 | "dev": true 443 | }, 444 | "@types/validator": { 445 | "version": "13.1.3", 446 | "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.1.3.tgz", 447 | "integrity": "sha512-DaOWN1zf7j+8nHhqXhIgNmS+ltAC53NXqGxYuBhWqWgqolRhddKzfZU814lkHQSTG0IUfQxU7Cg0gb8fFWo2mA==", 448 | "dev": true 449 | }, 450 | "any-promise": { 451 | "version": "1.3.0", 452 | "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", 453 | "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" 454 | }, 455 | "buffer-writer": { 456 | "version": "2.0.0", 457 | "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", 458 | "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" 459 | }, 460 | "debug": { 461 | "version": "4.3.1", 462 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", 463 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", 464 | "requires": { 465 | "ms": "2.1.2" 466 | } 467 | }, 468 | "dottie": { 469 | "version": "2.0.2", 470 | "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.2.tgz", 471 | "integrity": "sha512-fmrwR04lsniq/uSr8yikThDTrM7epXHBAAjH9TbeH3rEA8tdCO7mRzB9hdmdGyJCxF8KERo9CITcm3kGuoyMhg==" 472 | }, 473 | "inflection": { 474 | "version": "1.12.0", 475 | "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz", 476 | "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY=" 477 | }, 478 | "inherits": { 479 | "version": "2.0.4", 480 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 481 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 482 | }, 483 | "lodash": { 484 | "version": "4.17.20", 485 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", 486 | "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" 487 | }, 488 | "lru-cache": { 489 | "version": "6.0.0", 490 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 491 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 492 | "requires": { 493 | "yallist": "^4.0.0" 494 | } 495 | }, 496 | "moment": { 497 | "version": "2.29.1", 498 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", 499 | "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" 500 | }, 501 | "moment-timezone": { 502 | "version": "0.5.32", 503 | "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.32.tgz", 504 | "integrity": "sha512-Z8QNyuQHQAmWucp8Knmgei8YNo28aLjJq6Ma+jy1ZSpSk5nyfRT8xgUbSQvD2+2UajISfenndwvFuH3NGS+nvA==", 505 | "requires": { 506 | "moment": ">= 2.9.0" 507 | } 508 | }, 509 | "ms": { 510 | "version": "2.1.2", 511 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 512 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 513 | }, 514 | "packet-reader": { 515 | "version": "1.0.0", 516 | "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", 517 | "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" 518 | }, 519 | "pg": { 520 | "version": "8.5.1", 521 | "resolved": "https://registry.npmjs.org/pg/-/pg-8.5.1.tgz", 522 | "integrity": "sha512-9wm3yX9lCfjvA98ybCyw2pADUivyNWT/yIP4ZcDVpMN0og70BUWYEGXPCTAQdGTAqnytfRADb7NERrY1qxhIqw==", 523 | "requires": { 524 | "buffer-writer": "2.0.0", 525 | "packet-reader": "1.0.0", 526 | "pg-connection-string": "^2.4.0", 527 | "pg-pool": "^3.2.2", 528 | "pg-protocol": "^1.4.0", 529 | "pg-types": "^2.1.0", 530 | "pgpass": "1.x" 531 | } 532 | }, 533 | "pg-connection-string": { 534 | "version": "2.4.0", 535 | "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.4.0.tgz", 536 | "integrity": "sha512-3iBXuv7XKvxeMrIgym7njT+HlZkwZqqGX4Bu9cci8xHZNT+Um1gWKqCsAzcC0d95rcKMU5WBg6YRUcHyV0HZKQ==" 537 | }, 538 | "pg-format": { 539 | "version": "1.0.4", 540 | "resolved": "https://registry.npmjs.org/pg-format/-/pg-format-1.0.4.tgz", 541 | "integrity": "sha1-J3NCNsKtP05QZJFaWTNOIAQKgo4=" 542 | }, 543 | "pg-int8": { 544 | "version": "1.0.1", 545 | "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", 546 | "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" 547 | }, 548 | "pg-pool": { 549 | "version": "3.2.2", 550 | "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.2.2.tgz", 551 | "integrity": "sha512-ORJoFxAlmmros8igi608iVEbQNNZlp89diFVx6yV5v+ehmpMY9sK6QgpmgoXbmkNaBAx8cOOZh9g80kJv1ooyA==", 552 | "requires": {} 553 | }, 554 | "pg-protocol": { 555 | "version": "1.4.0", 556 | "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.4.0.tgz", 557 | "integrity": "sha512-El+aXWcwG/8wuFICMQjM5ZSAm6OWiJicFdNYo+VY3QP+8vI4SvLIWVe51PppTzMhikUJR+PsyIFKqfdXPz/yxA==" 558 | }, 559 | "pg-types": { 560 | "version": "2.2.0", 561 | "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", 562 | "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", 563 | "requires": { 564 | "pg-int8": "1.0.1", 565 | "postgres-array": "~2.0.0", 566 | "postgres-bytea": "~1.0.0", 567 | "postgres-date": "~1.0.4", 568 | "postgres-interval": "^1.1.0" 569 | } 570 | }, 571 | "pgpass": { 572 | "version": "1.0.4", 573 | "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.4.tgz", 574 | "integrity": "sha512-YmuA56alyBq7M59vxVBfPJrGSozru8QAdoNlWuW3cz8l+UX3cWge0vTvjKhsSHSJpo3Bom8/Mm6hf0TR5GY0+w==", 575 | "requires": { 576 | "split2": "^3.1.1" 577 | } 578 | }, 579 | "postgres-array": { 580 | "version": "2.0.0", 581 | "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", 582 | "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" 583 | }, 584 | "postgres-bytea": { 585 | "version": "1.0.0", 586 | "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", 587 | "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=" 588 | }, 589 | "postgres-date": { 590 | "version": "1.0.7", 591 | "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", 592 | "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==" 593 | }, 594 | "postgres-interval": { 595 | "version": "1.2.0", 596 | "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", 597 | "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", 598 | "requires": { 599 | "xtend": "^4.0.0" 600 | } 601 | }, 602 | "readable-stream": { 603 | "version": "3.6.0", 604 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 605 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 606 | "requires": { 607 | "inherits": "^2.0.3", 608 | "string_decoder": "^1.1.1", 609 | "util-deprecate": "^1.0.1" 610 | } 611 | }, 612 | "retry-as-promised": { 613 | "version": "3.2.0", 614 | "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-3.2.0.tgz", 615 | "integrity": "sha512-CybGs60B7oYU/qSQ6kuaFmRd9sTZ6oXSc0toqePvV74Ac6/IFZSI1ReFQmtCN+uvW1Mtqdwpvt/LGOiCBAY2Mg==", 616 | "requires": { 617 | "any-promise": "^1.3.0" 618 | } 619 | }, 620 | "safe-buffer": { 621 | "version": "5.2.1", 622 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 623 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 624 | }, 625 | "semver": { 626 | "version": "7.3.4", 627 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", 628 | "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", 629 | "requires": { 630 | "lru-cache": "^6.0.0" 631 | } 632 | }, 633 | "sequelize": { 634 | "version": "6.5.0", 635 | "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.5.0.tgz", 636 | "integrity": "sha512-owBt8fnzVy8E1OvyCyfCdVk7OOLyPVrBCMEf+CvRReC5oCyo+UqeXCtwaex9L6LM9ifZ1i3TG3sFeM5MgLK0CQ==", 637 | "requires": { 638 | "debug": "^4.1.1", 639 | "dottie": "^2.0.0", 640 | "inflection": "1.12.0", 641 | "lodash": "^4.17.20", 642 | "moment": "^2.26.0", 643 | "moment-timezone": "^0.5.31", 644 | "retry-as-promised": "^3.2.0", 645 | "semver": "^7.3.2", 646 | "sequelize-pool": "^6.0.0", 647 | "toposort-class": "^1.0.1", 648 | "uuid": "^8.1.0", 649 | "validator": "^10.11.0", 650 | "wkx": "^0.5.0" 651 | } 652 | }, 653 | "sequelize-pool": { 654 | "version": "6.1.0", 655 | "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-6.1.0.tgz", 656 | "integrity": "sha512-4YwEw3ZgK/tY/so+GfnSgXkdwIJJ1I32uZJztIEgZeAO6HMgj64OzySbWLgxj+tXhZCJnzRfkY9gINw8Ft8ZMg==" 657 | }, 658 | "split2": { 659 | "version": "3.2.2", 660 | "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", 661 | "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", 662 | "requires": { 663 | "readable-stream": "^3.0.0" 664 | } 665 | }, 666 | "string_decoder": { 667 | "version": "1.3.0", 668 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 669 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 670 | "requires": { 671 | "safe-buffer": "~5.2.0" 672 | } 673 | }, 674 | "toposort-class": { 675 | "version": "1.0.1", 676 | "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", 677 | "integrity": "sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg=" 678 | }, 679 | "typescript": { 680 | "version": "4.1.3", 681 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", 682 | "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", 683 | "dev": true 684 | }, 685 | "util-deprecate": { 686 | "version": "1.0.2", 687 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 688 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 689 | }, 690 | "uuid": { 691 | "version": "8.3.2", 692 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", 693 | "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" 694 | }, 695 | "validator": { 696 | "version": "10.11.0", 697 | "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz", 698 | "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==" 699 | }, 700 | "wkx": { 701 | "version": "0.5.0", 702 | "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", 703 | "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", 704 | "requires": { 705 | "@types/node": "*" 706 | } 707 | }, 708 | "xtend": { 709 | "version": "4.0.2", 710 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 711 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" 712 | }, 713 | "yallist": { 714 | "version": "4.0.0", 715 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 716 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 717 | } 718 | } 719 | } 720 | -------------------------------------------------------------------------------- /magic_demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tapeworm", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "@types/node": "^14.14.13", 14 | "@types/pg-format": "^1.0.1", 15 | "@types/validator": "^13.1.3", 16 | "typescript": "^4.1.3" 17 | }, 18 | "dependencies": { 19 | "pg": "^8.5.1", 20 | "pg-format": "^1.0.4", 21 | "sequelize": "^6.5.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /magic_demo/src/orm-demo.ts: -------------------------------------------------------------------------------- 1 | import { SchemaOf, getModels } from './orm'; 2 | 3 | const connection = { 4 | db: 'postgres', 5 | user: 'postgres', 6 | host: 'database', 7 | port: 5432 8 | } 9 | 10 | type MySchema = SchemaOf; 11 | 12 | 13 | async function go() { 14 | const models = await getModels(connection); 15 | const User = models.get('users'); 16 | const Paper = models.get('papers'); 17 | const Review = models.get('reviews'); 18 | 19 | const papers = await Paper.findAll(); 20 | 21 | for (let i = 0; i < papers.length; i++) { 22 | const paper = papers[i]; 23 | const author = (await paper.author()).name; 24 | console.log(`paper id ${paper.id} title ${paper.title} author ${author}`); 25 | } 26 | 27 | const users = await User.findAll(); 28 | users.forEach(user => { 29 | const id = user.id; 30 | const name = user.name; 31 | console.log(`their name is ${name} and id is ${id}`); 32 | }); 33 | 34 | // This exercises transitive foreign key traversals 35 | const review = (await Review.findAll())[0]; 36 | const { author, id, paper, score } = review; 37 | const opaper = await paper(); 38 | const oauthor = await author(); 39 | console.log(`the review had score ${score} and was written by ${oauthor.name}`); 40 | console.log(`the paper it was about was by ${(await opaper.author()).name}`); 41 | 42 | process.exit(0); 43 | } 44 | 45 | go(); 46 | -------------------------------------------------------------------------------- /magic_demo/src/orm.ts: -------------------------------------------------------------------------------- 1 | import { ParseJson } from './parse'; 2 | import format from 'pg-format'; 3 | 4 | import * as schema from '../../get_schema/src/schema'; 5 | import { 6 | Conn, DbClient, get_client, get_schema, 7 | DbSchema, TableSchema, TableSchemas, Column 8 | } from '../../get_schema/src/schema'; 9 | 10 | // Given connection `C`, calls the `get_schema` script to get the 11 | // schema of a postgresql database and returns a string literal type 12 | // containing it as json. 13 | type SchemaTxt = 14 | Shell<`node /tapeworm/get_schema/out/get_schema.js ${C['host']} ${C['port']} ${C['user']} ${C['db']}`>; 15 | 16 | // Given connection `C`, returns a structured type extending JsonOutput 17 | export type SchemaOf = ParseJson>; 18 | 19 | // Convert from a string describing a pg type to an appropriate typescript type 20 | type Inhab = 21 | K extends 'text' ? string : 22 | K extends 'integer' ? number : 23 | K; 24 | 25 | // A temporary stub type standing in for a table type so that we don't 26 | // get infinite recursion. 27 | type Stub = { stub: T }; 28 | 29 | // Given a single column type, return the type that should be the value 30 | // part of the row record for that column. 31 | type MakeColumn = Inhab; 32 | 33 | // Given a disjunction of columns, return the object type that is one row. 34 | type DisjunctToRow = 35 | { [K in T['column_name']]: MakeColumn> }; 36 | 37 | // Given a single table's schema, return the typescript type of one row of that table. 38 | type RowModel = DisjunctToRow; 39 | 40 | type AsyncThunk = () => Promise; 41 | 42 | type LookupStub = 43 | TABLE extends keyof TS ? AsyncThunk> : "couldn't find table reference"; 44 | 45 | type LookupStubs = 46 | { [K in keyof ROW]: ROW[K] extends schema.Stub ? 47 | LookupStub : ROW[K] }; 48 | 49 | // Given a single table's schema, return the typescript type of one row of that table, 50 | // with proxies for foreign keys 51 | type ProxiedRowModel = 52 | LookupStubs>; 53 | 54 | // Given a database schema (for recursive lookups), and single table's 55 | // schema, return the typescript type of one row of that table, with 56 | // proxies for foreign keys. 57 | type RowOfDb = 58 | ProxiedRowModel; 59 | 60 | // Given a single table's schema, return the typescript type of the utility class for that model 61 | interface TableModel { 62 | findAll: () => Promise[]> 63 | } 64 | 65 | // Given a database schema, return the typescript type of all utility classes for all models 66 | type TableModels = { [K in keyof S]: TableModel }; 67 | 68 | // Impedance matching for uncertainty about whether json parsing 69 | // actually produces something extending DbSchema 70 | type ModelsI = DB extends DbSchema ? Models : unknown; 71 | 72 | // Given a DbSchema, returns a class with getters for individual tables 73 | class Models { 74 | constructor(public client: DbClient, public schema: DB) { 75 | 76 | } 77 | 78 | get(tableName: K): TableModel { 79 | return new Model(this, tableName); 80 | } 81 | } 82 | 83 | function is_foreign(data_type: any): data_type is schema.ForeignTable { 84 | return typeof data_type === 'object' && 'target_table' in data_type; 85 | } 86 | 87 | // Implement the utility class for a model 88 | class Model implements TableModel { 89 | constructor(public models: Models, public name: string) { } 90 | 91 | proxyForeignKeys(row: RowModel): RowOfDb { 92 | return new Proxy(row, { 93 | get: (target, prop) => { 94 | const found = this.models.schema.table_schemas[this.name].cols 95 | .find(col => col.column_name === prop && is_foreign(col.data_type)); 96 | if (found && is_foreign(found.data_type)) { 97 | const dt = found.data_type; 98 | return async () => { 99 | const formatted = format( 100 | 'select * from %I tgt where tgt.%I = %L', 101 | dt.target_table, dt.target_col, (row as any)[found.column_name] 102 | ); 103 | const res = await this.models.client.query(formatted); 104 | return this.proxyForeignKeys(res.rows[0]); 105 | } 106 | } 107 | else { 108 | return (target as any)[prop]; 109 | } 110 | } 111 | }) as any; 112 | } 113 | 114 | async findAll(): Promise[]> { 115 | const res = await this.models.client.query(format('select * from %I', this.name)); 116 | return res.rows.map(row => this.proxyForeignKeys(row)) as any; 117 | } 118 | } 119 | 120 | export async function getModels(conn: C): Promise>> { 121 | const client = get_client(conn); 122 | return new Models(client, await get_schema(client)) as any; 123 | } 124 | 125 | const connection = { 126 | db: 'postgres', 127 | user: 'postgres', 128 | host: 'database', 129 | port: 5432 130 | } 131 | -------------------------------------------------------------------------------- /magic_demo/src/parse.ts: -------------------------------------------------------------------------------- 1 | // JSON Parser courtesy of https://twitter.com/buildsghost/status/1301976526603206657 2 | type ParserError = { error: true } & T 3 | type EatWhitespace = 4 | string extends State 5 | ? ParserError<"EatWhitespace got generic string type"> 6 | : State extends ` ${infer State}` | `\n${infer State}` 7 | ? EatWhitespace 8 | : State 9 | export type AddKeyValue, Key extends string, Value extends any, Optional extends true | false = false> = 10 | Memo & (Optional extends true ? { [K in Key] ?: Value } : { [K in Key]: Value }); 11 | type ParseJsonObject = {}> = 12 | string extends State 13 | ? ParserError<"ParseJsonObject got generic string type"> 14 | : EatWhitespace extends `}${infer State}` 15 | ? [Memo, State] 16 | : EatWhitespace extends `"${infer Key}"${infer State}` 17 | ? EatWhitespace extends `:${infer State}` 18 | ? ParseJsonValue extends [infer Value, `${infer State}`] 19 | ? EatWhitespace extends `,${infer State}` 20 | ? ParseJsonObject> 21 | : EatWhitespace extends `}${infer State}` 22 | ? [AddKeyValue, State] 23 | : ParserError<`ParseJsonObject received unexpected token: ${State}`> 24 | : ParserError<`ParseJsonValue returned unexpected value for: ${State}`> 25 | : ParserError<`ParseJsonObject received unexpected token: ${State}`> 26 | : ParserError<`ParseJsonObject received unexpected token: ${State}`> 27 | type ParseJsonArray = 28 | string extends State 29 | ? ParserError<"ParseJsonArray got generic string type"> 30 | : EatWhitespace extends `]${infer State}` 31 | ? [Memo, State] 32 | : ParseJsonValue extends [infer Value, `${infer State}`] 33 | ? EatWhitespace extends `,${infer State}` 34 | ? ParseJsonArray, [...Memo, Value]> 35 | : EatWhitespace extends `]${infer State}` 36 | ? [[...Memo, Value], State] 37 | : ParserError<`ParseJsonArray received unexpected token: ${State}`> 38 | : ParserError<`ParseJsonValue returned unexpected value for: ${State}`> 39 | type ParseJsonValue = 40 | string extends State 41 | ? ParserError<"ParseJsonValue got generic string type"> 42 | : EatWhitespace extends `null${infer State}` 43 | ? [null, State] 44 | : EatWhitespace extends `"${infer Value}"${infer State}` 45 | ? [Value, State] 46 | : EatWhitespace extends `[${infer State}` 47 | ? ParseJsonArray 48 | : EatWhitespace extends `{${infer State}` 49 | ? ParseJsonObject 50 | : ParserError<`ParseJsonValue received unexpected token: ${State}`> 51 | export type ParseJson = 52 | ParseJsonValue extends infer Result 53 | ? Result extends [infer Value, string] 54 | ? Value 55 | : Result extends ParserError 56 | ? Result 57 | : ParserError<"ParseJsonValue returned unexpected Result"> 58 | : ParserError<"ParseJsonValue returned uninferrable Result">; 59 | -------------------------------------------------------------------------------- /magic_demo/src/phone-a-friend-demo.ts: -------------------------------------------------------------------------------- 1 | import { PhoneAFriend } from './phone-a-friend'; 2 | 3 | // const x: PhoneAFriend<'What type is 42?'> = 42; 4 | -------------------------------------------------------------------------------- /magic_demo/src/phone-a-friend.ts: -------------------------------------------------------------------------------- 1 | type Interpret = Lowercase extends "number\n" ? number 2 | : Lowercase extends "string\n" ? string 3 | : Msg; 4 | 5 | export type PhoneAFriend = 6 | Interpret>>; 7 | -------------------------------------------------------------------------------- /magic_demo/src/z3-demo.ts: -------------------------------------------------------------------------------- 1 | import { Plus, LessEq, infer } from './z3'; 2 | 3 | function test_cases(x: LessEq<5>) { 4 | 5 | //// Error, because <= x (+ 2 3) is not the same as <= x 5! 6 | { const bad: LessEq> = x; } 7 | 8 | // However: 9 | { const good: LessEq> = infer(x); } 10 | 11 | //// Error, because not sound to infer <= (+ 2 2) from <= 5! 12 | { const bad: LessEq> = infer(x); } 13 | 14 | // <= 5 implies <= 6 15 | { const good: LessEq> = infer(x); } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /magic_demo/src/z3.ts: -------------------------------------------------------------------------------- 1 | // Strip trailing newline from a string literal type 2 | type StripNl = T extends `${infer S}\n` ? S : T; 3 | 4 | // Given a string type containing an sexp expressing a z3 program, 5 | // return 'sat' or 'unsat' 6 | type SolverResult = 7 | StripNl>; 8 | 9 | // A phantom type used to express constraints about integer values 10 | type Constr = { constr: T }; 11 | 12 | // An integer value so constrained 13 | type ConstrNum = number & Constr; 14 | 15 | // Generate a Z3 assertion for constraint T 16 | type GenAssert = T extends string ? `(${T})` : 'false'; 17 | 18 | // Generate Z3 code that checks whether T implies U. 19 | // Z3 will return 'unsat' if the implication *does* hold, 20 | // because T && !U will be false. 21 | type GenZ3 = ` 22 | (declare-const x Int) 23 | (assert ${GenAssert}) 24 | (assert (not ${GenAssert})) 25 | (check-sat) 26 | `; 27 | 28 | // If T => U, yields the appropriate result type for constraint U, otherwise unknown. 29 | type InferCond = SolverResult> extends 'unsat' ? ConstrNum : unknown; 30 | 31 | // Convert x from one constraint type to another 32 | export function infer(x: ConstrNum): InferCond { 33 | return x as any; 34 | } 35 | 36 | type strish = string | number; 37 | export type Plus = `(+ ${T} ${U})`; 38 | export type LessEq = ConstrNum<`<= x ${T}`>; 39 | -------------------------------------------------------------------------------- /magic_demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "out", 4 | "target": "es2017", 5 | "module": "commonjs", 6 | "lib": [ "es2017.object", "dom", "es6" ], 7 | "sourceMap": true, 8 | "noFallthroughCasesInSwitch": true, 9 | "noImplicitAny": true, 10 | "strictNullChecks": true, 11 | "strictPropertyInitialization": true, 12 | "esModuleInterop": true 13 | }, 14 | "include": ["src/**/*.ts"] 15 | } 16 | -------------------------------------------------------------------------------- /paper/oracle-types.bib: -------------------------------------------------------------------------------- 1 | @misc{lost, 2 | author = {Jeffrey Lieber and J. J. Abrams and Damon Lindelof}, 3 | address = {Oahu, Hawaii}, 4 | publisher = {ABC Studios}, 5 | title = {{\em Lost}}, 6 | year = {2004} 7 | } 8 | 9 | @misc{millionaire, 10 | author = {Regis Philbin and David Briggs and Steven Knight and Mike Whitehill and Michael Davies}, 11 | afilddress = {Oahu, Hawaii}, 12 | publisher = {Valleycrest Productions}, 13 | title = {{\em Who Wants to be a Millionaire?}}, 14 | year = {1999} 15 | } 16 | 17 | @InProceedings{z3, 18 | author="de Moura, Leonardo 19 | and Bj{\o}rner, Nikolaj", 20 | editor="Ramakrishnan, C. R. 21 | and Rehof, Jakob", 22 | title="Z3: An Efficient SMT Solver", 23 | booktitle="Tools and Algorithms for the Construction and Analysis of Systems", 24 | year="2008", 25 | publisher="Springer Berlin Heidelberg", 26 | address="Berlin, Heidelberg", 27 | pages="337--340", 28 | abstract="Satisfiability Modulo Theories (SMT) problem is a decision problem for logical first order formulas with respect to combinations of background theories such as: arithmetic, bit-vectors, arrays, and uninterpreted functions. Z3 is a new and efficient SMT Solver freely available from Microsoft Research. It is used in various software verification and analysis applications.", 29 | isbn="978-3-540-78800-3" 30 | } 31 | 32 | @misc{typescript-4.1, 33 | author = {Microsoft}, 34 | title = {TypeScript 4.1}, 35 | year = {2020}, 36 | publisher = {GitHub}, 37 | journal = {GitHub repository}, 38 | howpublished = {\url{https://github.com/microsoft/TypeScript}}, 39 | } 40 | 41 | @MISC{smtlib, 42 | author = {Clark Barrett and Pascal Fontaine and Cesare Tinelli}, 43 | title = {{The Satisfiability Modulo Theories Library (SMT-LIB)}}, 44 | howpublished = {{\tt www.SMT-LIB.org}}, 45 | year = 2016, 46 | } 47 | 48 | @misc{parse-json, 49 | author = {Jamie Kyle}, 50 | title = {{JSON} Parser in {TypeScript}}, 51 | year = {2020}, 52 | publisher = {GitHub}, 53 | journal = {GitHub repository}, 54 | howpublished = {\hfil\break{\tt https://github.com/jamiebuilds/json-parser\-in-typescript-very-bad-idea-please-dont-use}}, 55 | } 56 | -------------------------------------------------------------------------------- /paper/oracle-types.ltx: -------------------------------------------------------------------------------- 1 | \documentclass{article} 2 | 3 | \pagestyle{empty} % SIGBOVIK submission wants no page-numbers, 4 | % presumably so they can insert their own in the 5 | % proceedings pdf. 6 | 7 | \usepackage{url} 8 | \usepackage{lstlinebgrd} 9 | \usepackage{amssymb} 10 | \usepackage{listings} 11 | \usepackage{graphicx} 12 | \usepackage{float} 13 | 14 | \input{theorem} 15 | 16 | \input{prooftree} 17 | 18 | 19 | \bibliographystyle{alpha} 20 | \title{Oracle Types} 21 | \author{Akiva Leffert \and Jason Reed } 22 | \usepackage{xcolor} 23 | \definecolor{darkgreen}{rgb}{0,0.5,0} 24 | \usepackage{courier} 25 | \lstdefinelanguage{sql}{ 26 | keywords={CREATE, TABLE, PRIMARY, KEY, NOT, NULL, REFERENCES}, 27 | ndkeywords={int, text} 28 | } 29 | \lstdefinelanguage{ts}{ 30 | keywords={typeof,const, await, new, true, false, catch, function, async, return, null, catch, switch, var, if, in, while, do, else, case, break}, 31 | keywordstyle=\color{blue}\bfseries, 32 | ndkeywords={class, export, boolean, throw, implements, import, this, type}, 33 | ndkeywordstyle=\color{darkgreen}\bfseries, 34 | identifierstyle=\color{black}, 35 | sensitive=false, 36 | comment=[l]{//}, 37 | morecomment=[s]{/*}{*/}, 38 | commentstyle=\color{purple}\ttfamily, 39 | stringstyle=\color{red}\ttfamily, 40 | morestring=[b]', 41 | morestring=[b]", 42 | } 43 | 44 | \lstset{ % 45 | basicstyle=\tiny\ttfamily, % the size of the fonts that are used for the code 46 | breakatwhitespace=false, % sets if automatic breaks should only happen at whitespace 47 | breaklines=true, % sets automatic line breaking 48 | captionpos=b, % sets the caption-position to bottom 49 | deletekeywords={...}, % if you want to delete keywords from the given language 50 | escapeinside={\%*}{*)}, % if you want to add LaTeX within your code 51 | extendedchars=true, % lets you use non-ASCII characters; for 8-bits encodings only, does not work with UTF-8 52 | frame=tb, % adds a frame around the code 53 | keepspaces=true, % keeps spaces in text, useful for keeping indentation of code (possibly needs columns=flexible) 54 | language=ts, % the language of the code (can be overrided per snippet) 55 | otherkeywords={*,...}, % if you want to add more keywords to the set 56 | numbers=left, % where to put the line-numbers; possible values are (none, left, right) 57 | numbersep=5pt, % how far the line-numbers are from the code 58 | showspaces=false, % show spaces everywhere adding particular underscores; it overrides 'showstringspaces' 59 | showstringspaces=false, % underline spaces within strings only 60 | showtabs=false, % show tabs within strings adding particular underscores 61 | stepnumber=1, % the step between two line-numbers. If it's 1, each line will be numbered 62 | tabsize=2, % sets default tabsize to 2 spaces 63 | title=\lstname, % show the filename of files included with \lstinputlisting; also try caption instead of title 64 | columns=fixed % Using fixed column width (for e.g. nice alignment) 65 | } 66 | \def\x{\times} 67 | \def\to{\rightarrow} 68 | \begin{document} 69 | 70 | \maketitle 71 | \begin{abstract} 72 | We present Oracle Types, a new type-theoretic primitive for 73 | Typescript, which permits user-customizable extensions to the type 74 | language and type-checking algorithms. This enables several 75 | applications: arithmetic constraint checking, a dynamic live-update ORM, type-safe 76 | localization of multi-language data, and a rich, `mobile-first' 77 | interactive type-checking experience. 78 | \end{abstract} 79 | \section{Introduction} 80 | Although advanced type systems are commonly available, even for languages 81 | such as Javascript, the unquenchable thirst for new type-theoretic features 82 | exceeds the ability of even the most diligent implementors to keep up. But 83 | that hasn't stopped Typescript from trying. Pursuing their mission of typing 84 | even the most outlandish of Javascript programs, the Typescript team has 85 | taken the `yes and' approach to theoretical constructs to new heights, 86 | introducing concepts such as conditional types and string template types. 87 | 88 | Still, despite their best attempts, the designers of Typescript have failed to achieve 89 | apotheosis. 90 | 91 | What is needed, clearly, is {\em extensibility}, so that programmers 92 | can use whichever type theoretic features are most important to their 93 | domain, defying the limits of the system's creators. We employ a 94 | well-understood theoretical device to make things sort of work out in 95 | some arbitrary fashion despite the shackles of previously established 96 | premises (\cite{lost}) and defer to an {\em external oracle}. 97 | 98 | In order to do this in a principled way we introduce {\em Oracle 99 | Types} and proceed to demonstrate their utility by adding them into 100 | the Typescript compiler. 101 | 102 | In Section~\ref{constraint}, we show how oracle types can be used to 103 | attach annotations to numeric types which express arithmetic 104 | properties of them, which can be checked at compile time with 105 | constraint solvers. In Section~\ref{translation}, we use oracle types 106 | to automate localization of data structure fields. In 107 | Section~\ref{orm}, a dynamic ORM based on oracle types automatically 108 | ensures that the types of the primitives provided by the ORM reflect 109 | the current schema of the database, without requiring any explicit 110 | schema recompilation step. In Section~\ref{mobile}, we discuss an 111 | application that oracle types are uniquely suited for: using modern 112 | mobile technology, we can tap the type-theoretic expertise of 113 | individual human beings in real-time to contribute interactively to the 114 | type-checking process. 115 | 116 | Our implementation is available at 117 | {\center\url{https://github.com/bovik-labs/oracle-types}} 118 | \section{Examples} 119 | 120 | \subsection{Arithmetic Constraints} 121 | \label{constraint} 122 | 123 | An example of a {\em refinement type} of numbers is one that constrains a number to have a certain arithmetic property. 124 | For example, we can introduce the type 125 | 126 | \[\texttt{type LessEq} = \cdots\] 127 | 128 | so that, for example, {\tt LessEq<5>} is the type of all numbers that are less than or equal 5. 129 | Similarly, we have type operators such as 130 | 131 | \[\texttt{type Plus} = \cdots\] 132 | 133 | So that if $2$ and $3$ are understood as the singleton refinements consisting of only the number 2 (and 3, respectively), 134 | then naturally {\tt Plus<2,3>} is the singleton type consisting of only the number 5. Using Oracle Types, we can automate 135 | inference of semantically entailed subtyping relationships, by appealing to the constraint solver Z3 \cite{z3} 136 | to do the actual inference. For example, in the following code: 137 | 138 | \begin{lstlisting} 139 | import { Plus, LessEq, infer } from './z3'; 140 | 141 | function test_cases(x: LessEq<5>) { 142 | 143 | //// Error, because <= x (+ 2 3) is not the same as <= x 5! 144 | { const bad: LessEq> = x; } 145 | 146 | // However: 147 | { const good: LessEq> = infer(x); } 148 | 149 | //// Error, because not sound to infer <= (+ 2 2) from <= 5! 150 | { const bad: LessEq> = infer(x); } 151 | 152 | // <= 5 implies <= 6 153 | { const good: LessEq> = infer(x); } 154 | 155 | } 156 | \end{lstlisting} 157 | the {\tt infer} function allows subtyping from one {\tt LessEq} constraint to another, so long as the entailment 158 | is valid over the ordered monoid natural numbers under addition. The programmer must remember to insert enough 159 | {\tt infer} calls to mediate between syntactically non-identical constraint types, but due to Typescript's own type inference, 160 | the annotation burden is fairly mild: the type indexes on infer need not be specified in the example above, because they 161 | can be inferred from the return constraint type. 162 | \subsection{Dynamic Translantion} 163 | \label{translation} 164 | Ah, language, a true wonder of human ingenuity. And yet, most 165 | programming is done in English. APIs are typically designed in 166 | English. This hardly seems fair to the 94\% of the people of the world 167 | whose first language is not English. Good software is localized. 168 | Evidently programming languages are not good software. Even were one 169 | to localize a language itself, it would have to interact with English 170 | language names for libraries, API payloads, etc. 171 | 172 | Oracle types give us a solution. Consider a function 173 | \[\mathtt{Localize} : \mathtt{string} \x \mathtt{string} \times \tau \to \tau\] 174 | that would 175 | localize the fields of a record. For example, suppose we want to write 176 | a calendaring app, and we have a record with fields for each day of the week. 177 | 178 | \begin{lstlisting} 179 | type Schedule = { 180 | 'Sunday': string, 181 | 'Monday': string, 182 | 'Tuesday': string, 183 | 'Wednesday': string, 184 | 'Thursday': string, 185 | 'Friday': string, 186 | 'Saturday': string 187 | } 188 | \end{lstlisting} 189 | 190 | Look at those garbage English names! {\em ?`Y si quisi\'eramos 191 | programar en espa\~nol? } With the $\mathtt{Localized}$ type 192 | constructor, we can easily solve this problem. Simply wrap your type 193 | in it: 194 | 195 | \begin{lstlisting} 196 | type Calendario = Localized<'en', 'es', Schedule> 197 | 198 | // Equivalent to 199 | type Calendario = { 200 | 'Domingo': string, 201 | 'Lunes': string, 202 | 'Martes': string, 203 | 'Miercoles': string, 204 | 'Jueves': string, 205 | 'Viernes': string, 206 | 'Sabado': string 207 | } 208 | \end{lstlisting} 209 | 210 | \subsection{Dynamic ORM} 211 | \label{orm} 212 | 213 | An inconvenience of most ORM (Object-Relational Model) systems is that 214 | the user needs to explicitly represent the database schema in some way 215 | that the ORM can consume it, and present an API to the user that is 216 | type-correct with respect to that schema. With Oracle Types, we can 217 | avoid this explicit step, and instead consult the database itself at 218 | type-check time, and type the ORM API functions accordingly. 219 | 220 | Here is a small example to demonstrate its use. We imagine a database 221 | with three tables, {\tt users}, {\tt papers}, and {\tt reviews}, to 222 | model a set of research papers and reviews thereof. Its schema is the following: 223 | \begin{lstlisting}[language=sql] 224 | CREATE TABLE users (id int PRIMARY KEY NOT NULL, name text); 225 | CREATE TABLE papers (id int PRIMARY KEY NOT NULL, title text, author int REFERENCES users(id)); 226 | CREATE TABLE reviews ( 227 | id int PRIMARY KEY NOT NULL, 228 | score int, 229 | author int REFERENCES users(id), 230 | paper int REFERENCES papers(id) 231 | ); 232 | \end{lstlisting} 233 | 234 | Using this database we can write the following typescript code: 235 | \begin{lstlisting} 236 | import { getModels } from './orm'; 237 | 238 | const connection = { 239 | db: 'postgres', 240 | user: 'postgres', 241 | host: 'database', 242 | port: 5432 243 | } 244 | 245 | async function go() { 246 | const models = await getModels(connection); 247 | const User = models.get('users'); 248 | const Paper = models.get('papers'); 249 | const Review = models.get('reviews'); 250 | 251 | const papers = await Paper.findAll(); 252 | for (let i = 0; i < papers.length; i++) { 253 | const paper = papers[i]; 254 | const author = (await paper.author()).name; 255 | console.log(`paper id ${paper.id} title ${paper.title} author ${author}`); 256 | } 257 | 258 | const users = await User.findAll(); 259 | users.forEach(user => { 260 | const id = user.id; 261 | const name = user.name; 262 | console.log(`their name is ${name} and id is ${id}`); 263 | }); 264 | 265 | // Transitive foreign key traversals should work 266 | const review = (await Review.findAll())[0]; 267 | const { author, id, paper, score } = review; 268 | const opaper = await paper(); 269 | const oauthor = await author(); 270 | console.log(`the review had score ${score} and was written by ${oauthor.name}`); 271 | console.log(`the paper it was about was by ${(await opaper.author()).name}`); 272 | 273 | process.exit(0); 274 | } 275 | \end{lstlisting} 276 | 277 | On line 3, we set up the connection information required to connect to the database. 278 | Line 11 uses this information to obtain a single object that contains models 279 | for all tables of the database. Lines 12-14 get individual tables out of the models 280 | object --- enough type information is present that the programmer can autocomplete 281 | on the names of the tables upon entering the argument of the {\tt get} method. 282 | 283 | On line 19 we can see that we can get the author of a paper in a natural way, 284 | because the {\tt paper} model object obtained from line 16 has a method that 285 | is automatically populated from the foreign key relationship between the 286 | {\tt papers} table and the {\tt users} table. 287 | 288 | Lines 31-36 demonstrate that traversing multiple hops through the 289 | foreign key graph is scarcely more difficult than one hop. 290 | 291 | The principal advantage of this design is that if the database schema 292 | changes in such a way that the code is no longer semantically valid, 293 | that invalidity is immediately realized as a type error, without any 294 | intermediate step being required to regenerate the host-language 295 | representation of the schema. 296 | 297 | \subsection{Mobile-First Interactive Typechecking} 298 | \label{mobile} 299 | We take inspiration from other systems research work, in which a failure to answer a query by normal 300 | means can be remediated by a technique (pioneered in \cite{millionaire}) called `Phone-a-Friend'. 301 | 302 | Our realization of this protocol works as follows. The library we provide offers a type constructor 303 | {\tt PhoneAFriend}, which uses an external SMS API service to relay the query 304 | to the human with the given phone number. The human makes a response, which the SMS service sends 305 | to a previously configured HTTP endpoint on a webhook server running in the cloud. The typechecker makes an 306 | http request to the webhook server, waiting on a response, which is parsed into a type by our library. 307 | \begin{figure}[H] 308 | \centering 309 | \includegraphics[scale=0.5]{phone-a-friend} 310 | \caption{Interactive Type-Checking Network Architecture} 311 | \end{figure} 312 | 313 | \section{Implementation} 314 | The key implementation technique that enables all the above 315 | applications, which, to the best of the authors' knowledge, has 316 | somehow been overlooked by decades of programming language research, 317 | is allowing a type operator to have the ability, as a side-effect, to run an 318 | arbitrary shell command dervied from a type argument. 319 | 320 | \subsection{Modifying the Typescript Compiler} 321 | Typescript 4.1 \cite{typescript-4.1} added several new {\tt IntrinsicTypeKind} type operators which allow manipulation 322 | of types extending {\tt string}. For example, {\tt Uppercase<'foo' | 'bar'>} reduces to the type {\tt 'FOO' | 'BAR'}. 323 | By analogy with these types, we introduce {\tt Shell}, whose semantics is defined as follows: 324 | Any strings in the disjunctive expansion of {\tt T} are executed as shell subprocesses, and whatever they write 325 | to the {\tt stdout} file descriptor is collected and yielded as the result type. The implementation is quite simple; the 326 | crux of the change required is to extend the function {\tt applyStringMapping} in {\tt TypeScript/src/compiler/checker.ts} 327 | like so: 328 | \begin{lstlisting}[linebackgroundcolor={ \ifnum\value{lstnumber}>6\ifnum\value{lstnumber}<10 \color{green!25}\fi\fi 329 | }] 330 | function applyStringMapping(symbol: Symbol, str: string) { 331 | switch (intrinsicTypeKinds.get(symbol.escapedName as string)) { 332 | case IntrinsicTypeKind.Uppercase: return str.toUpperCase(); 333 | case IntrinsicTypeKind.Lowercase: return str.toLowerCase(); 334 | case IntrinsicTypeKind.Capitalize: return str.charAt(0).toUpperCase() + str.slice(1); 335 | case IntrinsicTypeKind.Uncapitalize: return str.charAt(0).toLowerCase() + str.slice(1); 336 | case IntrinsicTypeKind.Shell: { 337 | const exec = require('child_process').execSync; 338 | return exec(str).toString(); 339 | } 340 | } 341 | return str; 342 | } 343 | \end{lstlisting} 344 | The remaining changes are mere plumbing to ensure that {\tt IntrinsicTypeKind.Shell} is well-defined 345 | in the same way as {\tt Uppercase}, {\tt Lowercase}, etc. 346 | 347 | \subsection{Implementing Arithmetic Constraints} 348 | 349 | Given the type primitive {\tt Shell}, it is relatively straightforward to interface with Z3 350 | to provide constraint solving in the type system. We work throughout with template string literal 351 | types to manipulate sexpressions in SMT-LIB \cite{smtlib} format. 352 | 353 | \begin{lstlisting} 354 | // Strip trailing newline from a string literal type 355 | type StripNl = T extends `${infer S}\n` ? S : T; 356 | 357 | // Given a string type containing an sexp expressing a z3 program, 358 | // return 'sat' or 'unsat' 359 | type SolverResult = 360 | StripNl>; 361 | 362 | // A phantom type used to express constraints about integer values 363 | type Constr = { constr: T }; 364 | 365 | // An integer value so constrained 366 | type ConstrNum = number & Constr; 367 | 368 | // Generate a Z3 assertion for constraint T 369 | type GenAssert = T extends string ? `(${T})` : 'false'; 370 | 371 | // Generate Z3 code that checks whether T implies U. 372 | // Z3 will return 'unsat' if the implication *does* hold, 373 | // because T && !U will be false. 374 | type GenZ3 = ` 375 | (declare-const x Int) 376 | (assert ${GenAssert}) 377 | (assert (not ${GenAssert})) 378 | (check-sat) 379 | `; 380 | 381 | // If T => U, yields the appropriate result type for constraint U, otherwise unknown. 382 | type InferCond = SolverResult> extends 'unsat' ? ConstrNum : unknown; 383 | 384 | // Convert x from one constraint type to another 385 | export function infer(x: ConstrNum): InferCond { 386 | return x as any; 387 | } 388 | 389 | type strish = string | number; 390 | export type Plus = `(+ ${T} ${U})`; 391 | export type LessEq = ConstrNum<`<= x ${T}`>; 392 | \end{lstlisting} 393 | 394 | The type constructors {\tt Plus} and {\tt LessEq} (lines 37-38) build 395 | up sexpressions representing addition and the boolean less-than 396 | constraint, stringifying numerical constants as necessary. These can 397 | be used to build up instances of the type {\tt ConstrNum} (line 398 | 13), which represents the refinement of the type {\tt number} which 399 | must satisfy constraint {\tt T}. The type {\tt GenZ3} (line 21) 400 | converts two assertions $T$ and $U$ into a complete Z3 query which 401 | tries to determine the satisfiability of $T \land \lnot U$. This is the 402 | negation of the implication $ T \Rightarrow U$, so if the query returns an 403 | answer of {\bf un}satisfiable, we know the implication holds. 404 | for this reason, the type {\tt InferCond} (line 29) returns 405 | a {\tt ConstrNum} only if the {\tt GenZ3} query returns the string 406 | {\tt unsat}, and returns {\tt unknown} otherwise, inducing a type 407 | error, in the case that the attempted subtyping coercion fails. 408 | 409 | \subsection{Implementing Localized Types} 410 | To implement localization, we can simply shell out to a script that 411 | calls into the Google Translate API, or uses a local dictionary on the filesystem. 412 | 413 | \subsection{Implementing the Dynamic ORM} 414 | 415 | In order to implement the dynamic ORM, we first of all must have a way 416 | of getting the schema out of the database. Using the well-known 417 | open-source postgres database engine, this is not difficult: the 418 | schema (and foreign key relationships) are themselves stored in 419 | metadata tables, and are easily obtained with standard SQL queries. 420 | 421 | A standalone script named {\tt get\_schema.js} is implemented, which 422 | can be called by the Oracle Type invocation like so: 423 | \begin{lstlisting} 424 | // A type representing postgres connection information 425 | type Conn = { 426 | db: string, 427 | user: string, 428 | host: string, 429 | port: number 430 | }; 431 | 432 | // Given connection `C`, calls the `get_schema` script to get the 433 | // schema of a postgresql database and returns a string literal type 434 | // containing it as json. 435 | type SchemaTxt = 436 | Shell<`node get_schema.js ${C['host']} ${C['port']} ${C['user']} ${C['db']}`>; 437 | \end{lstlisting} 438 | Template literal types are used to interpolate the database connection 439 | information into the command-line arguments. The output of {\tt 440 | get\_schema.js} is a JSON string representing an object containing a 441 | description of the database schema, and so it must be parsed to obtain 442 | an actual object type. 443 | 444 | Fortunately, parsing JSON at the TypeScript type level is easily 445 | accomplished via well-understood and sound engineering practices 446 | \cite{parse-json}. From there, the implementation of our ORM is 447 | straightforward. The function {\tt getModels} uses the same 448 | schema-getting code (only now at run-time) to build a {\tt Models} object 449 | from the schema: 450 | \begin{lstlisting} 451 | export async function getModels(conn: C): Promise>> { 452 | const client = get_client(conn); 453 | return new Models(client, await get_schema(client)) as any; 454 | } 455 | \end{lstlisting} 456 | 457 | The {\tt Models} class so invoked simply constructs an instance of {\tt Model} for the appropriate table: 458 | \begin{lstlisting} 459 | // Given a DbSchema, returns a class with getters for individual tables 460 | class Models { 461 | constructor(public client: DbClient, public schema: DB) { } 462 | 463 | get(tableName: K): TableModel { 464 | return new Model(this, tableName); 465 | } 466 | } 467 | \end{lstlisting} 468 | We can see here specifically how IDE auto-completion of table names functions; {\tt DbSchema}'s field 469 | {\tt table\_schemas} is a map whose keys are table names, so the type of the only argument 470 | of {\tt get} becomes a disjunction type of all table names, and the TypeScript language server can 471 | communicate exactly this set to the programmer. 472 | 473 | Finally, the heart of the implementation is the {\tt Model} class: 474 | \begin{lstlisting} 475 | // Implement the utility class for a model 476 | class Model implements TableModel { 477 | constructor(public models: Models, public name: string) { } 478 | 479 | proxyForeignKeys(row: RowModel): RowOfDb { 480 | return new Proxy(row, { 481 | get: (target, prop) => { 482 | const found = this.models.schema.table_schemas[this.name].cols 483 | .find(col => col.column_name === prop && is_foreign(col.data_type)); 484 | if (found && is_foreign(found.data_type)) { 485 | const dt = found.data_type; 486 | return async () => { 487 | const formatted = format( 488 | 'select * from %I tgt where tgt.%I = %L', 489 | dt.target_table, dt.target_col, (row as any)[found.column_name] 490 | ); 491 | const res = await this.models.client.query(formatted); 492 | return this.proxyForeignKeys(res.rows[0]); 493 | } 494 | } 495 | else { 496 | return (target as any)[prop]; 497 | } 498 | } 499 | }) as any; 500 | } 501 | 502 | async findAll(): Promise[]> { 503 | const res = await this.models.client.query(format('select * from %I', this.name)); 504 | return res.rows.map(row => this.proxyForeignKeys(row)) as any; 505 | } 506 | } 507 | \end{lstlisting} 508 | The {\tt findAll} method actuall executes a query against the 509 | database, asking for all rows of a table. Instead of just returning 510 | the rows as they are, we modify them with the method {\tt 511 | proxyForeignKeys}. The effect of that is to patch field accesses to 512 | the row, using the {\tt Proxy} class, so that columns that are foreign 513 | keys become auto-populated method calls which perform further database 514 | queries to get the corresponding row of the foreign table. 515 | 516 | All of this proxying we have described takes place at run-time; a 517 | corresponding rewriting must also take place at type-checking time for 518 | the types to appear correct to the programmer. This takes place in the definition 519 | of the types that lead up to {\tt TableModel}: 520 | \begin{lstlisting} 521 | // Convert from a string describing a pg type to an appropriate typescript type 522 | type Inhab = 523 | K extends 'text' ? string : 524 | K extends 'integer' ? number : 525 | K; 526 | 527 | // Given a single column type, return the type that should be the value 528 | // part of the row record for that column. 529 | type MakeColumn = Inhab; 530 | 531 | // Given a disjunction of columns, return the object type that is one row. 532 | type DisjunctToRow = 533 | { [K in T['column_name']]: MakeColumn> }; 534 | 535 | // Given a single table's schema, return the typescript type of one row of that table. 536 | type RowModel = DisjunctToRow; 537 | 538 | type AsyncThunk = () => Promise; 539 | 540 | type LookupStub = 541 | TABLE extends keyof TS ? AsyncThunk> : "couldn't find table reference"; 542 | 543 | type LookupStubs = 544 | { [K in keyof ROW]: ROW[K] extends schema.Stub ? 545 | LookupStub : ROW[K] }; 546 | 547 | // Given a single table's schema, return the typescript type of one row of that table, 548 | // with proxies for foreign keys 549 | type ProxiedRowModel = 550 | LookupStubs>; 551 | 552 | // Given a database schema (for recursive lookups), and single table's 553 | // schema, return the typescript type of one row of that table, with 554 | // proxies for foreign keys. 555 | type RowOfDb = 556 | ProxiedRowModel; 557 | 558 | // Given a single table's schema, return the typescript type of the utility class for that model 559 | interface TableModel { 560 | findAll: () => Promise[]> 561 | } 562 | \end{lstlisting} 563 | Note the use in {\tt LookupStubs} of conditional types with inference, in that we can find 564 | the target table ({\tt infer TGT}) of a stubbed foreign key in the schema. 565 | 566 | \subsection{Implementing Mobile-First Interactive Typechecking} 567 | 568 | The client-server architecture that enables SMS-based interactive 569 | typechecking is fairly simple. The client looks something like this: 570 | 571 | \begin{lstlisting} 572 | import * as http from 'http'; 573 | import * as tiny from 'tiny-json-http'; 574 | import * as fs from 'fs'; 575 | 576 | const twilio = require('twilio'); 577 | 578 | const accountSid = ... ; 579 | const authToken = ... ; 580 | const client = twilio(accountSid, authToken); 581 | const server = ... ; 582 | const url = `http://${server}/listen`; 583 | const args = process.argv.slice(2); 584 | 585 | async function send_msg(msg: string): Promise { 586 | return client.messages 587 | .create({ 588 | from: ... , 589 | to: ... , 590 | body: msg, 591 | }); 592 | } 593 | 594 | async function go() { 595 | const msg = args[0]; 596 | if (msg !== undefined && msg !== "") { 597 | await send_msg(msg); 598 | } 599 | // receive response 600 | const res = await tiny.get({ url }); 601 | console.log(res.body.message); 602 | } 603 | 604 | go().catch(x => console.log(JSON.stringify({ error: x }))); 605 | \end{lstlisting} 606 | 607 | This script sends a message based on the commandline arguments, 608 | and makes a request to a {\tt /listen} callback, expecting to receive 609 | a message corresponding to the user-returned type. 610 | 611 | The server this client communicates with is build with the Twilio API: 612 | \begin{lstlisting} 613 | const fs = require('fs'); 614 | const accountSid = ... ; 615 | const authToken = ... ; 616 | const client = require('twilio')(accountSid, authToken); 617 | 618 | const http = require('http'); 619 | const express = require('express'); 620 | 621 | const MessagingResponse = require('twilio').twiml.MessagingResponse; 622 | 623 | const app = express(); 624 | app.use(express.urlencoded({extended: false})); 625 | 626 | // listener: undefined | (x : string) => void 627 | let listener = undefined; 628 | 629 | function notify(message) { 630 | if (listener !== undefined) { 631 | listener(message); 632 | listener = undefined; 633 | } 634 | } 635 | 636 | app.post('/sms', (req, res) => { 637 | console.log('got a message'); 638 | console.log(req.body); 639 | const message = req.body.Body; 640 | notify(message); 641 | res.writeHead(200, {'Content-Type': 'text/xml'}); 642 | res.end(''); 643 | }); 644 | 645 | app.get('/listen', (req, res) => { 646 | listener = (message) => { res.json({message}); } 647 | }); 648 | 649 | http.createServer(app).listen(1234, () => { 650 | console.log(Relay server listening on port 1234'); 651 | }); 652 | \end{lstlisting} 653 | It establishes a webhook callback at {\tt /sms} for Twilio to notify it 654 | that an inbound SMS message has arrived, and notifies a listener waiting 655 | for a response on {\tt /listen}. The service only supports a single user 656 | for the sake of a prototype, but we expect it would be a fruitful exercise 657 | to scale this server up to many concurrent users. 658 | 659 | To use the client script {\tt client.js} from a typescript program, one 660 | needs only write code such as 661 | \begin{lstlisting} 662 | type Interpret = Lowercase extends "number\n" ? number 663 | : Lowercase extends "string\n" ? string 664 | : Msg; 665 | 666 | export type PhoneAFriend = 667 | Interpret>>; 668 | 669 | const x: PhoneAFriend<'What type is 42?'> = 42; 670 | \end{lstlisting} 671 | 672 | When this code is typechecked, the user will be texted ``What type is 673 | 42?'' and they will have the opportunity to respond, and their 674 | response will be converted to the appropriate type. 675 | \begin{figure}[H] 676 | \centering 677 | \includegraphics[scale=0.15]{txt-message} 678 | \caption{Interactive SMS Type-Checking} 679 | \end{figure} 680 | 681 | \section{Conclusion} 682 | As our example applications show, opening the door to arbitrary shell 683 | computation at the type level leads to a variety of useful 684 | applications. Petty concerns about `determinacy' or `security' or `why 685 | am I being charged \$0.0075' or `where did all my files go, I just 686 | tried to type-check some sketchy code I found on the Dark Web' are 687 | clearly the purview of regressive, hide-bound curmudgeons who don't 688 | think programming should be fun. 689 | 690 | 691 | \bibliography{oracle-types} 692 | \end{document} 693 | -------------------------------------------------------------------------------- /paper/oracle-types.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bovik-labs/oracle-types/97fac25ecbfb0ab44c50d4db997288f88d65d422/paper/oracle-types.pdf -------------------------------------------------------------------------------- /paper/phone-a-friend.eps: -------------------------------------------------------------------------------- 1 | %!PS-Adobe-3.0 EPSF-3.0 2 | %%Creator: cairo 1.15.12 (http://cairographics.org) 3 | %%CreationDate: Sat Mar 13 10:58:55 2021 4 | %%Pages: 1 5 | %%DocumentData: Clean7Bit 6 | %%LanguageLevel: 2 7 | %%BoundingBox: 0 7 543 332 8 | %%EndComments 9 | %%BeginProlog 10 | 50 dict begin 11 | /q { gsave } bind def 12 | /Q { grestore } bind def 13 | /cm { 6 array astore concat } bind def 14 | /w { setlinewidth } bind def 15 | /J { setlinecap } bind def 16 | /j { setlinejoin } bind def 17 | /M { setmiterlimit } bind def 18 | /d { setdash } bind def 19 | /m { moveto } bind def 20 | /l { lineto } bind def 21 | /c { curveto } bind def 22 | /h { closepath } bind def 23 | /re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto 24 | 0 exch rlineto 0 rlineto closepath } bind def 25 | /S { stroke } bind def 26 | /f { fill } bind def 27 | /f* { eofill } bind def 28 | /n { newpath } bind def 29 | /W { clip } bind def 30 | /W* { eoclip } bind def 31 | /BT { } bind def 32 | /ET { } bind def 33 | /BDC { mark 3 1 roll /BDC pdfmark } bind def 34 | /EMC { mark /EMC pdfmark } bind def 35 | /cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def 36 | /Tj { show currentpoint cairo_store_point } bind def 37 | /TJ { 38 | { 39 | dup 40 | type /stringtype eq 41 | { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse 42 | } forall 43 | currentpoint cairo_store_point 44 | } bind def 45 | /cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore 46 | cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def 47 | /Tf { pop /cairo_font exch def /cairo_font_matrix where 48 | { pop cairo_selectfont } if } bind def 49 | /Td { matrix translate cairo_font_matrix matrix concatmatrix dup 50 | /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point 51 | /cairo_font where { pop cairo_selectfont } if } bind def 52 | /Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def 53 | cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def 54 | /g { setgray } bind def 55 | /rg { setrgbcolor } bind def 56 | /d1 { setcachedevice } bind def 57 | /cairo_data_source { 58 | CairoDataIndex CairoData length lt 59 | { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } 60 | { () } ifelse 61 | } def 62 | /cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def 63 | /cairo_image { image cairo_flush_ascii85_file } def 64 | /cairo_imagemask { imagemask cairo_flush_ascii85_file } def 65 | %%EndProlog 66 | %%BeginSetup 67 | %%BeginResource: font f-0-0 68 | %!FontType1-1.1 f-0-0 1.0 69 | 11 dict begin 70 | /FontName /f-0-0 def 71 | /PaintType 0 def 72 | /FontType 1 def 73 | /FontMatrix [0.001 0 0 0.001 0 0] readonly def 74 | /FontBBox {0 -217 762 741 } readonly def 75 | /Encoding 256 array 76 | 0 1 255 {1 index exch /.notdef put} for 77 | dup 32 /space put 78 | dup 74 /J put 79 | dup 78 /N put 80 | dup 79 /O put 81 | dup 83 /S put 82 | dup 84 /T put 83 | dup 99 /c put 84 | dup 101 /e put 85 | dup 104 /h put 86 | dup 107 /k put 87 | dup 109 /m put 88 | dup 110 /n put 89 | dup 111 /o put 90 | dup 112 /p put 91 | dup 113 /q put 92 | dup 114 /r put 93 | dup 115 /s put 94 | dup 116 /t put 95 | dup 117 /u put 96 | dup 121 /y put 97 | readonly def 98 | currentdict end 99 | currentfile eexec 100 | f983ef0097ece636fb4a96c74d26ab84185f6dfa4a16a7a1c27bbe3f1156aea698df336d20b467 101 | b10e7f33846656653c5ac6962759d3056cbdb3190bac614b984bf5a132dc418192443014ba63de 102 | 800d392b6fea026574bb2535fd7bb5338f35bf15a88ea328fdaa49670c7852e3d060f3c5d6b07f 103 | 2ef6d0f22646c5d18e19a2ae3ee120390f6dd96f76dcf1e127de5e9299077a00c17c0d71e36e5b 104 | 9d5ec58fceda57739a6a4214d4b79d6c48d2784b60c320323c7acddddf34db833cac0cf109f799 105 | 69d114a330d372e5c978a66acc84e3fe5557f6240856a013ffaa0199444e5c5036f775eba4a5c5 106 | 8cde66cf604b9aca2178431127b8a1ff7ed633a65c04600af5f573483112251caf33acb5858037 107 | 6f0aa57249d5c83bbc6da135bcb3ea11806cc490207d7ab1d63393db4a3a2d0808f69e186900d4 108 | bc843efd53fef9de6208081585cd9a31f08186db7f297a43ec5f3cedb463c0edbdea533b501162 109 | 11037a3a43559b3b9ee7bbcec8ba90d9c1d8ecebd0cf65502b7056fecc5a11e04634319fb46b40 110 | ab2d60c7e569a1af7b997f72d02625815ff887ba3be35f8e49defae1ef81b2f5f2368ed041ad38 111 | 602a8196d14e27176b18b3932bc88c7b3becc5da2cf426394848a7693b1d5539159da0b358f47d 112 | 91e9253bd8d89802932a1fa7e66bb2fecbcb6b57c762f815ed686fbe67b009d7ca00dd940c5c63 113 | 961b95a2ff373b9d621e90e7a0f109e79484193dee1943eb0fad6db6808b658ba67ad24f7a25be 114 | 80de8bd576ad5bc146b0a91f35e8a5b3665e3c0ad9463338bee20053bd99e479de11374317051a 115 | c0d34b967e2a1f72703e66f0a6aa2e1be8fc92198f002f2377e202e0b7e12c0e53befa143039dc 116 | 9ae5ea424d447304e26fd8637e8bc01a836007f0a061fee6af0b81d180bd8a70e194489b1fd064 117 | 2f36f83de74bdb939beb6961b7d8053ca27da1be87c9945b5c3773f66c2912d1b43f8223d6c8fe 118 | ed62fb81ea41ab09ec77713c7864f41050119e8c2f9864e565bafedf990a6c966631159a251672 119 | 9808fff5abd458ce4605ae04ecbfaf70e92c9f1875bd722ab953209e695e150e8186217e2eb7b8 120 | 0527ada84bed3026b135bd6a49812e96e06aa1422ac73bd1e3bf0e2d8920e5c5b2e1af78fdf6ea 121 | 96a29b4f443ba6b719e479a75741e70652534d814c1645a3b469d4a42620e8f8a37f53d10cf1f9 122 | e41a1194b4c9bd8274bd69b0393790f5ed17efc2b0b6ecd515ec314cae246b4c3a1c70a47cc4c3 123 | b8f1aea828173958bc9f842680bda479743d4fdeaae73eecf638c03497cfc1efd5668b0eb78368 124 | 34eeaceed5f9dc9e6dfd1d84880d191169535623357d0fc8afb3964be2d1d0e802842e0b8ee7c7 125 | 3193aa88f4497460e93c0b3fbe69b76ee6e3ba8bf96f5b9650792d4b00654c322f6168ba914517 126 | dcbbafbc9b22476c18950008e157b858252717cafa40d4e1d5518be6981784e0e8314d71ee46cf 127 | cf77f472105ead10ccb3ffd68828e7794c63a239bb87a58abccf4d28d829b4d858a2944a15e227 128 | 763e96f2c385b9f9572094d0599a91f3f12cf98d170a6db4311c791a1c1938c2420317285e37e3 129 | aee1e66933824007efa19a2244aa44bc2969f8fc83b7774c816e81e5eab538f97b5d887bb167be 130 | ec482b19a8acd91e2405926544fbfeb1c181828206e9e3b9029aa4c97a1685691262473599c79d 131 | 3cdecebf80bf2ca13070ab4c631c8ee22d61f6104faeebecbab55327c37c076a620799e779c9be 132 | 09db2521648f48cc413ede714c6da7277905206fb2cdca16beccfe6bc8af43b25fe1fe35dfc114 133 | 5ee04fbda5ee3ad5ec5be4297a3c2030d0eb7861f923f728b340460dba57323a40c57041ef7f6a 134 | f8949a2ca26b3a8ddea0515d53ba158dbeed61697dc36ea42efc791745c45531b0a71c5b26e391 135 | dbf8fb08bb5964b14229425e7df4a0ec19a97773f15addb24a26e55914938ee692fe457628a085 136 | 1c64ca773e0843c29974617e1eae8514940ce6160460a1c3ac19785493488a5b7b9e9952b0a6e1 137 | 9c294b275969406aa6a71554d461b2681aac16a76eb5525d9817594ba184ee702ced2b4e3e685e 138 | 89c2f15691c2be07a4db73776519178d2c51ef1171fa7ef4dfb4cd6a291243659636d60581ea76 139 | 055431089059d17e570f52c644a10abb7cbc3af96201fbecc94bb13c3549e427bb8ad2cad4de2a 140 | 0d808fd49d30177e985c01ac82f38eba5fb2a886b3b94ae61ef1ef2c4b3b2f3bf6665e03f9a604 141 | ae5450eff89f27679d4843cb717756f6192eab4e31222358108fb3fa2042c0af43920df41b40ad 142 | d15d56201c0386265c7fe410d6d3d8431684eab1bbc2f6438c4a77a2199ca1ebd759043d652346 143 | 535ac9b3b92ab343b6983cbc2d95cd4759c7578df005aa24b8618f19518e4f978bc9c9708bbb98 144 | c21c2685691d1ff75508bf9be05743b394e0838686f05433fb87932a162c16e29af4243c265e6d 145 | ca9518448d68575777dc54c5e93eba880e0b1b8ddd623eea954d917af4c3e7d2927c452c84444e 146 | cd1f8ca4575aeb3c9d00c1f7412d3aacc61094dc7b428cd087463d47aabd677d43ca0be314e393 147 | d5a553881ad7f0063c528a216c8f56771febc9d7aa4fb2826a6acab8e96e3edd05be74cc57e63a 148 | dc057d7c3ee6c54ecd7d2641de92afc45814a2e9f800d9e2ae57b24e48dc9f02ff054d03b4067a 149 | ae2b7e9a6065ae24e6f858b0049429f7e9b54a81b72822584a788f83e3e371c8cd5330a73d8f3f 150 | 9ace93666d15736c339c5bfbf3721a3a2568d1ec3f9076ef81b69095de45ac3a3432c148f44e29 151 | 01d494cf9c5f52eec271c52f43e1bfad66c916175279358dbb0c6bf5dc7a09c180cae029de5d26 152 | 150baff794aa502d47970a79d333a2ef0dd41d63e4aaf9efef65e5991d2402aac2146598547399 153 | 514cf0f122b1a99513f266841fdb49f36db8914a172edff69c178a21d160e45340cc6936931c50 154 | 5753c1d3d827c7e4648344b32259a6d9351d48527544e28f808a167dddb867de94e685cc9801cb 155 | 292749fd28c9d5e7bc448ea67520b33c951c310de1a9160935bf586d61de3a50e496d1baa1779a 156 | 3e6d1f44feb3c9eb6a500f07505aee8a5970848a7eff6198251a9ee32cd7d8e59ec577e9bac358 157 | b2f3677144e1bf234590de75b3c97078f5b25093116c4b108214aa05f90367e180a141702bf23c 158 | 70924f658cda081a69936c7039747ac66b238efc6b372c9209189f54ee130409c4444f0494a91c 159 | 00f11b9b682ba9178f7b6be54cc2e8af690e0f501a6f702ea86661247aa7697d8cbe9863a1df25 160 | f8c669538b1245a5eaaaeb4173ffcbd0610e982f060644379ed59b4c4c8f7d1d282f0589a4f640 161 | 81d571e408224187372ba1220236aa1d8391939e5eec19052de2edb42db62518c88ab50000000000000000000000000000000000000000000000000000000000000000 162 | 0000000000000000000000000000000000000000000000000000000000000000 163 | 0000000000000000000000000000000000000000000000000000000000000000 164 | 0000000000000000000000000000000000000000000000000000000000000000 165 | 0000000000000000000000000000000000000000000000000000000000000000 166 | 0000000000000000000000000000000000000000000000000000000000000000 167 | 0000000000000000000000000000000000000000000000000000000000000000 168 | 0000000000000000000000000000000000000000000000000000000000000000 169 | cleartomark 170 | %%EndResource 171 | %%BeginResource: font DejaVuSans 172 | 11 dict begin 173 | /FontType 42 def 174 | /FontName /DejaVuSans def 175 | /PaintType 0 def 176 | /FontMatrix [ 1 0 0 1 0 0 ] def 177 | /FontBBox [ 0 0 0 0 ] def 178 | /Encoding 256 array def 179 | 0 1 255 { Encoding exch /.notdef put } for 180 | Encoding 32 /space put 181 | Encoding 45 /hyphen put 182 | Encoding 65 /A put 183 | Encoding 67 /C put 184 | Encoding 72 /H put 185 | Encoding 73 /I put 186 | Encoding 77 /M put 187 | Encoding 80 /P put 188 | Encoding 83 /S put 189 | Encoding 87 /W put 190 | Encoding 97 /a put 191 | Encoding 98 /b put 192 | Encoding 99 /c put 193 | Encoding 100 /d put 194 | Encoding 101 /e put 195 | Encoding 103 /g put 196 | Encoding 104 /h put 197 | Encoding 105 /i put 198 | Encoding 107 /k put 199 | Encoding 108 /l put 200 | Encoding 109 /m put 201 | Encoding 110 /n put 202 | Encoding 111 /o put 203 | Encoding 112 /p put 204 | Encoding 113 /q put 205 | Encoding 114 /r put 206 | Encoding 115 /s put 207 | Encoding 116 /t put 208 | Encoding 117 /u put 209 | Encoding 118 /v put 210 | Encoding 120 /x put 211 | Encoding 121 /y put 212 | /CharStrings 33 dict dup begin 213 | /.notdef 0 def 214 | /C 1 def 215 | /l 2 def 216 | /o 3 def 217 | /u 4 def 218 | /d 5 def 219 | /space 6 def 220 | /H 7 def 221 | /s 8 def 222 | /t 9 def 223 | /i 10 def 224 | /n 11 def 225 | /g 12 def 226 | /S 13 def 227 | /e 14 def 228 | /r 15 def 229 | /v 16 def 230 | /c 17 def 231 | /W 18 def 232 | /b 19 def 233 | /h 20 def 234 | /k 21 def 235 | /M 22 def 236 | /A 23 def 237 | /P 24 def 238 | /I 25 def 239 | /m 26 def 240 | /a 27 def 241 | /hyphen 28 def 242 | /p 29 def 243 | /x 30 def 244 | /q 31 def 245 | /y 32 def 246 | end readonly def 247 | /sfnts [ 248 | <0001000000090080000300106376742000691d390000185c000001fe6670676d7134766a0000 249 | 1a5c000000ab676c796636371dd20000009c000017c068656164085da0a700001b0800000036 250 | 686865610d9f078e00001b4000000024686d74789bc410fe00001b64000000846c6f63610001 251 | 5d6400001be8000000886d617870048e067100001c7000000020707265703b07f10000001c90 252 | 0000056800020066fe96046605a400030007001a400c04fb0006fb0108057f0204002fc4d4ec 253 | 310010d4ecd4ec301311211125211121660400fc73031bfce5fe96070ef8f272062900010073 254 | ffe3052705f000190036401a0da10eae0a951101a100ae04951791118c1a07190d003014101a 255 | 10fcec32ec310010e4f4ecf4ec10eef6ee30b40f1b1f1b02015d01152e012320001110002132 256 | 3637150e01232000111000213216052766e782ff00fef00110010082e7666aed84feadfe7a01 257 | 86015386ed0562d55f5efec7fed8fed9fec75e5fd34848019f01670168019f470000000100c1 258 | 00000179061400030022b7009702010800460410fcec31002fec30400d100540055005600570 259 | 05f00506015d13331123c1b8b80614f9ec0000020071ffe30475047b000b0017004a401306b9 260 | 1200b90cb8128c1809120f51031215451810fcecf4ec310010e4f4ec10ee3040233f197b007b 261 | 067f077f087f097f0a7f0b7b0c7f0d7f0e7f0f7f107f117b12a019f01911015d012206151416 262 | 333236353426273200111000232200111000027394acab9593acac93f00112feeef0f1feef01 263 | 1103dfe7c9c9e7e8c8c7e99cfec8feecfeedfec701390113011401380000000200aeffe30458 264 | 047b00130014003b401c030900030e0106870e118c0a01bc14b80c0d0908140b4e0208004615 265 | 10fcecf439ec3231002fe4e432f4c4ec1112173930b46f15c01502015d131133111416333236 266 | 3511331123350e0123222601aeb87c7c95adb8b843b175c1c801cf01ba02a6fd619f9fbea402 267 | 7bfba0ac6663f003a80000020071ffe3045a06140010001c003840191ab9000e14b905088c0e 268 | b801970317040008024711120b451d10fcecf4ec323231002fece4f4c4ec10c4ee30b6601e80 269 | 1ea01e03015d0111331123350e0123220211100033321601141633323635342623220603a2b8 270 | b83ab17ccbff00ffcb7cb1fdc7a79292a8a89292a703b6025ef9eca864610144010801080144 271 | 61fe15cbe7e7cbcbe7e7000100c90000053b05d5000b002c4014089502ad0400810a0607031c 272 | 053809011c00040c10fcec32fcec3231002f3ce432fcec30b2500d01015d1333112111331123 273 | 11211123c9ca02decacafd22ca05d5fd9c0264fa2b02c7fd39000001006fffe303c7047b0027 274 | 00e7403c0d0c020e0b531f1e080902070a531f1f1e420a0b1e1f041500860189041486158918 275 | b91104b925b8118c281e0a0b1f1b0700521b080e07081422452810fcc4ecd4ece41112393939 276 | 39310010e4f4ec10fef5ee10f5ee121739304b535807100eed111739070eed1117395922b200 277 | 2701015d406d1c0a1c0b1c0c2e092c0a2c0b2c0c3b093b0a3b0b3b0c0b200020012402280a28 278 | 0b2a132f142f152a16281e281f292029212427860a860b860c860d12000000010202060a060b 279 | 030c030d030e030f03100319031a031b031c041d09272f293f295f297f2980299029a029f029 280 | 185d005d7101152e012322061514161f011e0115140623222627351e013332363534262f012e 281 | 01353436333216038b4ea85a898962943fc4a5f7d85ac36c66c661828c65ab40ab98e0ce66b4 282 | 043fae282854544049210e2a99899cb62323be353559514b50250f2495829eac1e0000000001 283 | 0037000002f2059e0013003840190e05080f03a9001101bc08870a0b08090204000810120e46 284 | 1410fc3cc4fc3cc432393931002fecf43cc4ec3211393930b2af1501015d0111211521111416 285 | 3b01152322263511233533110177017bfe854b73bdbdd5a28787059efec28ffda0894e9a9fd2 286 | 02608f013e000000000200c100000179061400030007002b400e06be04b100bc020501080400 287 | 460810fc3cec3231002fe4fcec30400b1009400950096009700905015d1333112311331523c1 288 | b8b8b8b80460fba00614e900000100ba00000464047b001300364019030900030e0106870e11 289 | b80cbc0a010208004e0d09080b461410fcec32f4ec31002f3ce4f4c4ec1112173930b46015cf 290 | 1502015d0111231134262322061511231133153e013332160464b87c7c95acb9b942b375c1c6 291 | 02a4fd5c029e9f9ebea4fd870460ae6564ef00020071fe56045a047b000b0028004a4023190c 292 | 1d0912861316b90f03b92623b827bc09b90fbd1a1d261900080c4706121220452910fcc4ecf4 293 | ec323231002fc4e4ece4f4c4ec10fed5ee1112393930b6602a802aa02a03015d013426232206 294 | 15141633323617100221222627351e013332363d010e0123220211101233321617353303a2a5 295 | 9594a5a59495a5b8fefefa61ac51519e52b5b439b27ccefcfcce7cb239b8023dc8dcdcc8c7dc 296 | dcebfee2fee91d1eb32c2abdbf5b6362013a01030104013a6263aa0000010087ffe304a205f0 297 | 0027007e403c0d0c020e0b021e1f1e080902070a021f1f1e420a0b1e1f0415010015a1149418 298 | 9511049500942591118c281e0a0b1f1b0700221b190e2d071914222810dcc4ecfcece4111239 299 | 393939310010e4f4e4ec10eef6ee10c6111739304b535807100eed11173907100eed11173959 300 | 22b20f2901015db61f292f294f29035d01152e012322061514161f011e011514042122262735 301 | 1e013332363534262f012e01353424333216044873cc5fa5b377a67ae2d7feddfee76aef807b 302 | ec72adbc879a7be2ca0117f569da05a4c53736807663651f192bd9b6d9e0302fd04546887e6e 303 | 7c1f182dc0abc6e4260000020071ffe3047f047b0014001b00704024001501098608880515a9 304 | 0105b90c01bb18b912b80c8c1c1b1502081508004b02120f451c10fcecf4ecc4111239310010 305 | e4f4ece410ee10ee10f4ee1112393040293f1d701da01dd01df01d053f003f013f023f153f1b 306 | 052c072f082f092c0a6f006f016f026f156f1b095d71015d0115211e0133323637150e012320 307 | 00111000333200072e0123220607047ffcb20ccdb76ac76263d06bfef4fec70129fce20107b8 308 | 02a5889ab90e025e5abec73434ae2a2c0138010a01130143feddc497b4ae9e00000100ba0000 309 | 034a047b001100304014060b0700110b03870eb809bc070a06080008461210fcc4ec3231002f 310 | e4f4ecc4d4cc11123930b450139f1302015d012e012322061511231133153e0133321617034a 311 | 1f492c9ca7b9b93aba85132e1c03b41211cbbefdb20460ae6663050500000001003d0000047f 312 | 0460000600fb402703110405040211010205050402110302060006011100000642020300bf05 313 | 06050302010504000710d44bb00a5458b90000004038594bb014544bb015545b58b90000ffc0 314 | 3859c4173931002fec3239304b5358071005ed071008ed071008ed071005ed592201408e4802 315 | 6a027b027f02860280029102a402080600060109030904150015011a031a0426002601290329 316 | 042008350035013a033a04300846004601490349044605480640085600560159035904500866 317 | 00660169036904670568066008750074017b037b0475057a0685008501890389048905860696 318 | 00960197029a03980498059706a805a706b008c008df08ff083e5d005d133309013301233dc3 319 | 015e015ec3fe5cfa0460fc5403acfba0000000010071ffe303e7047b0019003f401b00860188 320 | 040e860d880ab91104b917b8118c1a07120d004814451a10fce432ec310010e4f4ec10fef4ee 321 | 10f5ee30400b0f1b101b801b901ba01b05015d01152e0123220615141633323637150e012322 322 | 0011100021321603e74e9d50b3c6c6b3509d4e4da55dfdfed6012d010655a20435ac2b2be3cd 323 | cde32b2baa2424013e010e0112013a23000000010044000007a605d5000c017b4049051a0605 324 | 090a09041a0a09031a0a0b0a021a01020b0b0a061107080705110405080807021103020c000c 325 | 011100000c420a050203060300af0b080c0b0a09080605040302010b07000d10d4cc17393100 326 | 2f3cec32321739304b5358071005ed071008ed071008ed071005ed071008ed071005ed0705ed 327 | 071008ed5922b2000e01015d40f206020605020a000a000a120a2805240a200a3e023e05340a 328 | 300a4c024d05420a400a59026a026b05670a600a7b027f027c057f05800a960295051d070009 329 | 020803000406050005000601070408000807090009040a0a0c000e1a0315041508190c100e20 330 | 0421052006200720082309240a250b200e200e3c023a033504330530083609390b3f0c300e46 331 | 0046014a0240044505400542064207420840084009440a4d0c400e400e58025608590c500e66 332 | 026703610462056006600760086409640a640b770076017b0278037704740579067907770870 333 | 08780c7f0c7f0e860287038804890585098a0b8f0e97049f0eaf0e5b5d005d13330901330901 334 | 33012309012344cc013a0139e3013a0139cdfe89fefec5fec2fe05d5fb1204eefb1204eefa2b 335 | 0510faf00000000200baffe304a40614000b001c0038401903b90c0f09b918158c0fb81b9719 336 | 00121247180c06081a461d10fcec3232f4ec31002fece4f4c4ec10c6ee30b6601e801ea01e03 337 | 015d013426232206151416333236013e01333200111002232226271523113303e5a79292a7a7 338 | 9292a7fd8e3ab17bcc00ffffcc7bb13ab9b9022fcbe7e7cbcbe7e702526461febcfef8fef8fe 339 | bc6164a80614000100ba000004640614001300344019030900030e0106870e11b80c970a0102 340 | 08004e0d09080b461410fcec32f4ec31002f3cecf4c4ec1112173930b2601501015d01112311 341 | 34262322061511231133113e013332160464b87c7c95acb9b942b375c1c602a4fd5c029e9f9e 342 | bea4fd870614fd9e6564ef00000100ba0000049c0614000a00bc402908110506050711060605 343 | 03110405040211050504420805020303bc009709060501040608010800460b10fcec32d4c411 344 | 3931002f3cece41739304b5358071004ed071005ed071005ed071004ed5922b2100c01015d40 345 | 5f04020a081602270229052b0856026602670873027705820289058e08930296059708a30212 346 | 09050906020b030a072803270428052b062b07400c6803600c8903850489058d068f079a0397 347 | 07aa03a705b607c507d607f703f003f704f0041a5d71005d1333110133090123011123bab902 348 | 25ebfdae026bf0fdc7b90614fc6901e3fdf4fdac0223fddd000100c90000061f05d5000c00bf 349 | 403403110708070211010208080702110302090a0901110a0a09420a070203080300af080b05 350 | 0908030201050a061c043e0a1c00040d10fcecfcec11173931002f3cc4ec32111739304b5358 351 | 071005ed071008ed071008ed071005ed5922b2700e01015d405603070f080f09020a15021407 352 | 130a260226072007260a200a3407350a69027c027b07790a80028207820a90021604010b0313 353 | 011b0323012c032708280934013c035608590965086a097608790981018d0395019b03145d00 354 | 5d13210901211123110123011123c9012d017d017f012dc5fe7fcbfe7fc405d5fc0803f8fa2b 355 | 051ffc000400fae10000000200100000056805d50002000a00c2404100110100040504021105 356 | 050401110a030a0011020003030a0711050406110505040911030a08110a030a420003079501 357 | 0381090509080706040302010009050a0b10d4c4173931002f3ce4d4ec1239304b5358071005 358 | ed0705ed071005ed0705ed071008ed071005ed071005ed071008ed5922b2200c01015d40420f 359 | 010f020f070f080f005800760070008c000907010802060309041601190256015802500c6701 360 | 6802780176027c0372047707780887018802800c980299039604175d005d0901210133012303 361 | 21032302bcfeee0225fe7be50239d288fd5f88d5050efd1903aefa2b017ffe810000000200c9 362 | 0000048d05d500080013003a40180195100095098112100a0802040005190d3f11001c090414 363 | 10fcec32fcec11173931002ff4ecd4ec30400b0f151f153f155f15af1505015d011133323635 364 | 342623252132041514042b0111230193fe8d9a9a8dfe3801c8fb0101fefffbfeca052ffdcf92 365 | 878692a6e3dbdde2fda8000100c90000019305d50003002eb700af02011c00040410fc4bb010 366 | 5458b9000000403859ec31002fec3001400d30054005500560058f059f05065d13331123c9ca 367 | ca05d5fa2b00000100ba0000071d047b0022005a4026061209180f00061d07150c871d2003b8 368 | 1bbc19100700110f0808065011080f501c18081a462310fcec32fcfcfcec11123931002f3c3c 369 | e4f43cc4ec32111217393040133024502470249024a024a024bf24df24ff2409015d013e0133 370 | 32161511231134262322061511231134262322061511231133153e01333216042945c082afbe 371 | b972758fa6b972778da6b9b93fb0797aab03897c76f5e2fd5c029ea19cbea4fd87029ea29bbf 372 | a3fd870460ae67627c0000000002007bffe3042d047b000a002500bc4027191f0b17090e00a9 373 | 1706b90e1120861fba1cb923b8118c170c001703180d09080b1f030814452610fcecccd4ec32 374 | 3211393931002fc4e4f4fcf4ec10c6ee10ee11391139123930406e301d301e301f3020302130 375 | 223f27401d401e401f402040214022501d501e501f50205021502250277027851d871e871f87 376 | 20872185229027a027f0271e301e301f30203021401e401f40204021501e501f50205021601e 377 | 601f60206021701e701f70207021801e801f80208021185d015d0122061514163332363d0137 378 | 1123350e01232226353436332135342623220607353e0133321602bedfac816f99b9b8b83fbc 379 | 88accbfdfb0102a79760b65465be5af3f00233667b6273d9b4294cfd81aa6661c1a2bdc0127f 380 | 8b2e2eaa2727fc000001006401df027f028300030011b6009c020401000410dccc310010d4ec 381 | 301321152164021bfde50283a400000200bafe5604a4047b0010001c003e401b1ab9000e14b9 382 | 0508b80e8c01bd03bc1d11120b471704000802461d10fcec3232f4ec310010e4e4e4f4c4ec10 383 | c4ee304009601e801ea01ee01e04015d2511231133153e013332001110022322260134262322 384 | 061514163332360173b9b93ab17bcc00ffffcc7bb10238a79292a7a79292a7a8fdae060aaa64 385 | 61febcfef8fef8febc6101ebcbe7e7cbcbe7e70000000001003b000004790460000b01434046 386 | 0511060706041103040707060411050401020103110202010b110001000a11090a0101000a11 387 | 0b0a0708070911080807420a070401040800bf05020a0704010408000208060c10d44bb00a54 388 | 4bb00f545b4bb010545b4bb011545b58b90006004038594bb0145458b90006ffc03859c4d4c4 389 | 11173931002f3cec321739304b5358071005ed071008ed071008ed071005ed071005ed071008 390 | ed071008ed071005ed59220140980a04040a1a04150a260a3d04310a55045707580a660a7601 391 | 7a047607740a8d04820a99049f049707920a900aa601a904af04a507a30aa00a1c0a03040505 392 | 090a0b1a03150515091a0b2903260525092a0b200d3a013903370534073609390b300d490346 393 | 0545094a0b400d590056015902590357055606590756085609590b500d6f0d78017f0d9b0194 394 | 07ab01a407b00dcf0ddf0dff0d2f5d005d09022309012309013309010464fe6b01aad9febafe 395 | bad901b3fe72d9012901290460fddffdc101b8fe48024a0216fe71018f0000020071fe56045a 396 | 047b000b001c003e401b03b90c0f09b91815b80f8c1bbd19bc1d180c06081a47001212451d10 397 | fcecf4ec3232310010e4e4e4f4c4ec10c6ee304009601e801ea01ee01e04015d011416333236 398 | 353426232206010e012322021110003332161735331123012fa79292a8a89292a702733ab17c 399 | cbff00ffcb7cb13ab8b8022fcbe7e7cbcbe7e7fdae646101440108010801446164aaf9f60000 400 | 0001003dfe56047f0460000f018b40430708020911000f0a110b0a00000f0e110f000f0d110c 401 | 0d00000f0d110e0d0a0b0a0c110b0b0a420d0b0910000b058703bd0e0bbc100e0d0c0a090603 402 | 00080f040f0b1010d44bb00a544bb008545b58b9000b004038594bb0145458b9000bffc03859 403 | c4c4111739310010e432f4ec113911391239304b5358071005ed071008ed071008ed071005ed 404 | 071008ed0705ed173259220140f0060005080609030d160a170d100d230d350d490a4f0a4e0d 405 | 5a095a0a6a0a870d800d930d120a000a09060b050c0b0e0b0f1701150210041005170a140b14 406 | 0c1a0e1a0f2700240124022004200529082809250a240b240c270d2a0e2a0f20113700350135 407 | 0230043005380a360b360c380d390e390f301141004001400240034004400540064007400842 408 | 09450a470d490e490f40115400510151025503500450055606550756085709570a550b550c59 409 | 0e590f501166016602680a690e690f60117b08780e780f89008a09850b850c890d890e890f99 410 | 09950b950c9a0e9a0fa40ba40cab0eab0fb011cf11df11ff11655d005d050e012b0135333236 411 | 3f01013309013302934e947c936c4c543321fe3bc3015e015ec368c87a9a488654044efc9403 412 | 6c000000013500b800cb00cb00c100aa009c01a600b800660000007100cb00a002b200850075 413 | 00b800c301cb0189022d00cb00a600f000d300aa008700cb03aa0400014a003300cb000000d9 414 | 050200f4015400b4009c01390114013907060400044e04b4045204b804e704cd0037047304cd 415 | 04600473013303a2055605a60556053903c5021200c9001f00b801df007300ba03e9033303bc 416 | 0444040e00df03cd03aa00e503aa0404000000cb008f00a4007b00b80014016f007f027b0252 417 | 008f00c705cd009a009a006f00cb00cd019e01d300f000ba018300d5009803040248009e01d5 418 | 00c100cb00f600830354027f00000333026600d300c700a400cd008f009a0073040005d5010a 419 | 00fe022b00a400b4009c00000062009c0000001d032d05d505d505d505f0007f007b005400a4 420 | 06b80614072301d300b800cb00a601c301ec069300a000d3035c037103db0185042304a80448 421 | 008f0139011401390360008f05d5019a0614072306660179046004600460047b009c00000277 422 | 046001aa00e904600762007b00c5007f027b000000b4025205cd006600bc00660077061000cd 423 | 013b01850389008f007b0000001d00cd074a042f009c009c0000077d006f0000006f0335006a 424 | 006f007b00ae00b2002d0396008f027b00f600830354063705f6008f009c04e10266008f018d 425 | 02f600cd03440029006604ee00730000140000960000b707060504030201002c2010b0022549 426 | 64b040515820c859212d2cb002254964b040515820c859212d2c20100720b00050b00d7920b8 427 | ffff5058041b0559b0051cb0032508b0042523e120b00050b00d7920b8ffff5058041b0559b0 428 | 051cb0032508e12d2c4b505820b0fd454459212d2cb002254560442d2c4b5358b00225b00225 429 | 45445921212d2c45442d2cb00225b0022549b00525b005254960b0206368208a108a233a8a10 430 | 653a2d000001000000025999ca7763805f0f3cf5001f080000000000d17dfdf400000000d17d 431 | fdf4f7d6fc4c0e5909dc00000008000000010000000000010000076dfe1d00000efef7d6fa51 432 | 0e5900010000000000000000000000000000002104cd006605960073023900c104e500710512 433 | 00ae05140071028b0000060400c9042b006f03230037023900c1051200ba0514007105140087 434 | 04ec0071034a00ba04bc003d0466007107e90044051400ba051200ba04a200ba06e700c90579 435 | 001004d300c9025c00c907cb00ba04e7007b02e30064051400ba04bc003b0514007104bc003d 436 | 0000000000000044000000dc00000118000001bc00000240000002d8000002d8000003340000 437 | 04940000051000000560000005d8000006a0000007980000086c000008dc00000a0000000a98 438 | 00000c5400000cec00000d6400000e5400000f500000104c000010cc00001114000011d80000 439 | 130400001330000013d000001554000015f4000017c00001000000210354002b0068000c0002 440 | 00100099000800000415021600080004b8028040fffbfe03fa1403f92503f83203f79603f60e 441 | 03f5fe03f4fe03f32503f20e03f19603f02503ef8a4105effe03ee9603ed9603ecfa03ebfa03 442 | eafe03e93a03e84203e7fe03e63203e5e45305e59603e48a4105e45303e3e22f05e3fa03e22f 443 | 03e1fe03e0fe03df3203de1403dd9603dcfe03db1203da7d03d9bb03d8fe03d68a4105d67d03 444 | d5d44705d57d03d44703d3d21b05d3fe03d21b03d1fe03d0fe03cffe03cefe03cd9603cccb1e 445 | 05ccfe03cb1e03ca3203c9fe03c6851105c61c03c51603c4fe03c3fe03c2fe03c1fe03c0fe03 446 | bffe03befe03bdfe03bcfe03bbfe03ba1103b9862505b9fe03b8b7bb05b8fe03b7b65d05b7bb 447 | 03b78004b6b52505b65d40ff03b64004b52503b4fe03b39603b2fe03b1fe03b0fe03affe03ae 448 | 6403ad0e03acab2505ac6403abaa1205ab2503aa1203a98a4105a9fa03a8fe03a7fe03a6fe03 449 | a51203a4fe03a3a20e05a33203a20e03a16403a08a4105a096039ffe039e9d0c059efe039d0c 450 | 039c9b19059c64039b9a10059b19039a1003990a0398fe0397960d0597fe03960d03958a4105 451 | 95960394930e05942803930e0392fa039190bb0591fe03908f5d0590bb039080048f8e25058f 452 | 5d038f40048e25038dfe038c8b2e058cfe038b2e038a8625058a410389880b05891403880b03 453 | 878625058764038685110586250385110384fe038382110583fe0382110381fe0380fe037ffe 454 | 0340ff7e7d7d057efe037d7d037c64037b5415057b25037afe0379fe03780e03770c03760a03 455 | 75fe0374fa0373fa0372fa0371fa0370fe036ffe036efe036c21036bfe036a1142056a530369 456 | fe03687d036711420566fe0365fe0364fe0363fe0362fe03613a0360fa035e0c035dfe035bfe 457 | 035afe0359580a0559fa03580a035716190557320356fe035554150555420354150353011005 458 | 531803521403514a130551fe03500b034ffe034e4d10054efe034d10034cfe034b4a13054bfe 459 | 034a4910054a1303491d0d05491003480d0347fe0346960345960344fe0343022d0543fa0342 460 | bb03414b0340fe033ffe033e3d12053e14033d3c0f053d12033c3b0d053c40ff0f033b0d033a 461 | fe0339fe033837140538fa033736100537140336350b05361003350b03341e03330d0332310b 462 | 0532fe03310b03302f0b05300d032f0b032e2d09052e10032d09032c32032b2a25052b64032a 463 | 2912052a25032912032827250528410327250326250b05260f03250b0324fe0323fe03220f03 464 | 210110052112032064031ffa031e1d0d051e64031d0d031c1142051cfe031bfa031a42031911 465 | 420519fe031864031716190517fe031601100516190315fe0314fe0313fe031211420512fe03 466 | 11022d05114203107d030f64030efe030d0c16050dfe030c0110050c16030bfe030a100309fe 467 | 0308022d0508fe030714030664030401100504fe03401503022d0503fe0302011005022d0301 468 | 100300fe0301b80164858d012b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b 469 | 2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b 470 | 2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b 471 | 2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b 472 | 2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b002b2b2b2b2b2b2b2b2b2b2b2b 473 | 2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b 474 | 2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b 475 | 2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b 476 | 2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b1d00> 477 | ] def 478 | /f-1-0 currentdict end definefont pop 479 | %%EndResource 480 | %%EndSetup 481 | %%Page: 1 1 482 | %%BeginPageSetup 483 | %%PageBoundingBox: 0 7 543 332 484 | %%EndPageSetup 485 | q 0 7 543 325 rectclip 486 | 1 0 0 -1 0 334 cm q 487 | 0.854902 0.909804 0.988235 rg 488 | 264.75 54.75 m 227.25 54.75 217.875 90.375 247.875 97.5 c 217.875 113.176 489 | 251.625 147.375 276 133.125 c 292.875 161.625 349.125 161.625 367.875 133.125 490 | c 405.375 133.125 405.375 104.625 381.938 90.375 c 405.375 61.875 367.875 491 | 33.375 335.062 47.625 c 311.625 26.25 274.125 26.25 264.75 54.75 c h 492 | 264.75 54.75 m f 493 | 0.423529 0.556863 0.74902 rg 494 | 0.75 w 495 | 0 J 496 | 0 j 497 | [] 0.0 d 498 | 10 M q 1 0 0 1 0 0 cm 499 | 264.75 54.75 m 227.25 54.75 217.875 90.375 247.875 97.5 c 217.875 113.176 500 | 251.625 147.375 276 133.125 c 292.875 161.625 349.125 161.625 367.875 133.125 501 | c 405.375 133.125 405.375 104.625 381.938 90.375 c 405.375 61.875 367.875 502 | 33.375 335.062 47.625 c 311.625 26.25 274.125 26.25 264.75 54.75 c h 503 | 264.75 54.75 m S Q 504 | 1 0.94902 0.8 rg 505 | 210.375 124.125 45 52.5 re f 506 | 0.839216 0.713726 0.337255 rg 507 | 4 M q 1 0 0 1 0 0 cm 508 | 210.375 124.125 45 52.5 re S Q 509 | 0 g 510 | 10 M q 1 0 0 1 0 0 cm 511 | 82.875 228.801 m 143.199 228.801 l 143.199 271.656 l 412.703 271.656 l S Q 512 | 416.641 271.656 m 411.391 274.281 l 412.703 271.656 l 411.391 269.031 l 513 | h 514 | 416.641 271.656 m f 515 | q 1 0 0 1 0 0 cm 516 | 416.641 271.656 m 411.391 274.281 l 412.703 271.656 l 411.391 269.031 l 517 | h 518 | 416.641 271.656 m S Q 519 | q 1 0 0 1 0 0 cm 520 | 480.699 297.375 m 480.699 326.301 l 63.906 326.301 l 63.906 271.402 l S Q 521 | 63.906 267.465 m 66.531 272.715 l 63.906 271.402 l 61.281 272.715 l h 522 | 63.906 267.465 m f 523 | q 1 0 0 1 0 0 cm 524 | 63.906 267.465 m 66.531 272.715 l 63.906 271.402 l 61.281 272.715 l h 525 | 63.906 267.465 m S Q 526 | 0.701961 g 527 | 45.375 198.547 m 45.375 196.711 46.086 194.949 47.355 193.648 c 48.613 528 | 192.352 50.332 191.625 52.125 191.625 c 76.125 191.625 l 77.918 191.625 529 | 79.637 192.352 80.895 193.648 c 82.164 194.949 82.875 196.711 82.875 198.547 530 | c 82.875 259.703 l 82.875 261.539 82.164 263.301 80.895 264.602 c 79.637 531 | 265.898 77.918 266.625 76.125 266.625 c 52.125 266.625 l 50.332 266.625 532 | 48.613 265.898 47.355 264.602 c 46.086 263.301 45.375 261.539 45.375 259.703 533 | c h 534 | 46.875 198.352 m 46.875 259.898 l 81.562 259.898 l 81.562 198.352 l h 535 | 46.875 198.352 m f 536 | 0 g 537 | q 1 0 0 1 0 0 cm 538 | 45.375 198.547 m 45.375 196.711 46.086 194.949 47.355 193.648 c 48.613 539 | 192.352 50.332 191.625 52.125 191.625 c 76.125 191.625 l 77.918 191.625 540 | 79.637 192.352 80.895 193.648 c 82.164 194.949 82.875 196.711 82.875 198.547 541 | c 82.875 259.703 l 82.875 261.539 82.164 263.301 80.895 264.602 c 79.637 542 | 265.898 77.918 266.625 76.125 266.625 c 52.125 266.625 l 50.332 266.625 543 | 48.613 265.898 47.355 264.602 c 46.086 263.301 45.375 261.539 45.375 259.703 544 | c h 545 | 46.875 198.352 m 46.875 259.898 l 81.562 259.898 l 81.562 198.352 l h 546 | 46.875 198.352 m S Q 547 | 1 g 548 | 58.5 193.164 m 58.5 193.48 58.25 193.738 57.938 193.738 c 57.625 193.738 549 | 57.375 193.48 57.375 193.164 c 57.375 192.844 57.625 192.586 57.938 192.586 550 | c 58.25 192.586 58.5 192.844 58.5 193.164 c h 551 | 58.5 193.164 m f 552 | 0 g 553 | 4 M q 1 0 0 1 0 0 cm 554 | 58.5 193.164 m 58.5 193.48 58.25 193.738 57.938 193.738 c 57.625 193.738 555 | 57.375 193.48 57.375 193.164 c 57.375 192.844 57.625 192.586 57.938 192.586 556 | c 58.25 192.586 58.5 192.844 58.5 193.164 c h 557 | 58.5 193.164 m S Q 558 | 1 g 559 | 60.781 262.012 m 67.469 262.012 l 68.109 262.012 68.625 262.527 68.625 560 | 263.168 c 68.625 263.551 l 68.625 264.191 68.109 264.703 67.469 264.703 561 | c 60.781 264.703 l 60.141 264.703 59.625 264.191 59.625 263.551 c 59.625 562 | 263.168 l 59.625 262.527 60.141 262.012 60.781 262.012 c h 563 | 60.781 262.012 m f 564 | 0 g 565 | q 1 0 0 1 0 0 cm 566 | 60.781 262.012 m 67.469 262.012 l 68.109 262.012 68.625 262.527 68.625 567 | 263.168 c 68.625 263.551 l 68.625 264.191 68.109 264.703 67.469 264.703 568 | c 60.781 264.703 l 60.141 264.703 59.625 264.191 59.625 263.551 c 59.625 569 | 263.168 l 59.625 262.527 60.141 262.012 60.781 262.012 c h 570 | 60.781 262.012 m S Q 571 | 1 g 572 | 60.195 192.969 m 67.867 192.969 l 68.078 192.969 68.25 193.137 68.25 193.352 573 | c 68.25 193.547 l 68.25 193.758 68.078 193.926 67.867 193.926 c 60.195 574 | 193.926 l 59.984 193.926 59.812 193.758 59.812 193.547 c 59.812 193.352 575 | l 59.812 193.137 59.984 192.969 60.195 192.969 c h 576 | 60.195 192.969 m f 577 | 0 g 578 | q 1 0 0 1 0 0 cm 579 | 60.195 192.969 m 67.867 192.969 l 68.078 192.969 68.25 193.137 68.25 193.352 580 | c 68.25 193.547 l 68.25 193.758 68.078 193.926 67.867 193.926 c 60.195 581 | 193.926 l 59.984 193.926 59.812 193.758 59.812 193.547 c 59.812 193.352 582 | l 59.812 193.137 59.984 192.969 60.195 192.969 c h 583 | 60.195 192.969 m S Q 584 | 1 g 585 | 70.312 193.164 m 70.312 193.48 70.062 193.738 69.75 193.738 c 69.438 193.738 586 | 69.188 193.48 69.188 193.164 c 69.188 192.844 69.438 192.586 69.75 192.586 587 | c 70.062 192.586 70.312 192.844 70.312 193.164 c h 588 | 70.312 193.164 m f 589 | 0 g 590 | q 1 0 0 1 0 0 cm 591 | 70.312 193.164 m 70.312 193.48 70.062 193.738 69.75 193.738 c 69.438 193.738 592 | 69.188 193.48 69.188 193.164 c 69.188 192.844 69.438 192.586 69.75 192.586 593 | c 70.062 192.586 70.312 192.844 70.312 193.164 c h 594 | 70.312 193.164 m S Q 595 | 1 g 596 | 72.188 193.164 m 72.188 193.48 71.938 193.738 71.625 193.738 c 71.312 193.738 597 | 71.062 193.48 71.062 193.164 c 71.062 192.844 71.312 192.586 71.625 192.586 598 | c 71.938 192.586 72.188 192.844 72.188 193.164 c h 599 | 72.188 193.164 m f 600 | 0 g 601 | q 1 0 0 1 0 0 cm 602 | 72.188 193.164 m 72.188 193.48 71.938 193.738 71.625 193.738 c 71.312 193.738 603 | 71.062 193.48 71.062 193.164 c 71.062 192.844 71.312 192.586 71.625 192.586 604 | c 71.938 192.586 72.188 192.844 72.188 193.164 c h 605 | 72.188 193.164 m S Q 606 | 1 g 607 | 77.062 193.934 m 77.062 194.359 76.727 194.703 76.312 194.703 c 75.898 608 | 194.703 75.562 194.359 75.562 193.934 c 75.562 193.512 75.898 193.164 76.312 609 | 193.164 c 76.727 193.164 77.062 193.512 77.062 193.934 c h 610 | 77.062 193.934 m f 611 | 0 g 612 | q 1 0 0 1 0 0 cm 613 | 77.062 193.934 m 77.062 194.359 76.727 194.703 76.312 194.703 c 75.898 614 | 194.703 75.562 194.359 75.562 193.934 c 75.562 193.512 75.898 193.164 76.312 615 | 193.164 c 76.727 193.164 77.062 193.512 77.062 193.934 c h 616 | 77.062 193.934 m S Q 617 | 10 M q 1 0 0 1 0 0 cm 618 | 210.699 137.73 m 150.699 137.73 l 150.699 108.801 l 95.152 108.801 l S Q 619 | 91.215 108.801 m 96.465 106.176 l 95.152 108.801 l 96.465 111.426 l h 620 | 91.215 108.801 m f 621 | q 1 0 0 1 0 0 cm 622 | 91.215 108.801 m 96.465 106.176 l 95.152 108.801 l 96.465 111.426 l h 623 | 91.215 108.801 m S Q 624 | BT 625 | 9 0 0 -9 138.208008 151.5 Tm 626 | /f-0-0 1 Tf 627 | [(r)9(e)3(sponse)-3( t)-14(ype)]TJ 628 | ET 629 | 0.835294 0.909804 0.831373 rg 630 | 0.375 86.625 90 45 re f 631 | 0.509804 0.701961 0.4 rg 632 | 4 M q 1 0 0 1 0 0 cm 633 | 0.375 86.625 90 45 re S Q 634 | 0 g 635 | BT 636 | 9 0 0 -9 19.749023 112.5 Tm 637 | /f-0-0 1 Tf 638 | [(T)101(ypechec)-8(k)21(er)]TJ 639 | ET 640 | 10 M q 1 0 0 1 0 0 cm 641 | 45.699 86.301 m 45.699 18.801 l 458.199 18.801 l 458.199 171.523 l S Q 642 | 458.199 175.461 m 455.574 170.211 l 458.199 171.523 l 460.824 170.211 l 643 | h 644 | 458.199 175.461 m f 645 | q 1 0 0 1 0 0 cm 646 | 458.199 175.461 m 455.574 170.211 l 458.199 171.523 l 460.824 170.211 l 647 | h 648 | 458.199 175.461 m S Q 649 | BT 650 | 9 0 0 -9 205.681641 8.25 Tm 651 | /f-0-0 1 Tf 652 | [(quer)-26(y t)15(erm)]TJ 653 | ET 654 | q 1 0 0 1 0 0 cm 655 | 407.844 219.156 m 278.199 219.156 l 278.199 161.301 l 260.477 161.301 l 656 | S Q 657 | 256.539 161.301 m 261.789 158.676 l 260.477 161.301 l 261.789 163.926 l 658 | h 659 | 256.539 161.301 m f 660 | q 1 0 0 1 0 0 cm 661 | 256.539 161.301 m 261.789 158.676 l 260.477 161.301 l 261.789 163.926 l 662 | h 663 | 256.539 161.301 m S Q 664 | BT 665 | 9 0 0 -9 308.024509 213.404175 Tm 666 | /f-0-0 1 Tf 667 | [(r)9(e)3(sponse)-3( JS)10(ON)]TJ 668 | ET 669 | 0.882353 0.835294 0.905882 rg 670 | 435.375 197.25 m 405.375 197.25 397.875 232.875 421.875 240 c 397.875 255.676 671 | 424.875 289.875 444.375 275.625 c 457.875 304.125 502.875 304.125 517.875 672 | 275.625 c 547.875 275.625 547.875 247.125 529.125 232.875 c 547.875 204.375 673 | 517.875 175.875 491.625 190.125 c 472.875 168.75 442.875 168.75 435.375 674 | 197.25 c h 675 | 435.375 197.25 m f 676 | 0.588235 0.45098 0.65098 rg 677 | q 1 0 0 1 0 0 cm 678 | 435.375 197.25 m 405.375 197.25 397.875 232.875 421.875 240 c 397.875 255.676 679 | 424.875 289.875 444.375 275.625 c 457.875 304.125 502.875 304.125 517.875 680 | 275.625 c 547.875 275.625 547.875 247.125 529.125 232.875 c 547.875 204.375 681 | 517.875 175.875 491.625 190.125 c 472.875 168.75 442.875 168.75 435.375 682 | 197.25 c h 683 | 435.375 197.25 m S Q 684 | 0 g 685 | BT 686 | 12 0 0 -12 273.150729 91.285573 Tm 687 | /f-1-0 1 Tf 688 | [(Cloud Hosting)]TJ 689 | 1.651367 -1.25 Td 690 | [(Service)]TJ 691 | 7.411151 0 0 -7.411151 215.693264 148.479193 Tm 692 | [(W)59(ebhook)]TJ 693 | 0.667969 -1.25 Td 694 | (Server)Tj 695 | 12 0 0 -12 451.483803 233.780479 Tm 696 | (SMS API )Tj 697 | 0.167969 -1.25 Td 698 | [(Service)]TJ 699 | 8.151561 0 0 -8.151561 181.159985 267.936516 Tm 700 | [(huma)-3(n-gener)-3(ated r)19(esponse t)-3(e)18(xt)]TJ 701 | 2.041573 -6.580083 Td 702 | [(huma)-3(n-r)19(eadable quer)-3(y)]TJ 703 | ET 704 | Q Q 705 | showpage 706 | %%Trailer 707 | end 708 | %%EOF 709 | -------------------------------------------------------------------------------- /paper/phone-a-friend.svg: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 23 | 25 | image/svg+xml 26 | 28 | 29 | 30 | 31 | 32 | 53 | 55 | 62 | 70 | 77 | 84 | 91 | 98 | 105 | 113 | 123 | 133 | 141 | 149 | 157 | 164 | 171 | 174 | 176 | 182 | 184 | response type 186 | 187 | 188 | response type 194 | 195 | 196 | 204 | 207 | 209 | 215 | 217 | Typechecker 219 | 220 | 221 | Typechecker 227 | 228 | 229 | 236 | 243 | 246 | 248 | 254 | 256 | query term 258 | 259 | 260 | query term 266 | 267 | 268 | 275 | 282 | 285 | 287 | 293 | 295 | response JSON 297 | 298 | 299 | response JSON 305 | 306 | 307 | 314 | Cloud HostingService 333 | 335 | 341 | 343 | Webhook 345 | 346 | 347 | Server 348 | 349 | 350 | 356 | 357 | 358 | WebhookServer SMS API Service human-generated response text 417 | human-readable query 428 | 429 | -------------------------------------------------------------------------------- /paper/txt-message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bovik-labs/oracle-types/97fac25ecbfb0ab44c50d4db997288f88d65d422/paper/txt-message.png -------------------------------------------------------------------------------- /scripts/.emacs: -------------------------------------------------------------------------------- 1 | (package-initialize) 2 | 3 | ;; When launched directly from an image, emacs doesn't seem to execute 4 | ;; the appropriate shell stuff in .bashrc 5 | (setq shell-file-name "/bin/bash") 6 | (setq exec-path (append exec-path '("/root/.nvm/versions/node/v15.4.0/bin"))) 7 | 8 | (require 'package) 9 | (setq package-archives 10 | '(("gnu" . "https://elpa.gnu.org/packages/") 11 | ("melpa" . "https://melpa.org/packages/"))) 12 | 13 | (custom-set-variables 14 | '(package-selected-packages (tide))) 15 | 16 | (defun setup-tide-mode () 17 | (add-hook 'before-save-hook 'tide-format-before-save) 18 | (setq tide-tsserver-executable "/tapeworm/TypeScript/tsserver.js") 19 | (setq typescript-indent-level 2) 20 | (tide-setup) 21 | (eldoc-mode +1) 22 | (flycheck-mode +1) 23 | (setq flycheck-check-syntax-automatically '(save mode-enabled)) 24 | (tide-hl-identifier-mode +1) 25 | (company-mode +1) 26 | (setq company-idle-delay nil) 27 | (define-key tide-mode-map "\C-c\C-r" 'tide-references) 28 | (define-key tide-mode-map "\C-c\C-s" 'tide-rename-symbol) 29 | (define-key tide-mode-map (kbd "M-;") 'company-complete) 30 | ;; aligns annotation to the right hand side 31 | (setq company-tooltip-align-annotations t) 32 | (fixup-tide-parse-error)) 33 | 34 | (add-hook 'typescript-mode-hook #'setup-tide-mode) 35 | 36 | (define-key global-map "\C-x;" 'comment-region) 37 | 38 | (set-cursor-color "#700") 39 | 40 | (defun fixup-tide-parse-error () 41 | (defun tide-parse-error (response checker) 42 | (-map 43 | (lambda (diagnostic) 44 | (let* ((start (plist-get diagnostic :start)) 45 | (line (plist-get start :line)) 46 | (column (plist-get start :offset)) 47 | (level (if (string= (plist-get diagnostic :category) "suggestion") 'info 'error)) 48 | (text (plist-get diagnostic :text))) 49 | (when (plist-get diagnostic :relatedInformation) 50 | (setq text (concat text (propertize " ⮐" 'face 'font-lock-warning-face)))) 51 | (put-text-property 0 1 'diagnostic diagnostic text) 52 | (flycheck-error-new-at line column level text 53 | :checker checker 54 | :id (plist-get diagnostic :code)))) 55 | (let ((diagnostic (car (tide-plist-get response :body)))) 56 | (-concat (plist-get diagnostic :syntaxDiag) 57 | (plist-get diagnostic :semanticDiag) 58 | ;(plist-get diagnostic :suggestionDiag) 59 | ))))) 60 | -------------------------------------------------------------------------------- /scripts/demo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | . /root/.bashrc 4 | emacs /tapeworm/magic_demo/src/orm-demo.ts 5 | -------------------------------------------------------------------------------- /scripts/init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Initializing database demo..." 4 | 5 | # save from having to type password 6 | echo "database:5432:postgres:postgres:glorpglop" > ~/.pgpass 7 | chmod 0600 ~/.pgpass 8 | 9 | PSQL="psql -h database -U postgres" 10 | 11 | # convenience alias so `psql` at the commandline has the right arguments 12 | echo "alias psql=\"${PSQL}\"" >> ~/.bashrc 13 | 14 | emacs --script /tapeworm/scripts/setup-emacs.el 15 | -------------------------------------------------------------------------------- /scripts/populate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | RETRIES=10 4 | 5 | PSQL="psql -U ${POSTGRES_USER} -h ${DB_HOST}" 6 | until ${PSQL} -c "SELECT 1"> /dev/null 2>&1 || [ $RETRIES -eq 0 ]; do 7 | echo "Waiting for postgres, $((RETRIES--))..." 8 | sleep 2 9 | done 10 | 11 | # populate the database with a table and some rows 12 | ${PSQL} -c "CREATE TABLE users (id int PRIMARY KEY NOT NULL, name text);" 13 | ${PSQL} -c "CREATE TABLE papers (id int PRIMARY KEY NOT NULL, title text, author int REFERENCES users(id));"; 14 | ${PSQL} -f - <= 6.0.0" 120 | } 121 | }, 122 | "node_modules/asap": { 123 | "version": "2.0.6", 124 | "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", 125 | "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" 126 | }, 127 | "node_modules/axios": { 128 | "version": "0.21.1", 129 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", 130 | "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", 131 | "dependencies": { 132 | "follow-redirects": "^1.10.0" 133 | } 134 | }, 135 | "node_modules/buffer-equal-constant-time": { 136 | "version": "1.0.1", 137 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 138 | "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" 139 | }, 140 | "node_modules/dayjs": { 141 | "version": "1.10.4", 142 | "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.4.tgz", 143 | "integrity": "sha512-RI/Hh4kqRc1UKLOAf/T5zdMMX5DQIlDxwUe3wSyMMnEbGunnpENCdbUgM+dW7kXidZqCttBrmw7BhN4TMddkCw==" 144 | }, 145 | "node_modules/debug": { 146 | "version": "4.3.1", 147 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", 148 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", 149 | "dependencies": { 150 | "ms": "2.1.2" 151 | }, 152 | "engines": { 153 | "node": ">=6.0" 154 | }, 155 | "peerDependenciesMeta": { 156 | "supports-color": { 157 | "optional": true 158 | } 159 | } 160 | }, 161 | "node_modules/ecdsa-sig-formatter": { 162 | "version": "1.0.11", 163 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", 164 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", 165 | "dependencies": { 166 | "safe-buffer": "^5.0.1" 167 | } 168 | }, 169 | "node_modules/follow-redirects": { 170 | "version": "1.13.3", 171 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.3.tgz", 172 | "integrity": "sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA==", 173 | "funding": [ 174 | { 175 | "type": "individual", 176 | "url": "https://github.com/sponsors/RubenVerborgh" 177 | } 178 | ], 179 | "engines": { 180 | "node": ">=4.0" 181 | }, 182 | "peerDependenciesMeta": { 183 | "debug": { 184 | "optional": true 185 | } 186 | } 187 | }, 188 | "node_modules/https-proxy-agent": { 189 | "version": "5.0.0", 190 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", 191 | "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", 192 | "dependencies": { 193 | "agent-base": "6", 194 | "debug": "4" 195 | }, 196 | "engines": { 197 | "node": ">= 6" 198 | } 199 | }, 200 | "node_modules/jsonwebtoken": { 201 | "version": "8.5.1", 202 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", 203 | "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", 204 | "dependencies": { 205 | "jws": "^3.2.2", 206 | "lodash.includes": "^4.3.0", 207 | "lodash.isboolean": "^3.0.3", 208 | "lodash.isinteger": "^4.0.4", 209 | "lodash.isnumber": "^3.0.3", 210 | "lodash.isplainobject": "^4.0.6", 211 | "lodash.isstring": "^4.0.1", 212 | "lodash.once": "^4.0.0", 213 | "ms": "^2.1.1", 214 | "semver": "^5.6.0" 215 | }, 216 | "engines": { 217 | "node": ">=4", 218 | "npm": ">=1.4.28" 219 | } 220 | }, 221 | "node_modules/jwa": { 222 | "version": "1.4.1", 223 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", 224 | "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", 225 | "dependencies": { 226 | "buffer-equal-constant-time": "1.0.1", 227 | "ecdsa-sig-formatter": "1.0.11", 228 | "safe-buffer": "^5.0.1" 229 | } 230 | }, 231 | "node_modules/jws": { 232 | "version": "3.2.2", 233 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", 234 | "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", 235 | "dependencies": { 236 | "jwa": "^1.4.1", 237 | "safe-buffer": "^5.0.1" 238 | } 239 | }, 240 | "node_modules/lodash": { 241 | "version": "4.17.21", 242 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 243 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 244 | }, 245 | "node_modules/lodash.includes": { 246 | "version": "4.3.0", 247 | "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", 248 | "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" 249 | }, 250 | "node_modules/lodash.isboolean": { 251 | "version": "3.0.3", 252 | "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", 253 | "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" 254 | }, 255 | "node_modules/lodash.isinteger": { 256 | "version": "4.0.4", 257 | "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", 258 | "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" 259 | }, 260 | "node_modules/lodash.isnumber": { 261 | "version": "3.0.3", 262 | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", 263 | "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" 264 | }, 265 | "node_modules/lodash.isplainobject": { 266 | "version": "4.0.6", 267 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", 268 | "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" 269 | }, 270 | "node_modules/lodash.isstring": { 271 | "version": "4.0.1", 272 | "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", 273 | "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" 274 | }, 275 | "node_modules/lodash.once": { 276 | "version": "4.1.1", 277 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", 278 | "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" 279 | }, 280 | "node_modules/ms": { 281 | "version": "2.1.2", 282 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 283 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 284 | }, 285 | "node_modules/pop-iterate": { 286 | "version": "1.0.1", 287 | "resolved": "https://registry.npmjs.org/pop-iterate/-/pop-iterate-1.0.1.tgz", 288 | "integrity": "sha1-zqz9q0q/NT16DyqqLB/Hs/lBO6M=" 289 | }, 290 | "node_modules/q": { 291 | "version": "2.0.3", 292 | "resolved": "https://registry.npmjs.org/q/-/q-2.0.3.tgz", 293 | "integrity": "sha1-dbjbAlWhpa+C9Yw/Oqoe/sfQ0TQ=", 294 | "dependencies": { 295 | "asap": "^2.0.0", 296 | "pop-iterate": "^1.0.1", 297 | "weak-map": "^1.0.5" 298 | } 299 | }, 300 | "node_modules/qs": { 301 | "version": "6.9.6", 302 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", 303 | "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", 304 | "engines": { 305 | "node": ">=0.6" 306 | }, 307 | "funding": { 308 | "url": "https://github.com/sponsors/ljharb" 309 | } 310 | }, 311 | "node_modules/querystringify": { 312 | "version": "2.2.0", 313 | "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", 314 | "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" 315 | }, 316 | "node_modules/requires-port": { 317 | "version": "1.0.0", 318 | "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", 319 | "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" 320 | }, 321 | "node_modules/rootpath": { 322 | "version": "0.1.2", 323 | "resolved": "https://registry.npmjs.org/rootpath/-/rootpath-0.1.2.tgz", 324 | "integrity": "sha1-Wzeah9ypBum5HWkKWZQ5vvJn6ms=" 325 | }, 326 | "node_modules/safe-buffer": { 327 | "version": "5.2.1", 328 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 329 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 330 | "funding": [ 331 | { 332 | "type": "github", 333 | "url": "https://github.com/sponsors/feross" 334 | }, 335 | { 336 | "type": "patreon", 337 | "url": "https://www.patreon.com/feross" 338 | }, 339 | { 340 | "type": "consulting", 341 | "url": "https://feross.org/support" 342 | } 343 | ] 344 | }, 345 | "node_modules/scmp": { 346 | "version": "2.1.0", 347 | "resolved": "https://registry.npmjs.org/scmp/-/scmp-2.1.0.tgz", 348 | "integrity": "sha512-o/mRQGk9Rcer/jEEw/yw4mwo3EU/NvYvp577/Btqrym9Qy5/MdWGBqipbALgd2lrdWTJ5/gqDusxfnQBxOxT2Q==" 349 | }, 350 | "node_modules/semver": { 351 | "version": "5.7.1", 352 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 353 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", 354 | "bin": { 355 | "semver": "bin/semver" 356 | } 357 | }, 358 | "node_modules/tiny-json-http": { 359 | "version": "7.3.0", 360 | "resolved": "https://registry.npmjs.org/tiny-json-http/-/tiny-json-http-7.3.0.tgz", 361 | "integrity": "sha512-dZrf9ZGZQhe8QhhPN3o4uDCQuBc3Gaq4CtbU/67hQDWXuvjLI1mayr8AOFSiUGa3F818SpIgUnC3mM673VRHGQ==" 362 | }, 363 | "node_modules/twilio": { 364 | "version": "3.57.0", 365 | "resolved": "https://registry.npmjs.org/twilio/-/twilio-3.57.0.tgz", 366 | "integrity": "sha512-gt1NtEM647c/+KGcPiBEY2YCCrvm7nKXfd2bOT6PNYUAbBF5n0s3Ed0lYQW5BngpJEjeZROMZjfp9ikwehS/pg==", 367 | "dependencies": { 368 | "axios": "^0.21.1", 369 | "dayjs": "^1.8.29", 370 | "https-proxy-agent": "^5.0.0", 371 | "jsonwebtoken": "^8.5.1", 372 | "lodash": "^4.17.19", 373 | "q": "2.0.x", 374 | "qs": "^6.9.4", 375 | "rootpath": "^0.1.2", 376 | "scmp": "^2.1.0", 377 | "url-parse": "^1.4.7", 378 | "xmlbuilder": "^13.0.2" 379 | }, 380 | "engines": { 381 | "node": ">=6.0" 382 | }, 383 | "peerDependencies": { 384 | "@types/express": "^4.17.7", 385 | "@types/qs": "6.9.4" 386 | } 387 | }, 388 | "node_modules/typescript": { 389 | "version": "4.2.3", 390 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz", 391 | "integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==", 392 | "dev": true, 393 | "bin": { 394 | "tsc": "bin/tsc", 395 | "tsserver": "bin/tsserver" 396 | }, 397 | "engines": { 398 | "node": ">=4.2.0" 399 | } 400 | }, 401 | "node_modules/url-parse": { 402 | "version": "1.5.1", 403 | "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz", 404 | "integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==", 405 | "dependencies": { 406 | "querystringify": "^2.1.1", 407 | "requires-port": "^1.0.0" 408 | } 409 | }, 410 | "node_modules/weak-map": { 411 | "version": "1.0.5", 412 | "resolved": "https://registry.npmjs.org/weak-map/-/weak-map-1.0.5.tgz", 413 | "integrity": "sha1-eWkVhNmGB/UHC9O3CkDmuyLkAes=" 414 | }, 415 | "node_modules/xmlbuilder": { 416 | "version": "13.0.2", 417 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz", 418 | "integrity": "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==", 419 | "engines": { 420 | "node": ">=6.0" 421 | } 422 | } 423 | }, 424 | "dependencies": { 425 | "@types/body-parser": { 426 | "version": "1.19.0", 427 | "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", 428 | "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", 429 | "requires": { 430 | "@types/connect": "*", 431 | "@types/node": "*" 432 | } 433 | }, 434 | "@types/connect": { 435 | "version": "3.4.34", 436 | "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz", 437 | "integrity": "sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ==", 438 | "requires": { 439 | "@types/node": "*" 440 | } 441 | }, 442 | "@types/express": { 443 | "version": "4.17.11", 444 | "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz", 445 | "integrity": "sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg==", 446 | "requires": { 447 | "@types/body-parser": "*", 448 | "@types/express-serve-static-core": "^4.17.18", 449 | "@types/qs": "*", 450 | "@types/serve-static": "*" 451 | } 452 | }, 453 | "@types/express-serve-static-core": { 454 | "version": "4.17.18", 455 | "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.18.tgz", 456 | "integrity": "sha512-m4JTwx5RUBNZvky/JJ8swEJPKFd8si08pPF2PfizYjGZOKr/svUWPcoUmLow6MmPzhasphB7gSTINY67xn3JNA==", 457 | "requires": { 458 | "@types/node": "*", 459 | "@types/qs": "*", 460 | "@types/range-parser": "*" 461 | } 462 | }, 463 | "@types/mime": { 464 | "version": "1.3.2", 465 | "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", 466 | "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" 467 | }, 468 | "@types/node": { 469 | "version": "14.14.34", 470 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.34.tgz", 471 | "integrity": "sha512-dBPaxocOK6UVyvhbnpFIj2W+S+1cBTkHQbFQfeeJhoKFbzYcVUGHvddeWPSucKATb3F0+pgDq0i6ghEaZjsugA==" 472 | }, 473 | "@types/q": { 474 | "version": "0.0.38", 475 | "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.38.tgz", 476 | "integrity": "sha512-uIgMipzzpJQG77qTC5zmHiGav9ig3qs7xC0U8NWiQQ+VE3HXLJoXosL4tQ6kv1/0eOybQp5qkDvmwd+5bpgz9w==", 477 | "dev": true 478 | }, 479 | "@types/qs": { 480 | "version": "6.9.4", 481 | "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.4.tgz", 482 | "integrity": "sha512-+wYo+L6ZF6BMoEjtf8zB2esQsqdV6WsjRK/GP9WOgLPrq87PbNWgIxS76dS5uvl/QXtHGakZmwTznIfcPXcKlQ==" 483 | }, 484 | "@types/range-parser": { 485 | "version": "1.2.3", 486 | "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", 487 | "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" 488 | }, 489 | "@types/serve-static": { 490 | "version": "1.13.9", 491 | "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.9.tgz", 492 | "integrity": "sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA==", 493 | "requires": { 494 | "@types/mime": "^1", 495 | "@types/node": "*" 496 | } 497 | }, 498 | "@types/tiny-json-http": { 499 | "version": "7.3.0", 500 | "resolved": "https://registry.npmjs.org/@types/tiny-json-http/-/tiny-json-http-7.3.0.tgz", 501 | "integrity": "sha512-67n6qRR0tHh1TLFnpuDjC3ItxiBedsdnkrU52PS1P6Phrv7vf9dx3A9bjTdlh/qLNkrRjn/YAaAb8OoUONz4Cg==", 502 | "dev": true 503 | }, 504 | "@types/twilio": { 505 | "version": "2.11.0", 506 | "resolved": "https://registry.npmjs.org/@types/twilio/-/twilio-2.11.0.tgz", 507 | "integrity": "sha512-hPuujay9boqWQuDdcnT02DnINl1tSz6m8K/o1gY31fjRDGybTC4s3mPbFSlF1LQ9H0yo3tzgYfa0cERRYsZiqA==", 508 | "dev": true, 509 | "requires": { 510 | "@types/express": "*", 511 | "@types/node": "*", 512 | "@types/q": "^0" 513 | } 514 | }, 515 | "agent-base": { 516 | "version": "6.0.2", 517 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", 518 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", 519 | "requires": { 520 | "debug": "4" 521 | } 522 | }, 523 | "asap": { 524 | "version": "2.0.6", 525 | "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", 526 | "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" 527 | }, 528 | "axios": { 529 | "version": "0.21.1", 530 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", 531 | "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", 532 | "requires": { 533 | "follow-redirects": "^1.10.0" 534 | } 535 | }, 536 | "buffer-equal-constant-time": { 537 | "version": "1.0.1", 538 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 539 | "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" 540 | }, 541 | "dayjs": { 542 | "version": "1.10.4", 543 | "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.4.tgz", 544 | "integrity": "sha512-RI/Hh4kqRc1UKLOAf/T5zdMMX5DQIlDxwUe3wSyMMnEbGunnpENCdbUgM+dW7kXidZqCttBrmw7BhN4TMddkCw==" 545 | }, 546 | "debug": { 547 | "version": "4.3.1", 548 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", 549 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", 550 | "requires": { 551 | "ms": "2.1.2" 552 | } 553 | }, 554 | "ecdsa-sig-formatter": { 555 | "version": "1.0.11", 556 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", 557 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", 558 | "requires": { 559 | "safe-buffer": "^5.0.1" 560 | } 561 | }, 562 | "follow-redirects": { 563 | "version": "1.13.3", 564 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.3.tgz", 565 | "integrity": "sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA==" 566 | }, 567 | "https-proxy-agent": { 568 | "version": "5.0.0", 569 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", 570 | "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", 571 | "requires": { 572 | "agent-base": "6", 573 | "debug": "4" 574 | } 575 | }, 576 | "jsonwebtoken": { 577 | "version": "8.5.1", 578 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", 579 | "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", 580 | "requires": { 581 | "jws": "^3.2.2", 582 | "lodash.includes": "^4.3.0", 583 | "lodash.isboolean": "^3.0.3", 584 | "lodash.isinteger": "^4.0.4", 585 | "lodash.isnumber": "^3.0.3", 586 | "lodash.isplainobject": "^4.0.6", 587 | "lodash.isstring": "^4.0.1", 588 | "lodash.once": "^4.0.0", 589 | "ms": "^2.1.1", 590 | "semver": "^5.6.0" 591 | } 592 | }, 593 | "jwa": { 594 | "version": "1.4.1", 595 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", 596 | "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", 597 | "requires": { 598 | "buffer-equal-constant-time": "1.0.1", 599 | "ecdsa-sig-formatter": "1.0.11", 600 | "safe-buffer": "^5.0.1" 601 | } 602 | }, 603 | "jws": { 604 | "version": "3.2.2", 605 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", 606 | "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", 607 | "requires": { 608 | "jwa": "^1.4.1", 609 | "safe-buffer": "^5.0.1" 610 | } 611 | }, 612 | "lodash": { 613 | "version": "4.17.21", 614 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 615 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 616 | }, 617 | "lodash.includes": { 618 | "version": "4.3.0", 619 | "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", 620 | "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" 621 | }, 622 | "lodash.isboolean": { 623 | "version": "3.0.3", 624 | "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", 625 | "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" 626 | }, 627 | "lodash.isinteger": { 628 | "version": "4.0.4", 629 | "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", 630 | "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" 631 | }, 632 | "lodash.isnumber": { 633 | "version": "3.0.3", 634 | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", 635 | "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" 636 | }, 637 | "lodash.isplainobject": { 638 | "version": "4.0.6", 639 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", 640 | "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" 641 | }, 642 | "lodash.isstring": { 643 | "version": "4.0.1", 644 | "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", 645 | "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" 646 | }, 647 | "lodash.once": { 648 | "version": "4.1.1", 649 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", 650 | "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" 651 | }, 652 | "ms": { 653 | "version": "2.1.2", 654 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 655 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 656 | }, 657 | "pop-iterate": { 658 | "version": "1.0.1", 659 | "resolved": "https://registry.npmjs.org/pop-iterate/-/pop-iterate-1.0.1.tgz", 660 | "integrity": "sha1-zqz9q0q/NT16DyqqLB/Hs/lBO6M=" 661 | }, 662 | "q": { 663 | "version": "2.0.3", 664 | "resolved": "https://registry.npmjs.org/q/-/q-2.0.3.tgz", 665 | "integrity": "sha1-dbjbAlWhpa+C9Yw/Oqoe/sfQ0TQ=", 666 | "requires": { 667 | "asap": "^2.0.0", 668 | "pop-iterate": "^1.0.1", 669 | "weak-map": "^1.0.5" 670 | } 671 | }, 672 | "qs": { 673 | "version": "6.9.6", 674 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", 675 | "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==" 676 | }, 677 | "querystringify": { 678 | "version": "2.2.0", 679 | "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", 680 | "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" 681 | }, 682 | "requires-port": { 683 | "version": "1.0.0", 684 | "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", 685 | "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" 686 | }, 687 | "rootpath": { 688 | "version": "0.1.2", 689 | "resolved": "https://registry.npmjs.org/rootpath/-/rootpath-0.1.2.tgz", 690 | "integrity": "sha1-Wzeah9ypBum5HWkKWZQ5vvJn6ms=" 691 | }, 692 | "safe-buffer": { 693 | "version": "5.2.1", 694 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 695 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 696 | }, 697 | "scmp": { 698 | "version": "2.1.0", 699 | "resolved": "https://registry.npmjs.org/scmp/-/scmp-2.1.0.tgz", 700 | "integrity": "sha512-o/mRQGk9Rcer/jEEw/yw4mwo3EU/NvYvp577/Btqrym9Qy5/MdWGBqipbALgd2lrdWTJ5/gqDusxfnQBxOxT2Q==" 701 | }, 702 | "semver": { 703 | "version": "5.7.1", 704 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 705 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" 706 | }, 707 | "tiny-json-http": { 708 | "version": "7.3.0", 709 | "resolved": "https://registry.npmjs.org/tiny-json-http/-/tiny-json-http-7.3.0.tgz", 710 | "integrity": "sha512-dZrf9ZGZQhe8QhhPN3o4uDCQuBc3Gaq4CtbU/67hQDWXuvjLI1mayr8AOFSiUGa3F818SpIgUnC3mM673VRHGQ==" 711 | }, 712 | "twilio": { 713 | "version": "3.57.0", 714 | "resolved": "https://registry.npmjs.org/twilio/-/twilio-3.57.0.tgz", 715 | "integrity": "sha512-gt1NtEM647c/+KGcPiBEY2YCCrvm7nKXfd2bOT6PNYUAbBF5n0s3Ed0lYQW5BngpJEjeZROMZjfp9ikwehS/pg==", 716 | "requires": { 717 | "axios": "^0.21.1", 718 | "dayjs": "^1.8.29", 719 | "https-proxy-agent": "^5.0.0", 720 | "jsonwebtoken": "^8.5.1", 721 | "lodash": "^4.17.19", 722 | "q": "2.0.x", 723 | "qs": "^6.9.4", 724 | "rootpath": "^0.1.2", 725 | "scmp": "^2.1.0", 726 | "url-parse": "^1.4.7", 727 | "xmlbuilder": "^13.0.2" 728 | } 729 | }, 730 | "typescript": { 731 | "version": "4.2.3", 732 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz", 733 | "integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==", 734 | "dev": true 735 | }, 736 | "url-parse": { 737 | "version": "1.5.1", 738 | "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz", 739 | "integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==", 740 | "requires": { 741 | "querystringify": "^2.1.1", 742 | "requires-port": "^1.0.0" 743 | } 744 | }, 745 | "weak-map": { 746 | "version": "1.0.5", 747 | "resolved": "https://registry.npmjs.org/weak-map/-/weak-map-1.0.5.tgz", 748 | "integrity": "sha1-eWkVhNmGB/UHC9O3CkDmuyLkAes=" 749 | }, 750 | "xmlbuilder": { 751 | "version": "13.0.2", 752 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz", 753 | "integrity": "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==" 754 | } 755 | } 756 | } 757 | -------------------------------------------------------------------------------- /twilio_client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "twilio_client", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "@types/node": "^14.14.34", 14 | "@types/tiny-json-http": "^7.3.0", 15 | "@types/twilio": "^2.11.0", 16 | "typescript": "^4.2.3" 17 | }, 18 | "dependencies": { 19 | "tiny-json-http": "^7.3.0", 20 | "twilio": "^3.57.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /twilio_client/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as http from 'http'; 2 | import * as tiny from 'tiny-json-http'; 3 | import * as fs from 'fs'; 4 | 5 | type Config = { 6 | TWILIO_ACCOUNT_ID: string, 7 | TWILIO_PHONE_NUMBER_SRC: string, 8 | TWILIO_PHONE_NUMBER_DST: string, 9 | TWILIO_RELAY_SERVER: string, 10 | TWILIO_AUTH_TOKEN: string, 11 | } 12 | 13 | const twilio = require('twilio'); // @types/twilio seems to be obsolete? 14 | 15 | const configFile = process.env['TWILIO_JSON']; 16 | if (configFile == undefined) { 17 | throw "environment variable TWILIO_JSON should point to configuration file"; 18 | } 19 | const config = JSON.parse(fs.readFileSync(configFile, 'utf8')) as Config; 20 | 21 | const client = twilio(config.TWILIO_ACCOUNT_ID, config.TWILIO_AUTH_TOKEN); 22 | const url = `http://${config.TWILIO_RELAY_SERVER}/listen`; 23 | const args = process.argv.slice(2); 24 | 25 | async function send_msg(msg: string): Promise { 26 | return client.messages 27 | .create({ 28 | from: config.TWILIO_PHONE_NUMBER_SRC, 29 | to: config.TWILIO_PHONE_NUMBER_DST, 30 | body: msg, 31 | }); 32 | } 33 | 34 | async function go() { 35 | const msg = args[0]; 36 | if (msg !== undefined && msg !== "") { 37 | await send_msg(msg); 38 | } 39 | // receive response 40 | const res = await tiny.get({ url }); 41 | console.log(res.body.message); 42 | } 43 | 44 | go().catch(x => console.log(JSON.stringify({ error: x }))); 45 | -------------------------------------------------------------------------------- /twilio_client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "out", 4 | "target": "es2017", 5 | "module": "commonjs", 6 | "lib": [ "es2017.object", "dom", "es6" ], 7 | "sourceMap": true, 8 | "noFallthroughCasesInSwitch": true, 9 | "noImplicitAny": true, 10 | "strictNullChecks": true, 11 | "strictPropertyInitialization": true, 12 | "esModuleInterop": true 13 | }, 14 | "include": ["src/**/*.ts"] 15 | } 16 | -------------------------------------------------------------------------------- /twilio_server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "express": "^4.17.1", 15 | "twilio": "^3.57.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /twilio_server/server.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const accountSid = process.env['TWILIO_ACCOUNT_ID']; 3 | const authToken = fs.readFileSync('tok', 'utf8'); 4 | const client = require('twilio')(accountSid, authToken); 5 | 6 | const http = require('http'); 7 | const express = require('express'); 8 | 9 | const MessagingResponse = require('twilio').twiml.MessagingResponse; 10 | 11 | const app = express(); 12 | app.use(express.urlencoded({extended: false})); 13 | 14 | // listener: undefined | (x : string) => void 15 | let listener = undefined; 16 | 17 | function notify(message) { 18 | if (listener !== undefined) { 19 | listener(message); 20 | listener = undefined; 21 | } 22 | } 23 | 24 | app.post('/sms', (req, res) => { 25 | 26 | console.log('got a message'); 27 | console.log(req.body); 28 | const message = req.body.Body; 29 | notify(message); 30 | 31 | res.writeHead(200, {'Content-Type': 'text/xml'}); 32 | res.end(''); 33 | }); 34 | 35 | app.get('/listen', (req, res) => { 36 | 37 | console.log('got a listener'); 38 | 39 | listener = (message /*: string*/) => { 40 | res.json({message}); 41 | } 42 | }); 43 | 44 | http.createServer(app).listen(1337, () => { 45 | console.log('Express server listening on port 1337'); 46 | }); 47 | --------------------------------------------------------------------------------