├── .github └── workflows │ ├── dockerize.yaml │ ├── npm-build.yml │ ├── npm-publish.yml │ ├── npm-test.yml │ ├── pg-build.yaml │ ├── server-build.yaml │ └── synchronize-package-lock.yml ├── .gitignore ├── .gitpod.Dockerfile ├── .gitpod.yml ├── .npmignore ├── .nvmrc ├── .vscode └── settings.json ├── Dockerfile ├── LICENSE ├── README.md ├── automation └── remove-build-files.ts ├── benchmarks └── index.ts ├── bundles ├── deeplinks-bundler-postbundle.js ├── deeplinks-bundler-prepare.js ├── jsonschema.js ├── mathjs.js ├── music-metadata.js └── package.json ├── call.mjs ├── compiler └── index.cjs ├── docker-compose.yml ├── files.sh ├── import.ts ├── imports ├── api.ts ├── bool_exp_to_sql.ts ├── client-handler.tsx ├── client.tsx ├── client_types.ts ├── container-controller.ts ├── core-symbols.tsx ├── core.tsx ├── cyber.tsx ├── cyber │ ├── config.ts │ └── types │ │ └── networks.ts ├── debug.ts ├── dirname.ts ├── engine-server.js ├── engine.ts ├── experimental │ └── serial-transitions-builder.ts ├── files.tsx ├── global-ids.ts ├── gql │ ├── index.ts │ ├── mutation.ts │ ├── query.ts │ └── serial.ts ├── handlers.ts ├── jwt.ts ├── minilinks-query.ts ├── minilinks.tsx ├── package.ts ├── packager.ts ├── packages.ts ├── permission.ts ├── promise.ts ├── react-token.tsx ├── reserve.ts ├── reserved.ts ├── router │ ├── guest.ts │ ├── healthz.ts │ ├── index.ts │ ├── jwt.ts │ ├── links.ts │ ├── packager.ts │ ├── reserved-cleaner.ts │ ├── reserved.ts │ ├── scheduler.ts │ └── values.ts ├── traveler.ts └── type-table.ts ├── index.ts ├── integration.test.ts ├── jest.config.ts ├── migrations ├── 1616701513782-links.ts ├── 1616701513790-selectors-cache.ts ├── 1621815803560-auto-indexes.ts ├── 1621815803572-materialized-path.ts ├── 1621815803592-type-mp.ts ├── 1622230000000-reserved-links.ts ├── 1622421760250-values.ts ├── 1622421760256-types.ts ├── 1622421760258-selectors.ts ├── 1622421760259-can.ts ├── 1622421760260-permissions.ts ├── 1623023831753-jwt.ts ├── 1637975150573-packager.ts ├── 1637975150590-promises.ts ├── 1642827746040-demo.ts ├── 1655979260869-sync-handlers.ts ├── 1657720948151-handlers.ts ├── 1658622099992-unvalue.ts ├── 1658909585601-hasura-storage.ts ├── 1661392616544-core-symbols.ts ├── 1664940577200-tsx.ts ├── 1664940577209-deepcase.ts ├── 1677340638677-npm-packager.ts ├── 1678940577209-deepcase.ts ├── 1680017137379-npm-packager-ui.ts ├── 1682327540225-mathjs-schema-packages.ts ├── 1687790060025-finder.ts ├── 1689630159473-healthz.ts ├── 1690737739096-deepcase-opened.ts ├── 1690737739096-main-port.ts ├── 1693460330175-sync-handlers-update.ts ├── 1694386912180-sync-handlers-update-new.ts ├── 1725896812219-unsafe.ts ├── 1725896812619-files.ts ├── 1725896842619-readme.ts ├── 1725896857619-perception.ts ├── 1726136790617-tsx.ts ├── 1726165806644-authorization.ts ├── 1726165830140-passport-username-password.ts ├── 1726413155878-jsonschema.ts ├── 1726413249768-jsonschema-form.ts ├── 1726588950095-semantic.ts ├── 1726588950120-bots.ts ├── 1727169148530-payments.ts ├── 1727169149950-payments-tinkoff-c2b.ts ├── 1727224344983-promise-cleaner.ts ├── 1727234538431-rules.ts ├── 1727618396605-preload.ts ├── 1727714840428-timezone.ts └── 1727714840450-coordinates.ts ├── nodemon.json ├── open-deep.html ├── package-lock.json ├── package.json ├── postgresql └── dockerfile ├── react.test.tsx ├── snapshots ├── create.js └── last.js ├── tests ├── bool_exp.ts ├── client.react.tsx ├── client.tsx ├── cyber_client.tsx ├── demo.ts ├── experimental │ └── client.ts ├── handlers.ts ├── join-insert.ts ├── messanger.ts ├── minilinks-query.ts ├── minilinks.ts ├── packager.ts ├── permissions.ts ├── query.ts ├── reserve.ts ├── selectors.ts ├── sync-handlers.ts ├── tree.ts └── typing.ts ├── tsconfig.json ├── unit.test.ts ├── unix-start.sh ├── warmup.ts └── windows-start.ps1 /.github/workflows/npm-build.yml: -------------------------------------------------------------------------------- 1 | name: Npm Build 2 | 3 | on: 4 | pull_request: 5 | types: [opened, reopened, edited, synchronize] 6 | workflow_dispatch: 7 | 8 | jobs: 9 | main: 10 | uses: deep-foundation/workflows/.github/workflows/npm-build.yml@main -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | name: Npm Publish 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - package.json 9 | - .github/workflows/npm-publish.yml 10 | workflow_dispatch: 11 | 12 | jobs: 13 | main: 14 | uses: deep-foundation/workflows/.github/workflows/npm-publish.yml@npm-publish-4.0.1 15 | secrets: 16 | npm-token: ${{ secrets.NPM_TOKEN }} 17 | with: 18 | build-command: "npm run package:build" 19 | test-command: "echo 'Tests are temporarily disabled'" 20 | should-generate-documentation: true 21 | generate-documentation-command: "npx typedoc --out ./docs --entryPointStrategy expand ./imports" 22 | -------------------------------------------------------------------------------- /.github/workflows/npm-test.yml: -------------------------------------------------------------------------------- 1 | name: Npm Test 2 | 3 | on: 4 | pull_request: 5 | types: [opened, reopened, edited] 6 | workflow_dispatch: 7 | 8 | jobs: 9 | main: 10 | uses: deep-foundation/workflows/.github/workflows/npm-test.yml@main 11 | -------------------------------------------------------------------------------- /.github/workflows/pg-build.yaml: -------------------------------------------------------------------------------- 1 | name: Deploy pg-v8 2 | on: 3 | push: 4 | branches: [ deeplinks-70-menzorg ] 5 | paths: 6 | - postgresql/** 7 | workflow_dispatch: 8 | defaults: 9 | run: 10 | working-directory: ./ 11 | jobs: 12 | build: 13 | name: dockerize 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Log in to Docker Hub 18 | uses: docker/login-action@v1 19 | with: 20 | username: ${{ secrets.DOCKER_HUB_USERNAME }} 21 | password: ${{ secrets.DOCKERHUB_TOKEN }} 22 | - name: Extract metadata (tags, labels) for Docker 23 | id: meta 24 | uses: docker/metadata-action@v3 25 | with: 26 | images: deepf/pg12-plv8 27 | - name: build docker image and push 28 | id: docker_build 29 | uses: docker/build-push-action@v2 30 | with: 31 | context: ./postgresql 32 | push: true 33 | tags: ${{ steps.meta.outputs.tags }} 34 | labels: ${{ steps.meta.outputs.labels }} 35 | - name: Image digest 36 | run: echo ${{ steps.docker_build.outputs.digest }} 37 | -------------------------------------------------------------------------------- /.github/workflows/server-build.yaml: -------------------------------------------------------------------------------- 1 | name: Server build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-22.04 11 | steps: 12 | - name: Checkout code 13 | uses: actions/checkout@v2 14 | 15 | - name: Update dependencies 16 | run: | 17 | sudo apt-get update && sudo apt-get upgrade 18 | 19 | - name: Install dependencies 20 | run: | 21 | sudo apt install -y git curl 22 | 23 | - name: Allow ports 3006 and 3007 24 | run: | 25 | sudo ufw allow 3006 26 | sudo ufw allow 3007 27 | 28 | - name: Install Node Version Manager (NVM) 29 | run: | 30 | curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash 31 | export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")" 32 | [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" 33 | nvm install 18 && nvm use 18 && nvm alias default 18 34 | npm i -g npm@latest 35 | 36 | - name: Install and configure @deep-foundation/deeplinks 37 | run: | 38 | npm rm --unsafe-perm -g @deep-foundation/deeplinks 39 | npm install --unsafe-perm -g @deep-foundation/deeplinks@latest 40 | 41 | - name: Set environment variables 42 | run: | 43 | export HOST_IP="185.105.118.59" 44 | export DEEPCASE_HOST="$HOST_IP:3007" 45 | export DEEPLINKS_HOST="$HOST_IP:3006" 46 | export HASURA_ADMIN_SECRET=$(node -e "console.log(require('crypto').randomBytes(24).toString('hex'));") 47 | export POSTGRES_PASSWORD=$(node -e "console.log(require('crypto').randomBytes(24).toString('hex'));") 48 | export MINIO_ACCESS_KEY=$(node -e "console.log(require('crypto').randomBytes(24).toString('hex'));") 49 | export MINIO_SECRET_KEY=$(node -e "console.log(require('crypto').randomBytes(24).toString('hex'));") 50 | 51 | - name: Create call-options.json 52 | run: | 53 | tee call-options.json << JSON 54 | { 55 | "operation": "run", 56 | "envs": { 57 | "DEEPLINKS_PUBLIC_URL": "http://$DEEPLINKS_HOST", 58 | "NEXT_PUBLIC_DEEPLINKS_URL": "http://$DEEPLINKS_HOST", 59 | "NEXT_PUBLIC_GQL_PATH": "$DEEPLINKS_HOST/gql", 60 | "NEXT_PUBLIC_GQL_SSL": "0", 61 | "NEXT_PUBLIC_DEEPLINKS_SERVER": "http://$DEEPCASE_HOST", 62 | "NEXT_PUBLIC_ENGINES_ROUTE": "0", 63 | "NEXT_PUBLIC_DISABLE_CONNECTOR": "1", 64 | "JWT_SECRET": "'{\"type\":\"HS256\",\"key\":\"$(node -e "console.log(require('crypto').randomBytes(50).toString('base64'));")\"}'", 65 | "DEEPLINKS_HASURA_STORAGE_URL": "http://host.docker.internal:8000/", 66 | "HASURA_GRAPHQL_ADMIN_SECRET": "$HASURA_ADMIN_SECRET", 67 | "MIGRATIONS_HASURA_SECRET": "$HASURA_ADMIN_SECRET", 68 | "DEEPLINKS_HASURA_SECRET": "$HASURA_ADMIN_SECRET", 69 | "POSTGRES_PASSWORD": "$POSTGRES_PASSWORD", 70 | "HASURA_GRAPHQL_DATABASE_URL": "postgres://postgres:$POSTGRES_PASSWORD@postgres:5432/postgres", 71 | "POSTGRES_MIGRATIONS_SOURCE": "postgres://postgres:$POSTGRES_PASSWORD@host.docker.internal:5432/postgres?sslmode=disable", 72 | "RESTORE_VOLUME_FROM_SNAPSHOT": "0", 73 | "MANUAL_MIGRATIONS": "1", 74 | "MINIO_ROOT_USER": "$MINIO_ACCESS_KEY", 75 | "MINIO_ROOT_PASSWORD": "$MINIO_SECRET_KEY", 76 | "S3_ACCESS_KEY": "$MINIO_ACCESS_KEY", 77 | "S3_SECRET_KEY": "$MINIO_SECRET_KEY" 78 | } 79 | } 80 | JSON 81 | 82 | - name: Run deeplinks 83 | run: | 84 | export DEEPLINKS_CALL_OPTIONS=$(cat call-options.json) 85 | export DEBUG="deeplinks:engine:*,deeplinks:migrations:*" 86 | deeplinks 87 | -------------------------------------------------------------------------------- /.github/workflows/synchronize-package-lock.yml: -------------------------------------------------------------------------------- 1 | name: Synchronize package-lock.json 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | 7 | jobs: 8 | main: 9 | uses: deep-foundation/workflows/.github/workflows/synchronize-package-lock.yml@main 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | deep.config.json 2 | log.txt 3 | perception-app 4 | 5 | # Logs 6 | *.log 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | 11 | # Runtime data 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # Optional npm cache directory 42 | .npm 43 | 44 | # Optional eslint cache 45 | .eslintcache 46 | 47 | # Optional REPL history 48 | .node_repl_history 49 | 50 | # Output of 'npm pack' 51 | *.tgz 52 | 53 | # Yarn Integrity file 54 | .yarn-integrity 55 | 56 | # dotenv environment variables file 57 | .env 58 | 59 | # next.js build output 60 | .next 61 | .export 62 | .build 63 | 64 | # OS X temporary files 65 | .DS_Store 66 | 67 | # Rush temporary files 68 | common/deploy/ 69 | common/temp/ 70 | common/autoinstallers/*/.npmrc 71 | **/.rush/temp/ 72 | 73 | # OSX 74 | # 75 | .DS_Store 76 | 77 | # Xcode 78 | # 79 | build/ 80 | *.pbxuser 81 | !default.pbxuser 82 | *.mode1v3 83 | !default.mode1v3 84 | *.mode2v3 85 | !default.mode2v3 86 | *.perspectivev3 87 | !default.perspectivev3 88 | xcuserdata 89 | *.xccheckout 90 | *.moved-aside 91 | DerivedData 92 | *.hmap 93 | *.ipa 94 | ios/App/*.zip 95 | *.xcuserstate 96 | 97 | # Android/IntelliJ 98 | projects/app/android/app/src/main/assets 99 | build/ 100 | .idea 101 | .gradle 102 | local.properties 103 | *.iml 104 | 105 | # fastlane 106 | # 107 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 108 | # screenshots whenever they are needed. 109 | # For more information about the recommended setup visit: 110 | # https://docs.fastlane.tools/best-practices/source-control/ 111 | 112 | **/fastlane/report.xml 113 | **/fastlane/Preview.html 114 | **/fastlane/screenshots 115 | **/fastlane/report.xml 116 | 117 | # CocoaPods 118 | /ios/Pods/ 119 | 120 | # Electron 121 | projects/app/electron/app 122 | 123 | # migrations 124 | .migrate 125 | 126 | # deeplinks 127 | /*.js 128 | /*.js.map 129 | /*.d.ts 130 | /imports/**/*.js 131 | /imports/**/*.js.map 132 | /imports/**/*.d.ts 133 | /migrations/**/*.js 134 | /migrations/**/*.js.map 135 | /migrations/**/*.d.ts 136 | /tests/**/*.js 137 | /tests/**/*.js.map 138 | /tests/**/*.d.ts 139 | /benchmarks/**/*.js 140 | /benchmarks/**/*.js.map 141 | /benchmarks/**/*.d.ts 142 | /automation/**/*.js 143 | /automation/**/*.js.map 144 | /automation/**/*.d.ts 145 | !/imports/engine-server.js 146 | 147 | # snapshots 148 | /snapshots/[0-9]* 149 | /snapshots/[0-9]*.migrate 150 | 151 | 152 | # webpack 153 | !webpack.config.js 154 | 155 | -------------------------------------------------------------------------------- /.gitpod.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gitpod/workspace-full 2 | 3 | # Install custom tools, runtimes, etc. 4 | # For example "bastet", a command-line tetris clone: 5 | # RUN brew install bastet 6 | # 7 | # More information: https://www.gitpod.io/docs/config-docker/ 8 | RUN bash -c 'VERSION="18" \ 9 | && source $HOME/.nvm/nvm.sh && nvm install $VERSION \ 10 | && nvm use $VERSION && nvm alias default $VERSION' 11 | 12 | RUN echo "nvm use default &>/dev/null" >> ~/.bashrc.d/51-nvm-fix -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | image: 2 | file: .gitpod.Dockerfile 3 | 4 | ports: 5 | - port: 3007 6 | onOpen: open-preview 7 | visibility: public 8 | - port: 4000 9 | onOpen: open-browser 10 | visibility: public 11 | - port: 3006 12 | onOpen: ignore 13 | visibility: public 14 | - port: 8080 15 | onOpen: ignore 16 | visibility: public 17 | 18 | vscode: 19 | extensions: 20 | - ms-azuretools.vscode-docker 21 | - graphql.vscode-graphql 22 | - yzhang.markdown-all-in-one 23 | - ms-vscode.vscode-typescript-tslint-plugin 24 | - hediet.vscode-drawio 25 | - ms-azuretools.vscode-docker 26 | 27 | tasks: 28 | - init: | 29 | npm ci 30 | npm run build 31 | npm start -- --generate 32 | npm start -- --up 33 | npm start -- --snapshot -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | *.log 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | 7 | # Runtime data 8 | *.pid 9 | *.seed 10 | *.pid.lock 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # nyc test coverage 19 | .nyc_output 20 | 21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 22 | .grunt 23 | 24 | # Bower dependency directory (https://bower.io/) 25 | bower_components 26 | 27 | # node-waf configuration 28 | .lock-wscript 29 | 30 | # Compiled binary addons (https://nodejs.org/api/addons.html) 31 | build/Release 32 | 33 | # Dependency directories 34 | node_modules/ 35 | jspm_packages/ 36 | 37 | # Optional npm cache directory 38 | .npm 39 | 40 | # Optional eslint cache 41 | .eslintcache 42 | 43 | # Optional REPL history 44 | .node_repl_history 45 | 46 | # Output of 'npm pack' 47 | *.tgz 48 | 49 | # Yarn Integrity file 50 | .yarn-integrity 51 | 52 | # dotenv environment variables file 53 | .env 54 | 55 | # next.js build output 56 | .next 57 | .export 58 | .build 59 | 60 | # OS X temporary files 61 | .DS_Store 62 | 63 | # Rush temporary files 64 | common/deploy/ 65 | common/temp/ 66 | common/autoinstallers/*/.npmrc 67 | **/.rush/temp/ 68 | 69 | # OSX 70 | # 71 | .DS_Store 72 | 73 | # Xcode 74 | # 75 | build/ 76 | *.pbxuser 77 | !default.pbxuser 78 | *.mode1v3 79 | !default.mode1v3 80 | *.mode2v3 81 | !default.mode2v3 82 | *.perspectivev3 83 | !default.perspectivev3 84 | xcuserdata 85 | *.xccheckout 86 | *.moved-aside 87 | DerivedData 88 | *.hmap 89 | *.ipa 90 | ios/App/*.zip 91 | *.xcuserstate 92 | 93 | # Android/IntelliJ 94 | projects/app/android/app/src/main/assets 95 | build/ 96 | .idea 97 | .gradle 98 | local.properties 99 | *.iml 100 | 101 | # fastlane 102 | # 103 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 104 | # screenshots whenever they are needed. 105 | # For more information about the recommended setup visit: 106 | # https://docs.fastlane.tools/best-practices/source-control/ 107 | 108 | **/fastlane/report.xml 109 | **/fastlane/Preview.html 110 | **/fastlane/screenshots 111 | **/fastlane/report.xml 112 | 113 | # CocoaPods 114 | /ios/Pods/ 115 | 116 | # Electron 117 | projects/app/electron/app 118 | 119 | # migrations 120 | .migrate 121 | 122 | 123 | **/*.ts 124 | !**/*.d.ts 125 | **/*.tsx 126 | .github/** -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 18 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib", 3 | "workbench.colorCustomizations": { 4 | "activityBar.background": "#1B2535", 5 | "titleBar.activeBackground": "#1B2535", 6 | "titleBar.activeForeground": "#ffffff", 7 | "activityBar.inactiveForeground": "#BEE3F8", //icon inactive 8 | } 9 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-alpine3.17 AS node 2 | FROM docker:27.2.0-dind-alpine3.20 3 | 4 | COPY --from=node /usr/lib /usr/lib 5 | COPY --from=node /usr/local/share /usr/local/share 6 | COPY --from=node /usr/local/lib /usr/local/lib 7 | COPY --from=node /usr/local/include /usr/local/include 8 | COPY --from=node /usr/local/bin /usr/local/bin 9 | 10 | COPY package.json . 11 | COPY index.js . 12 | COPY index.js.map . 13 | COPY index.ts . 14 | COPY node_modules ./node_modules 15 | COPY imports ./imports 16 | COPY snapshots ./snapshots 17 | COPY backup ./backup 18 | 19 | ENV PORT 3006 20 | ENV DOCKER 1 21 | ENV DEBUG_COLORS true 22 | # ENV DEBUG deeplinks:* 23 | 24 | EXPOSE 3006 25 | ENTRYPOINT ["node", "index.js"] 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm](https://img.shields.io/npm/v/@deep-foundation/deeplinks.svg)](https://www.npmjs.com/package/@deep-foundation/deeplinks) 2 | [![Gitpod](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/deep-foundation/deeplinks) 3 | [![Discord](https://badgen.net/badge/icon/discord?icon=discord&label&color=purple)](https://discord.gg/deep-foundation) 4 | 5 | # Documentation 6 | 7 | ## Container and CLI 8 | 9 | ```sh 10 | # generate deep. config json 11 | npx -y @deep-foundation/deeplinks --generate 12 | # by default auto detect public ip or gitpod urls (gitpod too) 13 | # manual config 14 | npx -y @deep-foundation/deeplinks --generate --deeplinks="https://links.my-domain.io" --perception="https://peception.my-domain.io" 15 | # update and running all containers empty, without links, available for migrations 16 | nрх -y @deep-foundation/deeplinks --up 17 | # apply snapshot generated in gh-actions with basic links and packages 18 | npx -y @deep-foundation/deeplinks --snapshot 19 | # down all containers without removind data from values 20 | npx -y @deep-foundation/deeplinks --down 21 | # down and up with updating docker images in sequence 22 | npx -y @deep-foundation/deeplinks --down --up 23 | 24 | # exec js interactive terminal with deep in variables context 25 | npx -y @deep-foundation/deeplinks --exec 26 | > deep # DeepClient 27 | # instead of using snapshot, do manual migration 28 | npx -y @deep-foundation/deeplinks --migrate 29 | # unmigrate 30 | npx -y @deep-foundation/deeplinks --unmigrate 31 | # print all envs 32 | npx -y @deep-foundation/deeplinks --envs 33 | # exec bash script in envs based on deep.config json in deeplinks directory, for example stop all containers 34 | npx -y @deep-foundation/deeplinks --bash="docker compose -p deep down" 35 | ``` 36 | 37 | ## Imports 38 | 39 | ```ts 40 | import { 41 | useDeep, 42 | DeepProvider, 43 | useDeepGenerator, 44 | DeepClient, 45 | DeepContext, 46 | DeepNamespaceProvider, 47 | DeepNamespaceContext, 48 | useDeepNamespaces, 49 | useDeepNamespace, 50 | useTransparentState, 51 | useDebouncedInput, 52 | useDeepQuery, 53 | useDeepSubscription, 54 | useDeepId, 55 | useSearch, 56 | useAuthNode, 57 | useLink, 58 | useLinks, 59 | random, 60 | parseJwt, 61 | serializeQuery, 62 | serializeWhere, 63 | pathToWhere, 64 | Handler, 65 | Subscription, 66 | Observer, 67 | DeepClientOptions, 68 | DeepClientResult, 69 | DeepSearchOptions, 70 | DeepClientInstance, 71 | DeepClientAuthResult, 72 | DeepClientGuestOptions, 73 | DeepClientJWTOptions, 74 | UseDeepSubscriptionResult, 75 | DeepClientPackageSelector, 76 | DeepClientPackageContain, 77 | DeepClientLinkId, 78 | DeepClientStartItem, 79 | DeepClientPathItem, 80 | SelectTable, 81 | InsertTable, 82 | UpdateTable, 83 | DeleteTable, 84 | OperationType, 85 | SerialOperationType, 86 | Table, 87 | ValueForTable, 88 | ExpForTable, 89 | SerialOperationDetails, 90 | SerialOperation, 91 | DeepSerialOperation, 92 | AsyncSerialParams, 93 | INamespaces, 94 | Exp, 95 | UpdateValue, 96 | InsertObjects, 97 | Options, 98 | ReadOptions, 99 | WriteOptions, 100 | MinilinksLink, 101 | MinilinkCollection, 102 | minilinks, 103 | MinilinksContext, 104 | toPlain, 105 | Minilinks, 106 | useMinilinksConstruct, 107 | useMinilinksFilter, 108 | useMinilinksHandle, 109 | useMinilinksApply, 110 | useMinilinksQuery, 111 | useMinilinksSubscription, 112 | useMinilinksGenerator, 113 | MinilinksProvider, 114 | useMinilinks, 115 | Links, 116 | LinkPlain, 117 | LinkRelations, 118 | LinkHashFields, 119 | Link, 120 | MinilinksQueryOptions, 121 | MinilinksResult, 122 | MinilinksGeneratorOptions, 123 | MinilinksInstance, 124 | MinilinkError, 125 | ApplyReturnOptions, 126 | ApplyOptions, 127 | MinilinksHookInstance, 128 | Id, 129 | MinilinksQueryOptionAggregate, 130 | MinilinksApplyInput, 131 | useTokenController, 132 | TokenProvider, 133 | QueryLink, 134 | QueryLinkReturn, 135 | BoolExp, 136 | ReturnBoolExp, 137 | BoolExpLink, 138 | BoolExpValue, 139 | BoolExpCan, 140 | BoolExpSelector, 141 | BoolExpTree, 142 | BoolExpHandler, 143 | ComparasionExp, 144 | MutationInput, 145 | MutationInputLinkPlain, 146 | MutationInputLink, 147 | MutationInputValue, 148 | Query, 149 | LinkToLinksRelations, 150 | ComparasionType, 151 | CatchErrors, 152 | evalClientHandler, 153 | useFindClientHandler, 154 | ClientHandler, 155 | ClientHandlerRenderer, 156 | ClientHandlerRendererProps, 157 | ClientHandlerProps, 158 | Packages, 159 | } from "@deep-foundation/deeplinks"; 160 | ``` 161 | 162 | ## Manual export/import packages from admin, without using npm 163 | ```js 164 | const dc = '@deep-foundation/core'; 165 | const myPackageId = 1850; 166 | // If you just created Package without PackageVersion and PackageNamespace 167 | await deep.insert({ 168 | type_id: deep.idLocal(dc, 'PackageVersion'), 169 | from: { type_id: deep.idLocal(dc, 'PackageNamespace') }, 170 | to_id: myPackageId, 171 | string: '0.0.1', 172 | }); 173 | const packager = deep.Packager(); 174 | const pckg = await packager.export({ packageLinkId: myPackageId }); 175 | // Save it manually for example JSON.stringify(pckg); 176 | // Import again in other system 177 | await packager.import(pckg); 178 | // Coming soon packager.update({ packageLinkId: myPackageId, pckg }); 179 | ``` 180 | 181 | ## Write and read packages locally for example on server 182 | ```bash 183 | npx @deep-foundation/deeplinks --exec --localhost 184 | ``` 185 | ```js 186 | const packages = deep.Packages(); 187 | // export and write 188 | const exported = await packages.export(); 189 | // { [name@version]: Package } // possible Package.errors 190 | await packages.write(process.cwd(), exported); 191 | // read and import 192 | const readed = await packages.read(process.cwd()); 193 | // { [name@version]: Package } 194 | const imported = await packages.import(readed); 195 | // { [name@version]: Package } // possible Package.errors 196 | 197 | // shorthand 198 | await deep.Packages().write(process.cwd(), await deep.Packages().export()); 199 | ``` -------------------------------------------------------------------------------- /automation/remove-build-files.ts: -------------------------------------------------------------------------------- 1 | export {} 2 | 3 | // import glob from "glob"; 4 | // import fs from "fs-extra"; 5 | 6 | // const options = { 7 | // ignore: "**/node_modules/**", 8 | // nodir: true, 9 | // }; 10 | 11 | // glob("./**/*.{d.ts,js,js.map}", options, (err, files) => { 12 | // if (err) { 13 | // console.log(err); 14 | // return; 15 | // } 16 | 17 | // files.forEach((file) => { 18 | // fs.remove(file, (err) => { 19 | // if (err) console.error(`Failed to remove ${file}:`, err); 20 | // else console.log(`Successfully removed ${file}`); 21 | // }); 22 | // }); 23 | // }); 24 | -------------------------------------------------------------------------------- /benchmarks/index.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import { Suite } from 'benchmark'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import _ from 'lodash'; 5 | import Debug from 'debug'; 6 | 7 | const debug = Debug('deeplinks:benchmarks'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | // Force enable this file errors output 11 | 12 | const delay = time => new Promise(res => setTimeout(res, time)); 13 | 14 | const apolloClient = generateApolloClient({ 15 | path: `${process.env.DEEPLINKS_HASURA_PATH}/v1/graphql`, 16 | ssl: !!+process.env.DEEPLINKS_HASURA_SSL, 17 | secret: process.env.DEEPLINKS_HASURA_SECRET, 18 | }); 19 | 20 | const deep = new DeepClient({ apolloClient }); 21 | 22 | (async () => { 23 | var suite = new Suite(); 24 | const admin = await deep.jwt({ linkId: await deep.id('deep', 'admin') }); 25 | const deepAdmin = new DeepClient({ deep: deep, token: admin.token, linkId: admin.linkId }); 26 | const Query = deep.idLocal('@deep-foundation/core', 'Query'); 27 | const guest = await deep.guest({}); 28 | const deepGuest = new DeepClient({ deep: deepAdmin, ...guest }); 29 | 30 | await deepAdmin.insert({ 31 | type_id: deep.idLocal('@deep-foundation/core', 'Rule'), 32 | out: { data: [ 33 | { 34 | type_id: deep.idLocal('@deep-foundation/core', 'RuleSubject'), 35 | to: { data: { 36 | type_id: deep.idLocal('@deep-foundation/core', 'Selector'), 37 | out: { data: [ 38 | { 39 | type_id: deep.idLocal('@deep-foundation/core', 'SelectorInclude'), 40 | to_id: guest.linkId, 41 | out: { data: { 42 | type_id: deep.idLocal('@deep-foundation/core', 'SelectorTree'), 43 | to_id: deep.idLocal('@deep-foundation/core', 'containTree'), 44 | } }, 45 | }, 46 | ] } 47 | } } 48 | }, 49 | { 50 | type_id: deep.idLocal('@deep-foundation/core', 'RuleObject'), 51 | to: { data: { 52 | type_id: deep.idLocal('@deep-foundation/core', 'Selector'), 53 | out: { data: { 54 | type_id: deep.idLocal('@deep-foundation/core', 'SelectorInclude'), 55 | to_id: deep.idLocal('@deep-foundation/core'), 56 | out: { data: { 57 | type_id: deep.idLocal('@deep-foundation/core', 'SelectorTree'), 58 | to_id: deep.idLocal('@deep-foundation/core', 'containTree'), 59 | } }, 60 | } } 61 | } } 62 | }, 63 | { 64 | type_id: deep.idLocal('@deep-foundation/core', 'RuleAction'), 65 | to: { data: { 66 | type_id: deep.idLocal('@deep-foundation/core', 'Selector'), 67 | out: { data: { 68 | type_id: deep.idLocal('@deep-foundation/core', 'SelectorInclude'), 69 | to_id: deep.idLocal('@deep-foundation/core', 'AllowInsertType'), 70 | out: { data: { 71 | type_id: deep.idLocal('@deep-foundation/core', 'SelectorTree'), 72 | to_id: deep.idLocal('@deep-foundation/core', 'containTree'), 73 | } }, 74 | } } 75 | } } 76 | }, 77 | ] }, 78 | }); 79 | 80 | await (new Promise((res) => { 81 | suite.add('3000', { defer: true, fn: async function(deferred) { 82 | await delay(3000); 83 | deferred.resolve(); 84 | } }); 85 | suite.add('by deepRoot.id', { defer: true, fn: async function(deferred) { 86 | deep.idLocal('@deep-foundation/core', 'Promise'); 87 | deferred.resolve(); 88 | } }); 89 | // suite.add('by deepGuest.id', { defer: true, fn: async function(deferred) { 90 | // await deepGuest.id('@deep-foundation/core', 'Promise'); 91 | // deferred.resolve(); 92 | // } }); 93 | suite.add('deepAdmin.insert { type: Any } x1/1tr', { defer: true, fn: async function(deferred) { 94 | await deepAdmin.insert({ type_id: Query }); 95 | deferred.resolve(); 96 | } }); 97 | suite.add('deepAdmin.insert { type: Any } x100/1tr', { defer: true, fn: async function(deferred) { 98 | await deepAdmin.insert(_.times(100, (t) => ({ type_id: Query }))); 99 | deferred.resolve(); 100 | } }); 101 | suite.add('deepAdmin.insert { type: Any } x1000/1tr', { defer: true, fn: async function(deferred) { 102 | await deepAdmin.insert(_.times(1000, (t) => ({ type_id: Query }))); 103 | deferred.resolve(); 104 | } }); 105 | // suite.add('deepGuest.insert { type: Any } x1/1tr', { defer: true, fn: async function(deferred) { 106 | // await deepGuest.insert({ type_id: Query }); 107 | // deferred.resolve(); 108 | // } }); 109 | // suite.add('deepGuest.insert { type: Any } x100/1tr', { defer: true, fn: async function(deferred) { 110 | // await deepGuest.insert(_.times(100, (t) => ({ type_id: Query }))); 111 | // deferred.resolve(); 112 | // } }); 113 | // suite.add('deepGuest.insert { type: Any } x1000/1tr', { defer: true, fn: async function(deferred) { 114 | // await deepGuest.insert(_.times(1000, (t) => ({ type_id: Query }))); 115 | // deferred.resolve(); 116 | // } }); 117 | 118 | suite.on('cycle', function(event) { 119 | log(String(event.target)); 120 | }); 121 | suite.on('complete', function() { 122 | log('Fastest is ' + this.filter('fastest').map('name')); 123 | res(undefined); 124 | }); 125 | // run async 126 | suite.run({ 'async': false }); 127 | })); 128 | })(); -------------------------------------------------------------------------------- /bundles/deeplinks-bundler-postbundle.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | 4 | const libName = process.env.LIBNAME; 5 | const bundlePath = path.resolve(__dirname,`../bundles/${libName}.js`); 6 | 7 | if (fs.readFileSync(bundlePath, { encoding: 'utf8', flag: 'r' }).slice(-18) !== 'console.log("ok");'){ 8 | let bundle = fs.readFileSync(bundlePath).toString().split('\n'); 9 | bundle.unshift('const bundles__package = ()=>{let bundles__exports;'); 10 | fs.writeFileSync(bundlePath, bundle.join('\n')); 11 | fs.appendFileSync(bundlePath, 'return bundles__exports;}\nexports.code = bundles__package.toString();\nconsole.log(eval(exports.code)());'); 12 | } -------------------------------------------------------------------------------- /bundles/deeplinks-bundler-prepare.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | 4 | const libName = process.env.LIBNAME; 5 | 6 | const packageJson = require(path.resolve(__dirname,`../node_modules/${libName}/package.json`)); 7 | let packagePath; 8 | if (packageJson.main){ 9 | packagePath = packageJson.main.slice(-3) === '.js' ? packageJson.main : `${packageJson.main}/index.js`; 10 | } else { 11 | if (packageJson.exports?.['.']) 12 | packagePath = packageJson.exports?.['.']?.default ? packageJson.exports?.['.']?.default : packageJson.exports?.['.']?.node; 13 | } 14 | 15 | const libPath = path.resolve(__dirname, `../node_modules/${process.env.LIBNAME}`, packagePath); 16 | 17 | const libText = fs.readFileSync(libPath, { encoding: 'utf8', flag: 'r' }); 18 | 19 | if (libText.indexOf('module.exports') !== -1){ 20 | if (libText.slice(-9) !== "//bundled"){ 21 | fs.appendFileSync(libPath, '\nbundles__exports = module.exports; //bundled'); 22 | } 23 | } else { 24 | import(libPath).then( imported => { 25 | const allExports = Object.keys(imported); 26 | console.log({allExports}) 27 | if (libText.slice(-9) !== `//bundled`){ 28 | fs.appendFileSync(libPath, `\nbundles__exports = { ${allExports.join(', ')} }; //bundled`); 29 | } 30 | }); 31 | } 32 | 33 | -------------------------------------------------------------------------------- /bundles/package.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /compiler/index.cjs: -------------------------------------------------------------------------------- 1 | const config = require('../tsconfig.json'); 2 | config.compilerOptions.module = 'commonjs'; 3 | require('ts-node').register(config); 4 | require('dotenv').config(); 5 | module.exports = () => {}; 6 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | postgres: 3 | container_name: deep-postgres 4 | image: deepf/pg12-plv8:deeplinks-70-menzorg 5 | networks: 6 | - network 7 | ports: 8 | - "5432:5432" 9 | restart: always 10 | command: postgres -c max_connections=200 -c log_error_verbosity=TERSE -c client_min_messages=DEBUG5 -c log_min_messages=DEBUG5 -c log_min_error_statement=DEBUG5 -c log_duration=true 11 | volumes: 12 | - db-data:/var/lib/postgresql/data 13 | environment: 14 | - 'POSTGRES_USER=${DEEP_POSTGRES_USER}' 15 | - 'POSTGRES_PASSWORD=${DEEP_POSTGRES_PASSWORD}' 16 | - 'PGGSSENCMODE=${DEEP_POSTGRES_GSS_ENCODING_MODE}' 17 | - 'PGSSLMODE=${DEEP_POSTGRES_SSL_MODE}' 18 | - 'PGREQUIRESSL=${DEEP_POSTGRES_REQUIRE_SSL}' 19 | logging: 20 | driver: "json-file" 21 | options: 22 | max-size: 10m 23 | max-file: "3" 24 | hasura: 25 | container_name: deep-hasura 26 | image: hasura/graphql-engine:v2.43.0 27 | networks: 28 | - network 29 | ports: 30 | - "8080:8080" 31 | extra_hosts: ['host.docker.internal:host-gateway'] 32 | links: 33 | - "postgres:postgres" 34 | depends_on: 35 | - "postgres" 36 | restart: always 37 | environment: 38 | - 'HASURA_GRAPHQL_DATABASE_URL=${DEEP_HASURA_GRAPHQL_DATABASE_URL}' 39 | - 'HASURA_GRAPHQL_ENABLE_CONSOLE=${DEEP_HASURA_GRAPHQL_ENABLE_CONSOLE}' 40 | - 'HASURA_GRAPHQL_DEV_MODE=${DEEP_HASURA_GRAPHQL_DEV_MODE}' 41 | - 'HASURA_GRAPHQL_LOG_LEVEL=${DEEP_HASURA_GRAPHQL_LOG_LEVEL}' 42 | - 'HASURA_GRAPHQL_ENABLED_LOG_TYPES=${DEEP_HASURA_GRAPHQL_ENABLED_LOG_TYPES}' 43 | - 'HASURA_GRAPHQL_ADMIN_SECRET=${DEEP_HASURA_GRAPHQL_ADMIN_SECRET}' 44 | - 'HASURA_GRAPHQL_JWT_SECRET=${DEEP_HASURA_GRAPHQL_JWT_SECRET}' 45 | - 'HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS=${DEEP_HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS}' 46 | - 'HASURA_GRAPHQL_UNAUTHORIZED_ROLE=${DEEP_HASURA_GRAPHQL_UNAUTHORIZED_ROLE}' 47 | logging: 48 | driver: "json-file" 49 | options: 50 | max-size: 10m 51 | max-file: "3" 52 | storage: 53 | container_name: deep-hasura-storage 54 | image: nhost/hasura-storage:0.6.1 55 | networks: 56 | - network 57 | depends_on: 58 | - hasura 59 | restart: always 60 | ports: 61 | - "${DEEP_HASURA_STORAGE_PORT}:8000" 62 | extra_hosts: ['host.docker.internal:host-gateway'] 63 | environment: 64 | - 'PORT=8000' 65 | - 'DEBUG:1' 66 | # - 'HASURA_METADATA=1' 67 | - 'HASURA_ENDPOINT=${DEEP_HASURA_STORAGE_HASURA_ENDPOINT}' 68 | - 'HASURA_GRAPHQL_ADMIN_SECRET=${DEEP_HASURA_STORAGE_HASURA_GRAPHQL_ADMIN_SECRET}' 69 | - 'S3_ENDPOINT: http://deep-minio:9000' 70 | - 'S3_ACCESS_KEY: ${DEEP_MINIO_ROOT_USER}' 71 | - 'S3_SECRET_KEY: ${DEEP_MINIO_ROOT_PASSWORD}' 72 | - 'S3_BUCKET: "default"' 73 | - 'S3_ROOT_FOLDER: "f215cf48-7458-4596-9aa5-2159fc6a3caf"' 74 | - 'POSTGRES_MIGRATIONS=0' 75 | - 'POSTGRES_MIGRATIONS_SOURCE=${DEEP_HASURA_STORAGE_POSTGRES_MIGRATIONS_SOURCE}' 76 | - 'DATABASE_URL=${DEEP_HASURA_STORAGE_POSTGRES_MIGRATIONS_SOURCE}' 77 | - 'GRAPHQL_ENGINE_BASE_URL=${DEEP_HASURA_STORAGE_HASURA_ENDPOINT}' 78 | - 'GRAPHQL_ENDPOINT=${DEEP_HASURA_STORAGE_HASURA_ENDPOINT}' 79 | - 'JWT_SECRET=${DEEP_HASURA_GRAPHQL_JWT_SECRET}' 80 | # - 'CLAMAV_SERVER: tcp://deep-clamd:3310' 81 | command: serve 82 | logging: 83 | driver: "json-file" 84 | options: 85 | max-size: 10m 86 | max-file: "3" 87 | minio: 88 | container_name: deep-minio 89 | image: minio/minio:RELEASE.2023-09-30T07-02-29Z 90 | networks: 91 | - network 92 | restart: always 93 | volumes: 94 | - 'minio-data:/export' 95 | - 'minio-config:/root/.minio' 96 | ports: 97 | - "${DEEP_MINIO_PORT}:9000" 98 | - "${DEEP_MINIO_CONSOLE_PORT}:32765" 99 | environment: 100 | - 'MINIO_ROOT_USER=${DEEP_MINIO_ROOT_USER}' 101 | - 'MINIO_ROOT_PASSWORD=${DEEP_MINIO_ROOT_PASSWORD}' 102 | entrypoint: sh 103 | command: '-c ''mkdir -p /export/default && /opt/bin/minio server --address 0.0.0.0:9000 --console-address 0.0.0.0:32765 /export''' 104 | logging: 105 | driver: "json-file" 106 | options: 107 | max-size: 10m 108 | max-file: "3" 109 | links: 110 | container_name: deep-links 111 | image: deepf/deeplinks:main 112 | networks: 113 | - network 114 | ports: 115 | - "3006:3006" 116 | restart: always 117 | pull_policy: always 118 | volumes: 119 | - /var/run/docker.sock:/var/run/docker.sock 120 | extra_hosts: ['host.docker.internal:host-gateway'] 121 | environment: 122 | - 'DEEPLINKS_HASURA_PATH=${DEEPLINKS_HASURA_PATH}' 123 | - 'DEEPLINKS_HASURA_SSL=${DEEPLINKS_HASURA_SSL}' 124 | - 'DEEPLINKS_HASURA_SECRET=${DEEPLINKS_HASURA_SECRET}' 125 | - 'JWT_SECRET=${DEEP_HASURA_GRAPHQL_JWT_SECRET}' 126 | - 'DEEPLINKS_HASURA_STORAGE_URL=${DEEPLINKS_HASURA_STORAGE_URL}' 127 | - 'DEEPLINKS_PUBLIC_URL=${DEEPLINKS_PUBLIC_URL}' 128 | logging: 129 | driver: "json-file" 130 | options: 131 | max-size: 10m 132 | max-file: "3" 133 | perception: 134 | container_name: deep-perception 135 | image: deepf/perception:main 136 | networks: 137 | - network 138 | ports: 139 | - "3007:3007" 140 | restart: always 141 | pull_policy: always 142 | extra_hosts: ['host.docker.internal:host-gateway'] 143 | environment: 144 | - 'NEXT_PUBLIC_GRAPHQL_URL=${NEXT_PUBLIC_GQL_PATH}' 145 | - 'SECRET=${DEEPLINKS_HASURA_SECRET}' 146 | - 'GQL=${DOCKER_GQL_URL}' 147 | - 'SSL=${NEXT_PUBLIC_GQL_SSL}' 148 | logging: 149 | driver: "json-file" 150 | options: 151 | max-size: 10m 152 | max-file: "3" 153 | # clamd: 154 | # container_name: deep-clamd 155 | # image: nhost/clamav:0.1.2 156 | # restart: unless-stopped 157 | # ports: 158 | # - '3310:3310' 159 | volumes: 160 | db-data: 161 | name: deep-db-data 162 | minio-data: 163 | name: deep-minio-data 164 | minio-config: 165 | name: deep-minio-config 166 | networks: 167 | network: 168 | name: deep-network 169 | driver: bridge -------------------------------------------------------------------------------- /files.sh: -------------------------------------------------------------------------------- 1 | URL=http://localhost:3006/file 2 | AUTH="Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczovL2hhc3VyYS5pby9qd3QvY2xhaW1zIjp7IngtaGFzdXJhLWFsbG93ZWQtcm9sZXMiOlsiYWRtaW4iXSwieC1oYXN1cmEtZGVmYXVsdC1yb2xlIjoiYWRtaW4iLCJ4LWhhc3VyYS11c2VyLWlkIjoiMzYyIn0sImlhdCI6MTY2NzU4NjAyMX0.M37W4jo-8zwEpXt0yTJ-bUvKA0OGWSqinlX1KreiA3o" 3 | BUCKET=default 4 | 5 | FILE_ID=55af1e60-0f28-454e-885e-ea6aab2bb285 6 | ETAG=\"588be441fe7a59460850b0aa3e1c5a65\" 7 | 8 | # we sleep for 1s to make sure a drift in the clocks between client/server doesn't 9 | # lead to a JWTIssuedAtFuture error 10 | sleep 1 11 | 12 | output=`curl http://localhost:3006/file \ 13 | -v \ 14 | -H "Content-Type: multipart/form-data" \ 15 | -H "linkId: 652" \ 16 | -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczovL2hhc3VyYS5pby9qd3QvY2xhaW1zIjp7IngtaGFzdXJhLWFsbG93ZWQtcm9sZXMiOlsiYWRtaW4iXSwieC1oYXN1cmEtZGVmYXVsdC1yb2xlIjoiYWRtaW4iLCJ4LWhhc3VyYS11c2VyLWlkIjoiMzYyIn0sImlhdCI6MTY2NzU4NjAyMX0.M37W4jo-8zwEpXt0yTJ-bUvKA0OGWSqinlX1KreiA3o" \ 17 | -F "metadata[]={\"id\":\"55af1e60-0f28-454e-885e-ea6aab2bb285\", \"name\":\"test.txt\"};type=application/json" \ 18 | -F "file[]=@./test.txt"` 19 | 20 | echo $output 21 | 22 | # time curl -v -o test0.jpg $URL/${FILE_ID} \ 23 | # -H "$AUTH" 24 | 25 | # time curl -v -o test1.jpg $URL/${FILE_ID}?w=600\&h\=200\&q=50\&b=5 \ 26 | # -H "$AUTH" 27 | -------------------------------------------------------------------------------- /import.ts: -------------------------------------------------------------------------------- 1 | export { useDeep, DeepProvider, useDeepGenerator, DeepClient, DeepContext, DeepNamespaceProvider, DeepNamespaceContext, useDeepNamespaces, useDeepNamespace, useTransparentState, useDebouncedInput, useDeepQuery, useDeepSubscription, useDeepId, useSearch, useAuthNode, useLink, useLinks, random, parseJwt, serializeQuery, serializeWhere, pathToWhere, useCan, getServerSidePropsDeep, useDeepPath, useDeepToken } from "./imports/client.js"; 2 | export type { Handler, SubscriptionI as Subscription, Observer, DeepClientOptions, DeepClientResult, DeepSearchOptions, DeepClientInstance, DeepClientAuthResult, DeepClientGuestOptions, DeepClientJWTOptions, UseDeepSubscriptionResult, DeepClientPackageSelector, DeepClientPackageContain, DeepClientLinkId, DeepClientStartItem, DeepClientPathItem, SelectTable, InsertTable, UpdateTable, DeleteTable, OperationType, SerialOperationType, Table, ValueForTable, ExpForTable, SerialOperationDetails, SerialOperation, DeepSerialOperation, AsyncSerialParams, INamespaces, Exp, UpdateValue, InsertObjects, Options, ReadOptions, WriteOptions } from "./imports/client.js"; 3 | 4 | export { MinilinksLink, MinilinkCollection, minilinks, MinilinksContext, toPlain, Minilinks, useMinilinksConstruct, useMinilinksFilter, useMinilinksHandle, useMinilinksApply, useMinilinksQuery, useMinilinksSubscription, useMinilinksGenerator, MinilinksProvider, useMinilinks, useMinilinksId } from "./imports/minilinks.js"; 5 | export type { Links, LinkPlain, LinkRelations, LinkHashFields, Link, MinilinksQueryOptions, MinilinksResult, MinilinksGeneratorOptions, MinilinksInstance, MinilinkError, ApplyReturnOptions, ApplyOptions, MinilinksHookInstance, Id, MinilinksQueryOptionAggregate, MinilinksApplyInput } from "./imports/minilinks.js"; 6 | 7 | export { useTokenController, TokenProvider } from "./imports/react-token.js"; 8 | 9 | export type { QueryLink, QueryLinkReturn, BoolExp, ReturnBoolExp, BoolExpLink, BoolExpValue, BoolExpCan, BoolExpSelector, BoolExpTree, BoolExpHandler, ComparasionExp, MutationInput, MutationInputLinkPlain, MutationInputLink, MutationInputValue, Query, LinkToLinksRelations, ComparasionType } from "./imports/client_types.js"; 10 | 11 | export { CatchErrors, evalClientHandler, useFindClientHandler, ClientHandler, ClientHandlerRenderer } from "./imports/client-handler.js"; 12 | export type { ClientHandlerRendererProps, ClientHandlerProps } from "./imports/client-handler.js"; 13 | 14 | export { Packages } from "./imports/packages.js"; 15 | 16 | export { Files, useFiles, base64ToFile, fileToBase64 } from "./imports/files.js"; 17 | 18 | export { Packager } from './imports/packager.js'; -------------------------------------------------------------------------------- /imports/api.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface IExportTypeOptions { 3 | name: string; 4 | version: string; 5 | } 6 | 7 | export interface IImportTypeOptions { 8 | name: string; 9 | version: string; 10 | } 11 | 12 | export const DeeplinksPackerApi = () => { 13 | return { 14 | register: () => {}, 15 | exportType: (option: IExportTypeOptions): Promise | any => {}, 16 | importType: (option: IImportTypeOptions): Promise | any => {}, 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /imports/bool_exp_to_sql.ts: -------------------------------------------------------------------------------- 1 | import Debug from 'debug'; 2 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 3 | import { HasuraApi } from '@deep-foundation/hasura/api.js'; 4 | import { generateMutation, generateSerial } from './gql/index.js'; 5 | import { DeepClient } from './client.js'; 6 | import { Id } from './minilinks.js'; 7 | import { serializeError } from 'serialize-error'; 8 | 9 | const debug = Debug('deeplinks:bool_exp'); 10 | const log = debug.extend('log'); 11 | const error = debug.extend('error'); 12 | // Force enable this file errors output 13 | 14 | export const api = new HasuraApi({ 15 | path: process.env.DEEPLINKS_HASURA_PATH, 16 | ssl: !!+process.env.DEEPLINKS_HASURA_SSL, 17 | secret: process.env.DEEPLINKS_HASURA_SECRET, 18 | }); 19 | 20 | const client = generateApolloClient({ 21 | path: `${process.env.DEEPLINKS_HASURA_PATH}/v1/graphql`, 22 | ssl: !!+process.env.DEEPLINKS_HASURA_SSL, 23 | secret: process.env.DEEPLINKS_HASURA_SECRET, 24 | }); 25 | 26 | const deep = new DeepClient({ 27 | apolloClient: client, 28 | }) 29 | 30 | export const itemReplaceSymbol = 777777777777; 31 | export const userReplaceSymbol = 777777777778; 32 | export const fromReplaceSymbol = 777777777779; 33 | export const toReplaceSymbol = 777777777780; 34 | export const typeReplaceSymbol = 777777777781; 35 | export const itemPublicSymbol = 'X-Deep-Item-Id'; 36 | export const userPublicSymbol = 'X-Deep-User-Id'; 37 | export const fromPublicSymbol = 'X-Deep-From-Id'; 38 | export const toPublicSymbol = 'X-Deep-To-Id'; 39 | export const typePublicSymbol = 'X-Deep-Type-Id'; 40 | 41 | export const applyBoolExpToLink = (sql: string, linkId: Id) => { 42 | return sql.replace(`${itemReplaceSymbol}`, `${linkId}`); 43 | }; 44 | 45 | export const boolExpToSQL = async (boolExpId: Id, boolExpValue: any) => { 46 | log('boolExpToSQL', boolExpId, boolExpValue); 47 | let gql, explained, sql; 48 | try { 49 | if (typeof(boolExpValue) !== 'object') { 50 | throw new Error('boolExpValue must be object'); 51 | } 52 | const serializedQuery = deep.serializeQuery(boolExpValue); 53 | gql = JSON.stringify(serializedQuery?.where).replace(/"([^"]+)":/g, '$1:'); 54 | gql = gql.replace(new RegExp(`'${userPublicSymbol}'`, 'g'), userReplaceSymbol); 55 | gql = gql.replace(new RegExp(`"${userPublicSymbol}"`, 'g'), userReplaceSymbol); 56 | gql = gql.replace(new RegExp(`'${itemPublicSymbol}'`, 'g'), itemReplaceSymbol); 57 | gql = gql.replace(new RegExp(`"${fromPublicSymbol}"`, 'g'), fromReplaceSymbol); 58 | gql = gql.replace(new RegExp(`"${toPublicSymbol}"`, 'g'), toReplaceSymbol); 59 | gql = gql.replace(new RegExp(`"${typePublicSymbol}"`, 'g'), typeReplaceSymbol); 60 | explained = await api.explain(`{ links(where: { _and: [{ id: { _eq: ${itemReplaceSymbol} } }, ${gql}] }, limit: 1) { id } }`); 61 | sql = explained?.data?.[0]?.sql; 62 | if (sql) { 63 | const convertedSql = `SELECT json_array_length("root") as "root" FROM (${sql}) as "root"` 64 | const boolExp = await deep.select({ link_id: { _eq: boolExpId } }, { table: 'bool_exp' as any, returning: 'id link_id' }); 65 | const boolExpLink = boolExp?.data?.[0]; 66 | log('boolExpLink', boolExpLink); 67 | if (boolExpLink) { 68 | const mutateResult = await deep.update(boolExpLink.id, { 69 | value: convertedSql, 70 | }, { table: 'bool_exp' as any }); 71 | } else { 72 | const mutateResult = await deep.insert({ 73 | link_id: boolExpId, 74 | value: convertedSql, 75 | }, { table: 'bool_exp' as any }); 76 | } 77 | } 78 | } catch (e) { 79 | const serializedError = serializeError(e); 80 | error(JSON.stringify(serializedError, null, 2)); 81 | error('error', gql, explained, sql); 82 | } 83 | }; -------------------------------------------------------------------------------- /imports/cyber/types/networks.ts: -------------------------------------------------------------------------------- 1 | export const enum Networks { 2 | BOSTROM = 'bostrom', 3 | SPACE_PUSSY = 'space-pussy', 4 | ETH = 'eth', 5 | OSMO = 'osmo', 6 | TERRA = 'terra', 7 | COSMOS = 'cosmoshub-4', 8 | } 9 | 10 | export type NetworkConfig = { 11 | CHAIN_ID: Networks; 12 | DENOM_CYBER: string; 13 | DENOM_LIQUID_TOKEN: string; 14 | DENOM_CYBER_G: string; 15 | CYBER_NODE_URL_API: string; 16 | CYBER_WEBSOCKET_URL: string; 17 | CYBER_NODE_URL_LCD: string; 18 | CYBER_INDEX_HTTPS: string; 19 | CYBER_INDEX_WEBSOCKET: string; 20 | BECH32_PREFIX_ACC_ADDR_CYBER: string; 21 | BECH32_PREFIX_ACC_ADDR_CYBERVALOPER: string; 22 | MEMO_KEPLR: string; 23 | }; 24 | 25 | export type NetworksList = { 26 | [key in Networks]: NetworkConfig; 27 | }; 28 | -------------------------------------------------------------------------------- /imports/debug.ts: -------------------------------------------------------------------------------- 1 | import innerDebug from 'debug' 2 | 3 | export const debug = innerDebug(`@deep-foundation/deeplinks`); -------------------------------------------------------------------------------- /imports/dirname.ts: -------------------------------------------------------------------------------- 1 | export default __dirname; -------------------------------------------------------------------------------- /imports/experimental/serial-transitions-builder.ts: -------------------------------------------------------------------------------- 1 | import { DeepClient, DeepClientResult, Table } from "../client"; 2 | import { MutationInputLink } from "../client_types"; 3 | import { deleteMutation, generateSerial, IGenerateMutationBuilder, insertMutation, ISerialOptions, updateMutation } from "../gql"; 4 | import { Id } from "../minilinks"; 5 | 6 | /** 7 | * A class for building serial transitions 8 | * 9 | * @example 10 | ```ts 11 | const builder = new SerialTransitionsBuilder({ deep }); 12 | const table = 'links'; 13 | const transition = [null, { 14 | type_id: deep.id("@deep-foundation/core", "Type") 15 | }]; 16 | const {data} = 17 | builder 18 | .append({ table, transition, alias: firstLinkAlias }) 19 | .append({ table, transition, alias: secondLinkAlias }) 20 | .execute() 21 | const firstLink = data[firstLinkAlias] // or data[0] 22 | ``` 23 | */ 24 | export class SerialTransitionsBuilder { 25 | private deep: DeepClient; 26 | private serialActions: Array; 27 | private defaultTable: Table<'insert' | 'update' | 'delete'>; 28 | private executeOptions: ExecuteOptions; 29 | 30 | constructor(options: SerialTransitionsBuilderOptions) { 31 | this.deep = options.deep; 32 | this.serialActions = []; 33 | this.defaultTable = options.defaultTable ?? 'links'; 34 | this.executeOptions = options.executeOptions ?? {}; 35 | } 36 | 37 | public append(options: AppendTransitionOptions) { 38 | this.appendMultiple([options]) 39 | return this; 40 | } 41 | 42 | public appendMultiple(options: AppendTransitionOptions[]) { 43 | for (const optionsItem of options) { 44 | const { table = this.defaultTable, transition } = optionsItem; 45 | const transitionType = this.getTransitionType(transition) 46 | const index = this.serialActions.length 47 | let serialAction: SerialAction; 48 | switch (transitionType) { 49 | case 'insert': 50 | serialAction = { 51 | mutation: insertMutation(table, { objects: transition[1] }), 52 | index, 53 | transitionType, 54 | table 55 | } 56 | break; 57 | case 'update': 58 | serialAction = { 59 | mutation: updateMutation(table, { exp: transition[0], value: transition[1] }), 60 | index, 61 | transitionType, 62 | table 63 | } 64 | break; 65 | case 'delete': 66 | serialAction = { 67 | mutation: deleteMutation(table, { exp: transition[0] }), 68 | index, 69 | transitionType, 70 | table 71 | } 72 | default: 73 | throw new Error('Invalid transition type. If you want to insert link - the first element must be null and the second must be link. If you want to update link - the first and second elements must be links. If you want to delete link - the first element must be link and second must be null') 74 | } 75 | serialAction.alias = optionsItem.alias ?? `${transitionType}_${table}_${index}`; 76 | this.serialActions.push(serialAction) 77 | } 78 | return this; 79 | } 80 | 81 | public clear() { 82 | this.serialActions = []; 83 | return this; 84 | } 85 | 86 | public async execute(options: ExecuteOptions = this.executeOptions): Promise>> { 87 | const result = await this.deep.apolloClient.mutate(generateSerial({ 88 | actions: this.serialActions.map(serialAction => serialAction.mutation), 89 | ...options 90 | })) 91 | const data = result.data; 92 | for (const serialAction of this.serialActions) { 93 | const oldKey = `m${serialAction.index}`; 94 | const newValue = { 95 | ...data[oldKey].returning, 96 | index: serialAction.index 97 | }; 98 | data[serialAction.alias] = newValue; 99 | data[serialAction.index] = newValue; 100 | delete data[oldKey] 101 | } 102 | // @ts-ignore 103 | return { 104 | ...result, 105 | data, 106 | } as Record> 107 | 108 | } 109 | 110 | public actions() { 111 | return this.serialActions; 112 | } 113 | 114 | public getTransitionType(transition: Transition): TransitionType { 115 | if (transition[0] === null) { 116 | return 'insert' 117 | } else if (transition[1] === null) { 118 | return 'delete' 119 | } else if (transition[0] !== null && transition[1] !== null) { 120 | return 'update' 121 | } else { 122 | throw new Error('Invalid transition') 123 | } 124 | } 125 | 126 | public setDefaultTable(table: Table<'insert' | 'update' | 'delete'>) { 127 | this.defaultTable = table; 128 | return this; 129 | } 130 | 131 | public getDefaultTable() { 132 | return this.defaultTable; 133 | } 134 | 135 | public setDeep(deep: DeepClient) { 136 | this.deep = deep; 137 | return this; 138 | } 139 | 140 | public getDeep() { 141 | return this.deep; 142 | } 143 | 144 | public setExecuteOptions(options: ExecuteOptions) { 145 | this.executeOptions = options; 146 | return this; 147 | } 148 | 149 | public getExecuteOptions() { 150 | return this.executeOptions; 151 | } 152 | } 153 | 154 | export interface AppendTransitionOptions { 155 | /** 156 | * A transition to append 157 | * 158 | * If you want to insert link - the first element must be null and the second must be link. If you want to update link - the first and second elements must be links. If you want to delete link - the first element must be link and second must be null 159 | */ 160 | transition: Transition; 161 | /** 162 | * A table name where operation must be executed 163 | * 164 | * @defaultValue 'links' 165 | */ 166 | table?: Table; 167 | alias?: string; 168 | } 169 | 170 | export type TransitionType = 'insert' | 'update' | 'delete'; 171 | export type TransitionItem = MutationInputLink | null; 172 | export type Transition = Array; 173 | 174 | export type ExecuteOptions = Omit 175 | 176 | export type SerialTransitionsBuilderOptions = { 177 | deep: DeepClient; 178 | defaultTable?: Table<'insert' | 'update' | 'delete'>; 179 | executeOptions?: ExecuteOptions; 180 | } 181 | 182 | type SerialAction = { 183 | mutation: IGenerateMutationBuilder, 184 | alias?: string; 185 | index: number; 186 | transitionType: TransitionType, 187 | table: Table<'insert' | 'update' | 'delete'>; 188 | } -------------------------------------------------------------------------------- /imports/files.tsx: -------------------------------------------------------------------------------- 1 | import { useDeep } from './client.js'; 2 | import { Id } from './minilinks.js'; 3 | import React, { useMemo } from 'react'; 4 | import * as dz from 'react-dropzone'; 5 | 6 | export const Files = React.memo(function Files({ 7 | Component = 'div', 8 | render = ({ 9 | getRootProps, 10 | input, 11 | isDragAccept, 12 | isDragActive, 13 | isDragReject, 14 | children, 15 | deep, 16 | Props, 17 | Component, 18 | }) => ( 19 | 20 | {input} 21 | {children} 22 | 23 | ), 24 | children, 25 | prevent = false, 26 | 27 | onDrop, 28 | 29 | insert = {}, 30 | containerId, 31 | onInsert, 32 | Props = {}, 33 | ...props 34 | }: { 35 | render?: any; 36 | children?: any; 37 | prevent?: boolean; 38 | 39 | onDrop: (files, a, event, prevent) => void; 40 | 41 | insert?: any; 42 | containerId: Id; 43 | onInsert: (id, file, a, event) => void; 44 | Props?: any; 45 | [key: string]: any; 46 | }) { 47 | const deep = useDeep(); 48 | const { 49 | getInputProps, 50 | getRootProps, 51 | isDragActive, 52 | isDragAccept, 53 | isDragReject, 54 | } = useFiles({ 55 | prevent, 56 | onDrop, 57 | insert, 58 | containerId, 59 | onInsert, 60 | ...props, 61 | }); 62 | 63 | const input = ; 64 | 65 | return render({ 66 | getRootProps, 67 | input, 68 | isDragActive, 69 | isDragAccept, 70 | isDragReject, 71 | children, 72 | deep, 73 | Props, 74 | Component, 75 | }); 76 | }); 77 | 78 | export function useFiles({ 79 | prevent = false, 80 | 81 | onDrop: _onDrop, 82 | 83 | insert = {}, 84 | type_id: _type_id, 85 | containerId, 86 | onInsert, 87 | 88 | ...props 89 | }: { 90 | prevent?: boolean; 91 | 92 | onDrop: (files, a, event, prevent) => void; 93 | 94 | insert?: any; 95 | type_id?: Id; 96 | containerId: Id; 97 | onInsert: (id, file, a, event) => void; 98 | 99 | [key: string]: any; 100 | }) { 101 | const deep = useDeep(); 102 | const type_id = useMemo(() => _type_id || deep.idLocal('@deep-foundation/core', 'AsyncFile'), [_type_id]); 103 | const onDrop = async (files, a, event) => { 104 | let _prevent = prevent; 105 | _onDrop && _onDrop(files, a, event, () => { _prevent = true }); 106 | if (!_prevent) { 107 | for (const file of files) { 108 | const result = await deep.insert({ 109 | file, 110 | type_id, 111 | containerId, 112 | ...insert, 113 | }); 114 | onInsert && onInsert(result?.data?.[0]?.id, file, a, event); 115 | } 116 | } 117 | }; 118 | const dropzone = dz.useDropzone({ 119 | onDrop, 120 | ...props, 121 | }); 122 | return dropzone; 123 | } 124 | 125 | export async function base64ToFile(dataurl, filename): Promise { 126 | var arr = dataurl.split(','), 127 | mime = arr[0].match(/:(.*?);/)[1], 128 | bstr = atob(arr[arr.length - 1]), 129 | n = bstr.length, 130 | u8arr = new Uint8Array(n); 131 | while (n--) { 132 | u8arr[n] = bstr.charCodeAt(n); 133 | } 134 | return new File([u8arr], filename, { type: mime }); 135 | } 136 | 137 | export async function fileToBase64(file): Promise { 138 | return new Promise((res, rej) => { 139 | var reader = new FileReader(); 140 | reader.readAsDataURL(file); 141 | reader.onload = function () { 142 | res(String(reader.result)); 143 | }; 144 | reader.onerror = function (error) { 145 | rej(error); 146 | }; 147 | }); 148 | } -------------------------------------------------------------------------------- /imports/global-ids.ts: -------------------------------------------------------------------------------- 1 | export const ALLOWED_IDS = [5]; 2 | export const DENIED_IDS = [0, 10, 11, 12, 13]; 3 | -------------------------------------------------------------------------------- /imports/gql/index.ts: -------------------------------------------------------------------------------- 1 | import gql from 'graphql-tag'; 2 | 3 | export * from './mutation.js'; 4 | export * from './query.js'; 5 | export * from './serial.js'; 6 | -------------------------------------------------------------------------------- /imports/gql/mutation.ts: -------------------------------------------------------------------------------- 1 | import Debug from 'debug'; 2 | 3 | const debug = Debug('deeplinks:gql:mutation'); 4 | const log = debug.extend('log'); 5 | const error = debug.extend('error'); 6 | // Force enable this file errors output 7 | 8 | const fieldsInputs = (tableName): IGenerateMutationFieldTypes => ({ 9 | 'distinct_on': `[${tableName}_select_column!]`, 10 | 'limit': `Int`, 11 | 'offset': `Int`, 12 | 'order_by': `[${tableName}_order_by!]`, 13 | 'where': `${tableName}_bool_exp!`, 14 | 'objects': `[${tableName}_insert_input!]!`, 15 | 'object': `${tableName}_insert_input!`, 16 | '_inc': `${tableName}_inc_input`, 17 | '_set': `${tableName}_set_input`, 18 | 'on_conflict': `${tableName}_on_conflict`, 19 | }); 20 | 21 | export interface IGenerateMutationOptions { 22 | tableName: string; 23 | operation: 'insert' | 'update' | 'delete'; 24 | queryName?: string; 25 | returning?: string; 26 | variables?: any; 27 | } 28 | 29 | export interface IGenerateMutationBuilder { 30 | (alias: string, index: number): IGenerateMutationResult 31 | } 32 | 33 | export interface IGenerateMutationFieldTypes { 34 | [field: string]: string; 35 | } 36 | 37 | export interface IGenerateMutationResult extends IGenerateMutationOptions { 38 | resultReturning: string; 39 | fields: string[]; 40 | fieldTypes: IGenerateMutationFieldTypes; 41 | defs: string[]; 42 | args: string[]; 43 | alias: string; 44 | index: number; 45 | resultAlias: string; 46 | resultVariables: any; 47 | } 48 | 49 | export const generateMutation = ({ 50 | tableName, 51 | operation, 52 | queryName = `${operation}_${tableName}`, 53 | returning = `id`, 54 | variables, 55 | }: IGenerateMutationOptions): IGenerateMutationBuilder => { 56 | log('generateMutationOptions', { tableName, operation, queryName, returning, variables }); 57 | const fields = 58 | operation === 'insert' ? ['objects','on_conflict'] 59 | : operation === 'update' ? ['_inc','_set','where'] 60 | : operation === 'delete' ? ['where'] 61 | : []; 62 | const fieldTypes = fieldsInputs(tableName); 63 | 64 | return (alias: string, index: number): IGenerateMutationResult => { 65 | log('generateMutationBuilder', { tableName, operation, queryName, returning, variables, alias, index }); 66 | const defs = []; 67 | const args = []; 68 | for (let f = 0; f < fields.length; f++) { 69 | const field = fields[f]; 70 | if (variables[field]) { 71 | defs.push(`$${field + index}: ${fieldTypes[field]}`); 72 | args.push(`${field}: $${field}${index}`); 73 | } 74 | } 75 | const resultAlias = `${alias}${typeof(index) === 'number' ? index : ''}`; 76 | const resultReturning = `returning { ${returning} }`; 77 | const resultVariables = {}; 78 | for (const v in variables) { 79 | if (Object.prototype.hasOwnProperty.call(variables, v)) { 80 | const variable = variables[v]; 81 | resultVariables[v + index] = variable; 82 | } 83 | } 84 | const result = { 85 | tableName, 86 | operation, 87 | queryName, 88 | returning, 89 | variables, 90 | resultReturning, 91 | fields, 92 | fieldTypes, 93 | index, 94 | defs, 95 | args, 96 | alias, 97 | resultAlias, 98 | resultVariables, 99 | }; 100 | log('generateMutationResult', result); 101 | return result; 102 | }; 103 | }; 104 | 105 | export const insertMutation = (tableName: string, variables: any, options?: IGenerateMutationOptions) => { 106 | return generateMutation({ 107 | tableName, operation: 'insert', variables, ...options, 108 | }); 109 | } 110 | 111 | export const updateMutation = (tableName: string, variables: any, options?: IGenerateMutationOptions) => { 112 | return generateMutation({ 113 | tableName, operation: 'update', variables, ...options, 114 | }); 115 | } 116 | 117 | export const deleteMutation = (tableName: string, variables: any, options?: IGenerateMutationOptions) => { 118 | return generateMutation({ 119 | tableName, operation: 'delete', variables, ...options, 120 | }); 121 | } 122 | -------------------------------------------------------------------------------- /imports/gql/serial.ts: -------------------------------------------------------------------------------- 1 | import Debug from 'debug'; 2 | import gql from 'graphql-tag'; 3 | import { SerialOperationType, Table, SerialOperation, SerialOperationDetails } from '../client.js'; 4 | 5 | const debug = Debug('deeplinks:gql:serial'); 6 | const log = debug.extend('log'); 7 | const error = debug.extend('error'); 8 | // Force enable this file errors output 9 | 10 | export interface ISerialOptions { 11 | actions: any[]; 12 | name?: string; 13 | alias?: string; 14 | [key: string]: any; 15 | }; 16 | 17 | export interface ISerialResult { 18 | mutation: any; 19 | mutationString: any; 20 | variables: any; 21 | }; 22 | 23 | export const generateSerial = ({ 24 | actions = [], 25 | name = 'SERIAL', 26 | alias = 'm', 27 | ...options 28 | }: ISerialOptions): ISerialResult => { 29 | log('generateSerial', { name, alias, actions }); 30 | const calledActions = actions.map((m,i) => typeof(m) === 'function' ? m(alias, i) : m); 31 | const defs = calledActions.map(m => m.defs.join(',')).join(','); 32 | const mutationString = `mutation ${name} (${defs}) { ${calledActions.map(m => `${m.resultAlias}: ${m.queryName}(${m.args.join(',')}) { ${m.resultReturning} }`).join('')} }`; 33 | const mutation = gql`${mutationString}`; 34 | const variables = {}; 35 | for (let a = 0; a < calledActions.length; a++) { 36 | const action = calledActions[a]; 37 | for (const v in action.resultVariables) { 38 | if (Object.prototype.hasOwnProperty.call(action.resultVariables, v)) { 39 | const variable = action.resultVariables[v]; 40 | variables[v] = variable; 41 | } 42 | } 43 | } 44 | const result = { 45 | mutation, 46 | variables, 47 | mutationString, 48 | ...options 49 | }; 50 | log('generateSerialResult', JSON.stringify({ mutation: mutationString, variables }, null, 2)); 51 | return result; 52 | }; 53 | 54 | export function createSerialOperation< 55 | TSerialOperationType extends SerialOperationType, 56 | TTable extends Table 57 | >(params: { 58 | type: TSerialOperationType; 59 | table: TTable; 60 | } & SerialOperationDetails): SerialOperation { 61 | return params; 62 | } -------------------------------------------------------------------------------- /imports/jwt.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import JWT from 'jsonwebtoken'; 3 | import { Id } from './minilinks'; 4 | 5 | export interface Options { 6 | linkId: Id; 7 | secret: string; 8 | role?: string; 9 | } 10 | 11 | const apolloClient = generateApolloClient({ 12 | path: `${process.env.DEEPLINKS_HASURA_PATH}/v1/graphql`, 13 | ssl: !!+process.env.DEEPLINKS_HASURA_SSL, 14 | secret: process.env.DEEPLINKS_HASURA_SECRET, 15 | }); 16 | 17 | /** Only server, generate jwt by linkId option. */ 18 | export function jwt(options: Options) { 19 | const role = options.role || 'link'; 20 | return JWT.sign({ 21 | "https://hasura.io/jwt/claims": { 22 | "x-hasura-allowed-roles": [role], 23 | "x-hasura-default-role": role, 24 | "x-hasura-user-id": options.linkId.toString(), 25 | } 26 | }, options.secret); 27 | } 28 | -------------------------------------------------------------------------------- /imports/package.ts: -------------------------------------------------------------------------------- 1 | import { DeepClient, DeepClientInstance } from './client'; 2 | import {debug} from './debug.js' 3 | import { Id, Link } from './minilinks'; 4 | const moduleLog = debug.extend('package') 5 | 6 | /** 7 | * Represents a deep package 8 | * 9 | * @remarks 10 | * This class intended to be extended by packages 11 | * 12 | * @example 13 | ```ts 14 | const package = new Package({deep, name}); 15 | const {name: packageName} = package; 16 | const batteryLevelValueLinkId = await package.batteryLevelValue.id(); 17 | ``` 18 | */ 19 | export class Package { 20 | public deep: DeepClientInstance; 21 | /** 22 | * Name of the package 23 | */ 24 | public name: string; 25 | 26 | constructor(options: PackageOptions) { 27 | const log = moduleLog.extend(this.name) 28 | log({options}) 29 | this.deep = options.deep; 30 | this.name = options.name; 31 | } 32 | 33 | /** 34 | * Creates an entity 35 | * 36 | * @example 37 | * #### Create an entity 38 | ```ts 39 | class MyPackage extends Package { 40 | public yourLinkName = this.createEntity("YourLinkName"); 41 | } 42 | const myPackage = new MyPackage({deep}); 43 | const myLinkId = await myPackage.yourLinkName.id(); 44 | const myLinkLocalId = await myPackage.yourLinkName.idLocal(); 45 | ``` 46 | */ 47 | public createEntity(name: string) { 48 | const log = moduleLog.extend(this.createEntity.name) 49 | log({name}) 50 | const result = { 51 | /** 52 | * Gets id of the link 53 | * 54 | * @example 55 | * #### Get id of the link 56 | ```ts 57 | const myPackage = new MyPackage({deep}); 58 | const myLinkId = await myPackage.yourLinkName.id(); 59 | ``` 60 | */ 61 | id: async () => { 62 | const log = moduleLog.extend(this.id.name) 63 | const result = await this.id(name); 64 | log({result}) 65 | return result; 66 | }, 67 | /** 68 | * Gets id of the link from minilinks 69 | * 70 | * @example 71 | * #### Get id of the link from minilinks 72 | ```ts 73 | const myPackage = new MyPackage({deep}); 74 | const myLinkLocalId = await myPackage.yourLinkName.idLocal(); 75 | ``` 76 | */ 77 | idLocal: () => { 78 | return this.idLocal(name); 79 | }, 80 | /** 81 | * Name of the link 82 | */ 83 | name: name 84 | }; 85 | log({result}) 86 | return result; 87 | } 88 | 89 | /** 90 | * Gets id of the package link 91 | * 92 | * @example 93 | * #### Get id of the package link 94 | ```ts 95 | const package = new Package({deep}); 96 | const myLinkId = await package.id("MyLinkName"); 97 | ``` 98 | */ 99 | async id(...names: string[]) { 100 | const log = moduleLog.extend(this.id.name) 101 | log({names}) 102 | const deepIdArgs: Parameters = [this.name, ...names] 103 | log({deepIdArgs}) 104 | const result = await this.deep.id(...deepIdArgs); 105 | log({result}) 106 | return result; 107 | } 108 | 109 | /** 110 | * Gets id of the package link from minilinks 111 | * 112 | * @example 113 | * #### Get id of the package link from minilinks 114 | ```ts 115 | const package = new Package({deep}); 116 | await package.applyMiniLinks(); 117 | const myLinkId = await package.idLocal("MyLinkName"); 118 | ``` 119 | */ 120 | idLocal(...names: string[]) { 121 | const log = moduleLog.extend(this.idLocal.name) 122 | log({names}) 123 | const deepIdLocalArgs: Parameters = [this.name, ...names] 124 | log({deepIdLocalArgs}) 125 | const result = this.deep.idLocal(...deepIdLocalArgs) 126 | log({result}) 127 | return result; 128 | } 129 | 130 | /** 131 | * Pastes your links into minilinks 132 | * 133 | * @example 134 | * #### Use applyMiniLinks and idLocal 135 | ```ts 136 | const package = new Package({deep}); 137 | await package.applyMiniLinks(); 138 | const deviceLinkId = await package.Device.idLocal(); 139 | ``` 140 | */ 141 | async applyMiniLinks() { 142 | const log = moduleLog.extend(this.applyMiniLinks.name) 143 | const {data: packageLinks} = await this.deep.select({ 144 | up: { 145 | tree_id: { 146 | _id: ["@deep-foundation/core", 'containTree'] 147 | }, 148 | parent_id: { 149 | _id: [this.name] 150 | } 151 | } 152 | }) 153 | log({packageLinks}) 154 | 155 | if(!packageLinks) { 156 | throw new Error(`Package with name ${this.name} is not found`) 157 | } 158 | 159 | const result = this.deep.minilinks.apply(packageLinks as Link[]) 160 | log({result}) 161 | 162 | return result 163 | } 164 | } 165 | 166 | export interface PackageOptions { 167 | name: string; 168 | deep: DeepClientInstance; 169 | } 170 | -------------------------------------------------------------------------------- /imports/packages.ts: -------------------------------------------------------------------------------- 1 | import { DeepClient } from "./client.js"; 2 | import { Id, Link } from "./minilinks.js"; 3 | import fs from 'fs'; 4 | import path from 'path'; 5 | import { Package } from "./packager.js"; 6 | import _ from 'lodash';; 7 | 8 | export class Packages { 9 | deep: DeepClient>; 10 | constructor(deep) { 11 | this.deep = deep; 12 | } 13 | async select(query) { 14 | const deep = this.deep; 15 | const _and = [{ 16 | type_id: deep.idLocal('@deep-foundation/core', 'Package'), 17 | string: { value: { _neq: 'deep' } }, 18 | }]; 19 | if (query) _and.push(query); 20 | return await deep.select({ 21 | _and, 22 | }); 23 | } 24 | async export(query): Promise<{ [name: string]: Package }> { 25 | const deep = this.deep; 26 | const packager = deep.Packager(); 27 | const { data: packages } = await this.select(query); 28 | console.log('export packages', packages.map(p => p.id).join(', ')); 29 | const results = {}; 30 | for (let i = 0; i < packages.length; i++) { 31 | const p = packages[i]; 32 | console.log('export package', `${p.id} ${p?.value?.value}`); 33 | const pckg = await packager.export({ packageLinkId: p.id }); 34 | console.log('exported package', `${pckg.package.name} ${pckg.package.version}`); 35 | if (pckg.errors) console.log(JSON.stringify(pckg.errors, null, 2)); 36 | results[_.camelCase(`${pckg.package.name}@${pckg.package.version}`)] = pckg; 37 | } 38 | return results; 39 | } 40 | async write(address: string, pckgs: { [name: string]: Package }) { 41 | const packages = Object.values(pckgs); 42 | const deep = this.deep; 43 | console.log('write packages', Object.keys(pckgs).join(',')); 44 | for (let i = 0; i < packages.length; i++) { 45 | const p = packages[i]; 46 | fs.writeFileSync( 47 | path.join(address, _.camelCase(`${p?.package?.name}@${p.package.version}`))+'.json', 48 | JSON.stringify(p, null, 2), 49 | { encoding: 'utf-8' }, 50 | ); 51 | console.log('writeed package', `${path.join(address, _.camelCase(`${p?.package?.name}@${p.package.version}`))}`); 52 | } 53 | } 54 | async read(address: string): Promise<{ [name: string]: Package }> { 55 | const deep = this.deep; 56 | const packager = deep.Packager(); 57 | const pckgs = fs.readdirSync(address); 58 | console.log(`read packages from ${address} ${pckgs.join(', ')}`); 59 | const results = {}; 60 | for (let i = 0; i < pckgs.length; i++) { 61 | if (pckgs[i].slice(-5) === '.json' && pckgs[i] != 'deep.config.json') { 62 | console.log('read package', path.join(address, pckgs[i])); 63 | const json = fs.readFileSync(path.join(address, pckgs[i]), { encoding: 'utf-8' }); 64 | try { 65 | const pckg = JSON.parse(json); 66 | results[_.camelCase(`${pckg.package.name}@${pckg.package.version}`)] = pckg; 67 | } catch(e) { 68 | console.log('error read package', e); 69 | } 70 | } 71 | } 72 | return results; 73 | } 74 | async import(pckgs: { [name: string]: Package }): Promise<{ [name: string]: Package }> { 75 | const deep = this.deep; 76 | const packager = deep.Packager(); 77 | const packages = Object.values(pckgs); 78 | console.log(`import packages from ${Object.keys(pckgs).join(', ')}`); 79 | const results = {}; 80 | for (let i = 0; i < packages.length; i++) { 81 | const p = packages[i]; 82 | console.log(`import package ${_.camelCase(`${p.package.name}@${p.package.version}`)}`); 83 | results[_.camelCase(`${p.package.name}@${p.package.version}`)] = await packager.import(p); 84 | } 85 | return results; 86 | } 87 | }; -------------------------------------------------------------------------------- /imports/permission.ts: -------------------------------------------------------------------------------- 1 | import { HasuraApi } from '@deep-foundation/hasura/api.js'; 2 | 3 | export const permissions = async (api: HasuraApi, table: string | { name: string; schema: string; }, actions: { 4 | role: string; 5 | 6 | select: any; 7 | insert: any; 8 | update: any; 9 | delete: any; 10 | 11 | columns?: string | string[]; 12 | computed_fields?: string[]; 13 | } = { 14 | role: 'link', 15 | 16 | select: {}, 17 | insert: {}, 18 | update: {}, 19 | delete: {}, 20 | 21 | columns: '*', 22 | computed_fields: [], 23 | }) => { 24 | const columns = actions.columns || '*'; 25 | const computed_fields = actions.computed_fields || []; 26 | await api.metadata({ 27 | type: 'pg_create_select_permission', 28 | args: { 29 | table: table, 30 | role: actions.role, 31 | permission: { 32 | columns: columns, 33 | computed_fields, 34 | filter: actions.select, 35 | limit: 999, 36 | allow_aggregations: true 37 | } 38 | } 39 | }); 40 | await api.query({ 41 | type: 'create_insert_permission', 42 | args: { 43 | table: table, 44 | role: actions.role, 45 | permission: { 46 | check: actions.insert, 47 | columns: '*', 48 | } 49 | } 50 | }); 51 | await api.query({ 52 | type: 'create_update_permission', 53 | args: { 54 | table: table, 55 | role: actions.role, 56 | permission: { 57 | columns: '*', 58 | filter: actions.update, 59 | check: {}, 60 | } 61 | } 62 | }); 63 | await api.query({ 64 | type: 'create_delete_permission', 65 | args: { 66 | table: table, 67 | role: actions.role, 68 | permission: { 69 | filter: actions.delete, 70 | } 71 | } 72 | }); 73 | }; -------------------------------------------------------------------------------- /imports/react-token.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react'; 2 | import { TokenContext } from '@deep-foundation/react-hasura/token-context.js'; 3 | import { useLocalStore } from '@deep-foundation/store/local.js'; 4 | import { useCookiesStore } from '@deep-foundation/store/cookies.js' 5 | 6 | export function useTokenController(defaultValue: string = '') : [string, (string) => string] { 7 | const [localToken, setLocalToken] = useLocalStore('dc-dg-token', defaultValue); 8 | const [cookieToken, setCookieToken] = useCookiesStore('dc-dg-token', defaultValue); 9 | 10 | return [localToken || cookieToken, (newToken) => { 11 | const result = setLocalToken(newToken); 12 | setCookieToken(newToken); 13 | return result; 14 | }] 15 | } 16 | 17 | export const TokenProvider = function TokenProvider({ children }: { children?: any }) { 18 | const [token, setToken] = useTokenController(); 19 | // @ts-ignore 20 | return {children}; 21 | }; -------------------------------------------------------------------------------- /imports/reserve.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/index.js'; 2 | import type { ApolloClient } from '@apollo/client/index.js'; 3 | import { generateQuery, generateQueryData, generateSerial, insertMutation } from './gql/index.js'; 4 | import { Id } from './minilinks.js'; 5 | 6 | export interface ReservedOptions { 7 | count: number; 8 | client: ApolloClient; 9 | } 10 | 11 | export const RESERVE = gql`mutation RESERVE($count: Int!) { 12 | reserve(count: $count) { 13 | ids 14 | } 15 | }`; 16 | 17 | export async function reserve(options: ReservedOptions): Promise { 18 | const count = options.count; 19 | const result = await options.client.mutate({ 20 | mutation: RESERVE, 21 | variables: { count }, 22 | }); 23 | return result?.data?.reserve.ids; 24 | }; 25 | -------------------------------------------------------------------------------- /imports/reserved.ts: -------------------------------------------------------------------------------- 1 | import { gql } from '@apollo/client/index.js'; 2 | import type { ApolloClient } from '@apollo/client/index.js'; 3 | 4 | import { generateQuery, generateQueryData } from './gql/index.js'; 5 | import { Id } from './minilinks.js'; 6 | 7 | export interface ReseveOptions { 8 | count: number; 9 | client: ApolloClient; 10 | } 11 | 12 | export type ReserveResult = Id[]; 13 | 14 | const RESERVE = gql`query RESERVE($count: Int!) { reserve(count: $count) { ids } }`; 15 | 16 | export async function reserve(options: ReseveOptions): Promise { 17 | const result = await options.client.query({ 18 | query: RESERVE, variables: { count: options.count }, 19 | }); 20 | const ids: Id[] = result?.data?.reserve?.ids || []; 21 | return ids; 22 | } 23 | 24 | -------------------------------------------------------------------------------- /imports/router/guest.ts: -------------------------------------------------------------------------------- 1 | import { jwt } from '../jwt.js'; 2 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 3 | import gql from 'graphql-tag'; 4 | import { generateSerial, insertMutation } from '../gql/index.js'; 5 | import { ApolloServer } from 'apollo-server-express'; 6 | import { DeepClient } from '../client.js'; 7 | import { ApolloServerPluginDrainHttpServer, ApolloServerPluginLandingPageGraphQLPlayground } from 'apollo-server-core'; 8 | 9 | const JWT_SECRET = process.env.JWT_SECRET; 10 | const jwt_secret = JSON.parse(JWT_SECRET); 11 | 12 | export const typeDefsString = ` 13 | type Query { 14 | guest: GuestOutput 15 | } 16 | type GuestOutput { 17 | token: String 18 | linkId: Int 19 | } 20 | `; 21 | export const typeDefs = gql`${typeDefsString}`; 22 | 23 | const client = generateApolloClient({ 24 | path: `${process.env.DEEPLINKS_HASURA_PATH}/v1/graphql`, 25 | ssl: !!+process.env.DEEPLINKS_HASURA_SSL, 26 | secret: process.env.DEEPLINKS_HASURA_SECRET, 27 | }, { ApolloClient: { defaultOptions: { query: { fetchPolicy: 'no-cache' }, watchQuery: { fetchPolicy: 'no-cache' } } } }); 28 | 29 | const deep = new DeepClient({ 30 | apolloClient: client, 31 | }) 32 | 33 | const resolvers = { 34 | Query: { 35 | guest: async (source, args, context, info) => { 36 | const { data: [{ id }] } = await deep.insert({ 37 | type_id: deep.idLocal('@deep-foundation/core', 'User'), 38 | out: { data: [ 39 | { 40 | type_id: deep.idLocal('@deep-foundation/core', 'Join'), 41 | to_id: await deep.id('deep', 'users') 42 | }, 43 | ] }, 44 | }); 45 | await deep.insert({ 46 | type_id: deep.idLocal('@deep-foundation/core', 'Rule'), 47 | out: { data: [ 48 | { 49 | type_id: deep.idLocal('@deep-foundation/core', 'RuleSubject'), 50 | to: { data: { 51 | type_id: deep.idLocal('@deep-foundation/core', 'Selector'), 52 | out: { data: { 53 | type_id: deep.idLocal('@deep-foundation/core', 'SelectorInclude'), 54 | to_id: id, 55 | out: { data: { 56 | type_id: deep.idLocal('@deep-foundation/core', 'SelectorTree'), 57 | to_id: deep.idLocal('@deep-foundation/core', 'joinTree'), 58 | } }, 59 | } } 60 | } } 61 | }, 62 | { 63 | type_id: deep.idLocal('@deep-foundation/core', 'RuleObject'), 64 | to: { data: { 65 | type_id: deep.idLocal('@deep-foundation/core', 'Selector'), 66 | out: { data: { 67 | type_id: deep.idLocal('@deep-foundation/core', 'SelectorInclude'), 68 | to_id: id, 69 | out: { data: { 70 | type_id: deep.idLocal('@deep-foundation/core', 'SelectorTree'), 71 | to_id: deep.idLocal('@deep-foundation/core', 'containTree'), 72 | } }, 73 | } } 74 | } } 75 | }, 76 | { 77 | type_id: deep.idLocal('@deep-foundation/core', 'RuleAction'), 78 | to: { data: { 79 | type_id: deep.idLocal('@deep-foundation/core', 'Selector'), 80 | out: { data: { 81 | type_id: deep.idLocal('@deep-foundation/core', 'SelectorInclude'), 82 | to_id: deep.idLocal('@deep-foundation/core', 'AllowSelect'), 83 | out: { data: { 84 | type_id: deep.idLocal('@deep-foundation/core', 'SelectorTree'), 85 | to_id: deep.idLocal('@deep-foundation/core', 'containTree'), 86 | } }, 87 | } } 88 | } } 89 | }, 90 | ] }, 91 | }); 92 | const token = jwt({ 93 | secret: jwt_secret.key, 94 | linkId: id, 95 | }); 96 | return { token, linkId: id }; 97 | }, 98 | } 99 | }; 100 | 101 | const context = ({ req }) => { 102 | return { headers: req.headers }; 103 | }; 104 | 105 | const generateApolloServer = (httpServer) => { 106 | return new ApolloServer({ 107 | introspection: true, 108 | typeDefs, 109 | resolvers, 110 | context, 111 | plugins: [ 112 | ApolloServerPluginDrainHttpServer({ httpServer }), 113 | ApolloServerPluginLandingPageGraphQLPlayground() 114 | ]}); 115 | } 116 | 117 | export default generateApolloServer; -------------------------------------------------------------------------------- /imports/router/healthz.ts: -------------------------------------------------------------------------------- 1 | export default (req, res) => { 2 | return res.status(200).json({docker: +!!process.env.DOCKER}); 3 | }; -------------------------------------------------------------------------------- /imports/router/index.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import links from './links.js'; 3 | import values from './values.js'; 4 | import reservedCleaner from './reserved-cleaner.js'; 5 | import reserved from './reserved.js'; 6 | import healthz from './healthz.js'; 7 | import scheduler from './scheduler.js'; 8 | 9 | const router:express.IRouter = express.Router(); 10 | 11 | router.use('/api/links', links); 12 | router.use('/api/values', values); 13 | router.use('/api/reserved', reserved); 14 | router.use('/api/reserved-cleaner', reservedCleaner); 15 | router.use('/api/healthz', healthz); 16 | router.use('/api/scheduler', scheduler); 17 | 18 | export default router; 19 | -------------------------------------------------------------------------------- /imports/router/jwt.ts: -------------------------------------------------------------------------------- 1 | import { jwt } from '../jwt.js'; 2 | import { generateRemoteSchema } from '@deep-foundation/hasura/remote-schema.js'; 3 | import gql from 'graphql-tag'; 4 | import { ApolloServer } from 'apollo-server-express'; 5 | import { ApolloServerPluginDrainHttpServer, ApolloServerPluginLandingPageGraphQLPlayground } from 'apollo-server-core'; 6 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 7 | import { DeepClient } from '../client.js'; 8 | import { serializeError } from 'serialize-error'; 9 | 10 | const apolloClient = generateApolloClient({ 11 | path: `${process.env.DEEPLINKS_HASURA_PATH}/v1/graphql`, 12 | ssl: !!+process.env.DEEPLINKS_HASURA_SSL, 13 | secret: process.env.DEEPLINKS_HASURA_SECRET, 14 | }); 15 | 16 | const deep = new DeepClient({ apolloClient }); 17 | 18 | const JWT_SECRET = process.env.JWT_SECRET; 19 | const jwt_secret = JSON.parse(JWT_SECRET); 20 | 21 | export const typeDefsString = ` 22 | type Query { 23 | jwt(input: JWTInput): JWTOutput 24 | } 25 | input JWTInput { 26 | linkId: Int 27 | } 28 | type JWTOutput { 29 | token: String 30 | linkId: Int 31 | error: String 32 | } 33 | `; 34 | 35 | export const typeDefs = gql`${typeDefsString}`; 36 | 37 | const resolvers = { 38 | Query: { 39 | jwt: async (source, args, context, info) => { 40 | const { linkId } = args.input; 41 | try { 42 | if (!linkId) { 43 | return { 44 | linkId: +context?.headers?.['x-hasura-user-id'] 45 | }; 46 | } 47 | if (!+context?.headers?.['x-hasura-user-id'] && context?.headers?.['x-hasura-role'] !== 'admin') { 48 | return { error: '!currentUser' }; 49 | } 50 | if ( 51 | context?.headers?.['x-hasura-role'] !== 'admin' && 52 | !(await deep.select({ 53 | subject_id: { _eq: +context?.headers?.['x-hasura-user-id'] }, 54 | action_id: { _eq: deep.idLocal('@deep-foundation/core', 'AllowAdmin') }, 55 | }, { table: 'can', returning: 'rule_id' }))?.data?.[0] && 56 | +context?.headers?.['x-hasura-user-id'] !== linkId && 57 | !await deep.can( 58 | linkId, +context?.headers?.['x-hasura-user-id'], deep.idLocal('@deep-foundation/core', 'AllowLogin') 59 | ) 60 | ) { 61 | return { error: 'cant' }; 62 | } 63 | const token = jwt({ 64 | secret: jwt_secret.key, 65 | linkId, 66 | role: await deep.can(null, linkId, deep.idLocal('@deep-foundation/core', 'AllowAdmin')) ? 'admin' : 'link', 67 | }); 68 | return { token, linkId }; 69 | } catch (error) { 70 | const serializedError = serializeError(error); 71 | return { serializedError }; 72 | } 73 | }, 74 | } 75 | }; 76 | 77 | const context = ({ req }) => { 78 | return { headers: req.headers }; 79 | }; 80 | 81 | const generateApolloServer = (httpServer) => { 82 | return new ApolloServer({ 83 | introspection: true, 84 | typeDefs, 85 | resolvers, 86 | context, 87 | plugins: [ 88 | ApolloServerPluginDrainHttpServer({ httpServer }), 89 | ApolloServerPluginLandingPageGraphQLPlayground() 90 | ]}); 91 | } 92 | 93 | export default generateApolloServer; -------------------------------------------------------------------------------- /imports/router/reserved-cleaner.ts: -------------------------------------------------------------------------------- 1 | import { generateQuery, generateQueryData } from '../gql/index.js'; 2 | import { HasuraApi } from '@deep-foundation/hasura/api.js'; 3 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 4 | import { serializeError } from 'serialize-error'; 5 | 6 | const RESERVED_LIFETIME_MS = +process.env.RESERVED_LIFETIME || 24 * 60 * 60 * 1000; 7 | 8 | export const api = new HasuraApi({ 9 | path: process.env.DEEPLINKS_HASURA_PATH, 10 | ssl: !!+process.env.DEEPLINKS_HASURA_SSL, 11 | secret: process.env.DEEPLINKS_HASURA_SECRET, 12 | }); 13 | 14 | const client = generateApolloClient({ 15 | path: `${process.env.DEEPLINKS_HASURA_PATH}/v1/graphql`, 16 | ssl: !!+process.env.DEEPLINKS_HASURA_SSL, 17 | secret: process.env.DEEPLINKS_HASURA_SECRET, 18 | }); 19 | 20 | export default async (req, res) => { 21 | try { 22 | const body = req?.body; 23 | const result = await client.query(generateQuery({ 24 | queries: [ 25 | generateQueryData({ tableName: 'reserved', returning: `reserved_ids`, variables: { where: { 26 | created_at: { 27 | _lt: new Date(Date.now() - RESERVED_LIFETIME_MS) 28 | } 29 | } } }), 30 | ], 31 | name: 'CRON_RESERVED', 32 | })); 33 | 34 | return res.json({ cleaned: [] }); 35 | } catch (e) { 36 | const serializedError = serializeError(e); 37 | return res.status(500).json({ error: serializedError }); 38 | } 39 | }; -------------------------------------------------------------------------------- /imports/router/reserved.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import { HasuraApi } from '@deep-foundation/hasura/api.js'; 3 | import { generateMutation, generateSerial, insertMutation } from '../gql/index.js'; 4 | import Debug from 'debug'; 5 | import { serializeError } from 'serialize-error'; 6 | 7 | const debug = Debug('deeplinks:eh:reserved'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | // Force enable this file errors output 11 | 12 | const SCHEMA = 'public'; 13 | 14 | export const api = new HasuraApi({ 15 | path: process.env.DEEPLINKS_HASURA_PATH, 16 | ssl: !!+process.env.DEEPLINKS_HASURA_SSL, 17 | secret: process.env.DEEPLINKS_HASURA_SECRET, 18 | }); 19 | 20 | const client = generateApolloClient({ 21 | path: `${process.env.DEEPLINKS_HASURA_PATH}/v1/graphql`, 22 | ssl: !!+process.env.DEEPLINKS_HASURA_SSL, 23 | secret: process.env.DEEPLINKS_HASURA_SECRET, 24 | }); 25 | 26 | export default async (req, res) => { 27 | try { 28 | const count = req?.body?.input?.count; 29 | if (!count) return res.status(500).json({ error: 'no count' }); 30 | // const token = req?.body?.session_variables; 31 | const links = []; 32 | for (let i = 0; i < count; i++) links[i] = { type_id: 0 }; 33 | const mutateLinksResult = await client.mutate(generateSerial({ 34 | actions: [insertMutation('links', { objects: links })], 35 | name: 'INSERT_LINKS', 36 | })); 37 | const ids = mutateLinksResult.data['m0']?.returning?.map(node => node.id); 38 | if (!ids) res.status(500).json({ error: 'insert links error' }); 39 | const mutateReservedResult = await client.mutate(generateSerial({ 40 | actions: [ 41 | generateMutation({ 42 | tableName: 'reserved', operation: 'insert', 43 | variables: { objects: { reserved_ids: ids, user_id: 123123, created_at: new Date().toISOString() } }, //userid 44 | }), 45 | ], 46 | name: 'INSERT_RESERVED', 47 | })); 48 | if (!mutateLinksResult.data['m0']?.returning?.[0]?.id) res.status(500).json({ error: 'insert reserved error' }); 49 | return res.json({ ids }); 50 | } catch (e) { 51 | const serializedError = serializeError(e); 52 | error(JSON.stringify(serializedError, null, 2)); 53 | return res.status(500).json({ error: serializedError }); 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /imports/router/values.ts: -------------------------------------------------------------------------------- 1 | import Debug from 'debug'; 2 | 3 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 4 | import { HasuraApi } from '@deep-foundation/hasura/api.js'; 5 | // import { sql } from '@deep-foundation/hasura/sql.js'; 6 | import { gql } from '@apollo/client/index.js'; 7 | import vm from 'vm'; 8 | import { serializeError } from 'serialize-error'; 9 | import { permissions } from '../permission.js'; 10 | import { findPromiseLink, reject, resolve } from '../promise.js'; 11 | import { DeepClient } from '../client.js'; 12 | import { ALLOWED_IDS, DENIED_IDS } from '../global-ids.js'; 13 | import axios from 'axios'; 14 | import crypto from 'crypto'; 15 | import { 16 | handleOperation, 17 | handleSelectorOperation, 18 | } from './links.js'; 19 | import { boolExpToSQL } from '../bool_exp_to_sql.js'; 20 | 21 | const SCHEMA = 'public'; 22 | 23 | const debug = Debug('deeplinks:eh:values'); 24 | const log = debug.extend('log'); 25 | const error = debug.extend('error'); 26 | // Force enable this file errors output 27 | 28 | export const api = new HasuraApi({ 29 | path: process.env.DEEPLINKS_HASURA_PATH, 30 | ssl: !!+process.env.DEEPLINKS_HASURA_SSL, 31 | secret: process.env.DEEPLINKS_HASURA_SECRET, 32 | }); 33 | 34 | const client = generateApolloClient({ 35 | path: `${process.env.DEEPLINKS_HASURA_PATH}/v1/graphql`, 36 | ssl: !!+process.env.DEEPLINKS_HASURA_SSL, 37 | secret: process.env.DEEPLINKS_HASURA_SECRET, 38 | }); 39 | 40 | const deep = new DeepClient({ 41 | apolloClient: client, 42 | }) 43 | 44 | const portHash = {}; 45 | const containerHash = {}; 46 | 47 | export default async (req, res) => { 48 | try { 49 | const event = req?.body?.event; 50 | const triggeredByLinkId = parseInt(event.session_variables["x-hasura-user-id"]); 51 | const operation = event?.op; 52 | if (operation === 'INSERT' || operation === 'UPDATE' || operation === 'DELETE') { 53 | let linkId; 54 | let linkRow; 55 | let oldValueRow; 56 | let newValueRow; 57 | let newRow; 58 | let oldRow; 59 | try { 60 | oldValueRow = event?.data?.old; 61 | newValueRow = event?.data?.new; 62 | 63 | linkId = newValueRow?.link_id ?? oldValueRow?.link_id; 64 | linkRow = (await deep.select({ 65 | id: { _eq: linkId }, 66 | }, { 67 | returning: `id from_id type_id to_id`, 68 | }))?.data?.[0]; 69 | 70 | const handleUpdateTypeId = deep.idLocal('@deep-foundation/core', 'HandleUpdate'); 71 | const queryResult = (await client.query({ 72 | query: gql` 73 | query { 74 | promise_links(where: { 75 | handle_operation_type_id: { _eq: ${handleUpdateTypeId} }, 76 | old_link_id: {_eq: ${linkId}}, 77 | new_link_id: {_eq: ${linkId}}, 78 | values_operation: { _eq: "${operation}" } 79 | }) { 80 | id 81 | promise_id 82 | old_link_id 83 | old_link_type_id 84 | old_link_from_id 85 | old_link_to_id 86 | new_link_id 87 | new_link_type_id 88 | new_link_from_id 89 | new_link_to_id 90 | handle_operation_id 91 | } 92 | } 93 | `, 94 | }))?.data?.promise_links?.[0]; 95 | const oldLinkRow = { 96 | id: queryResult?.old_link_id, 97 | type_id: queryResult?.old_link_type_id, 98 | from_id: queryResult?.old_link_from_id, 99 | to_id: queryResult?.old_link_to_id, 100 | }; 101 | const newLinkRow = { 102 | id: queryResult?.new_link_id, 103 | type_id: queryResult?.new_link_type_id, 104 | from_id: queryResult?.new_link_from_id, 105 | to_id: queryResult?.new_link_to_id, 106 | }; 107 | log(`oldLinkRow: ${JSON.stringify(oldLinkRow)}`); 108 | log(`newLinkRow: ${JSON.stringify(newLinkRow)}`); 109 | 110 | oldRow = { ...linkRow, value: oldValueRow }; 111 | newRow = { ...linkRow, value: newValueRow }; 112 | 113 | if (!linkRow) { 114 | if(newValueRow?.link_id) { 115 | throw new Error('Value insert is handled before the link is added.'); 116 | } else { // if(oldValueRow?.link_id) { 117 | throw new Error('Value deletion is handled after its link is deleted.'); 118 | } 119 | } 120 | 121 | log('operation', operation); 122 | log('linkId', linkId); 123 | log('linkRow', linkRow); 124 | log('oldValueRow', oldValueRow); 125 | log('newValueRow', newValueRow); 126 | log('newRow', newRow); 127 | log('oldRow', oldRow); 128 | 129 | if(oldValueRow && !newValueRow) { 130 | // delete bool_exp trash 131 | await deep.delete({ 132 | link_id: { _eq: oldValueRow.link_id }, 133 | }, { table: 'bool_exp' as any }); 134 | } 135 | if(newValueRow && newRow.type_id === deep.idLocal('@deep-foundation/core','Query')) { 136 | // generate new bool_exp sql version 137 | await boolExpToSQL(newRow.id, newRow?.value?.value); 138 | } 139 | 140 | await handleOperation('Update', triggeredByLinkId, oldRow, newRow, operation); 141 | await handleSelectorOperation('Update', triggeredByLinkId, oldRow, newRow, operation); 142 | 143 | return res.status(200).json({}); 144 | } catch (e) { 145 | const serializedError = serializeError(e); 146 | log('operation', operation); 147 | log('linkId', linkId); 148 | log('linkRow', linkRow); 149 | log('oldValueRow', oldValueRow); 150 | log('newValueRow', newValueRow); 151 | log('newRow', newRow); 152 | log('oldRow', oldRow); 153 | error('Error', JSON.stringify(serializedError, null, 2)); 154 | throw e; 155 | } 156 | } 157 | return res.status(500).json({ error: 'operation can be only INSERT or UPDATE' }); 158 | } catch (e) { 159 | const serializedError = serializeError(e); 160 | return res.status(500).json({ error: serializedError }); 161 | } 162 | }; -------------------------------------------------------------------------------- /imports/traveler.ts: -------------------------------------------------------------------------------- 1 | import { DeepClient, Exp } from "./client"; 2 | import { QueryLink } from "./client_types"; 3 | import { Id, Link } from "./minilinks"; 4 | 5 | type Direction = "from" | "to" | "type" | "out" | "in" | "typed" | "up" | "down"; 6 | 7 | type Mode = 'local' | 'remote'; 8 | 9 | interface Travel { 10 | query: Exp; 11 | direction?: Direction; 12 | } 13 | 14 | export const inversions = { 15 | from: 'out', 16 | to: 'in', 17 | type: 'typed', 18 | out: 'from', 19 | in: 'to', 20 | typed: 'type', 21 | up: 'down', 22 | down: 'up', 23 | }; 24 | 25 | export class Traveler { 26 | deep: DeepClient>; 27 | links: Link[] = []; 28 | travels: Travel<"links">[]; 29 | mode: Mode = 'remote'; 30 | 31 | constructor(deep, links: Link[] = [], travels: Travel[] = [], mode: Mode = 'remote') { 32 | this.deep = deep; 33 | this.links = links; 34 | this.travels = travels; 35 | this.mode = mode; 36 | } 37 | 38 | from(query?: Exp<"links">) { 39 | return new Traveler(this.deep, this.links, [...this.travels, { query: query, direction: 'from' }], this.mode); 40 | } 41 | to(query?: Exp<"links">) { 42 | return new Traveler(this.deep, this.links, [...this.travels, { query: query, direction: 'to' }], this.mode); 43 | } 44 | type(query?: Exp<"links">) { 45 | return new Traveler(this.deep, this.links, [...this.travels, { query: query, direction: 'type' }], this.mode); 46 | } 47 | out(query?: Exp<"links">) { 48 | return new Traveler(this.deep, this.links, [...this.travels, { query: query, direction: 'out' }], this.mode); 49 | } 50 | in(query?: Exp<"links">) { 51 | return new Traveler(this.deep, this.links, [...this.travels, { query: query, direction: 'in' }], this.mode); 52 | } 53 | typed(query?: Exp<"links">) { 54 | return new Traveler(this.deep, this.links, [...this.travels, { query: query, direction: 'typed' }], this.mode); 55 | } 56 | 57 | up(query?: Exp<"tree">) { 58 | return new Traveler(this.deep, this.links, [...this.travels, { query: query, direction: 'up' }], this.mode); 59 | } 60 | down(query?: Exp<"tree">) { 61 | return new Traveler(this.deep, this.links, [...this.travels, { query: query, direction: 'down' }], this.mode); 62 | } 63 | 64 | and(query?: Exp<"links">) { 65 | return new Traveler(this.deep, this.links, [...this.travels, { query: query }], this.mode); 66 | } 67 | 68 | get query(): Exp<"links"> { 69 | let current: any = { id: { _in: this.links.map(l => l.id) } }; 70 | for (let t = 0; t < this.travels.length; t++) { 71 | const travel = this.travels[t]; 72 | if (!travel.direction) { 73 | current = { _and: [(travel.query as any || {}), current] }; 74 | } else if (['up','down'].includes(inversions[travel.direction])) { 75 | current = { 76 | [inversions[travel.direction]]: { 77 | ...(travel.query as any || {}), 78 | [inversions[travel.direction] === 'down' ? 'link' : 'parent']: current, 79 | }, 80 | }; 81 | } else { 82 | current = { ...(travel.query as any || {}), [inversions[travel.direction]]: current }; 83 | } 84 | } 85 | return current; 86 | } 87 | 88 | select() { 89 | const query = this.query; 90 | if (this.mode === 'remote') { 91 | return this.deep.select(query); 92 | } else { 93 | return this.deep.minilinks.select(query as QueryLink); 94 | } 95 | } 96 | subscription() { 97 | const query = this.query; 98 | if (this.mode === 'remote') { 99 | return this.deep.subscribe(query); 100 | } else { 101 | return this.deep.minilinks.subscribe(query as QueryLink); 102 | } 103 | } 104 | 105 | count() { 106 | const query = this.query; 107 | if (this.mode === 'remote') { 108 | return this.deep.select(query, { aggregate: 'count' }); 109 | } else { 110 | return this.deep.minilinks.select(query as QueryLink)?.length; 111 | } 112 | } 113 | sum() { 114 | const query = this.query; 115 | if (this.mode === 'remote') { 116 | return this.deep.select(query, { aggregate: 'sum' }); 117 | } else { 118 | } 119 | } 120 | avg() { 121 | const query = this.query; 122 | if (this.mode === 'remote') { 123 | return this.deep.select(query, { aggregate: 'avg' }); 124 | } else { 125 | } 126 | } 127 | min() { 128 | const query = this.query; 129 | if (this.mode === 'remote') { 130 | return this.deep.select(query, { aggregate: 'min' }); 131 | } else { 132 | } 133 | } 134 | max() { 135 | const query = this.query; 136 | if (this.mode === 'remote') { 137 | return this.deep.select(query, { aggregate: 'max' }); 138 | } else { 139 | } 140 | } 141 | 142 | get local() { 143 | return new Traveler(this.deep, this.links, this.travels, 'local'); 144 | } 145 | get remote() { 146 | return new Traveler(this.deep, this.links, this.travels, 'remote'); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /integration.test.ts: -------------------------------------------------------------------------------- 1 | import { createRequire } from 'module'; 2 | import { jest } from '@jest/globals'; 3 | const require = createRequire(import.meta.url); 4 | 5 | require('dotenv').config(); 6 | 7 | jest.setTimeout(120000); 8 | 9 | // Please move slowest tests to bottom to make progress bar more dynamic and get more tests done first. 10 | 11 | import './tests/client'; 12 | import './tests/join-insert'; 13 | import './tests/typing'; 14 | import './tests/selectors'; 15 | import './tests/bool_exp'; 16 | import './tests/permissions'; 17 | import './tests/demo'; 18 | import './tests/packager'; 19 | // import './tests/messanger'; 20 | import './tests/tree'; 21 | import './tests/experimental/client' 22 | 23 | // Slow tests here: 24 | import './tests/sync-handlers'; 25 | import './tests/handlers'; -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | verbose: true, 3 | 4 | projects: [ 5 | { 6 | displayName: 'dom', 7 | testEnvironment: 'jsdom', 8 | testMatch: [ '**/?(*.)+(react.test).[jt]s?(x)' ] 9 | }, 10 | { 11 | displayName: 'node', 12 | testEnvironment: 'node', 13 | testMatch: [ '**/?(*.)+(integration|unit).test.[jt]s?(x)' ], 14 | }, 15 | ], 16 | }; -------------------------------------------------------------------------------- /migrations/1621815803560-auto-indexes.ts: -------------------------------------------------------------------------------- 1 | import { HasuraApi } from '@deep-foundation/hasura/api.js'; 2 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 3 | import { sql } from '@deep-foundation/hasura/sql.js'; 4 | import Debug from 'debug'; 5 | 6 | const debug = Debug('deeplinks:migrations:auto-indexes'); 7 | const log = debug.extend('log'); 8 | const error = debug.extend('error'); 9 | 10 | const client = generateApolloClient({ 11 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 12 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 13 | secret: process.env.MIGRATIONS_HASURA_SECRET, 14 | }); 15 | 16 | const api = new HasuraApi({ 17 | path: process.env.MIGRATIONS_HASURA_PATH, 18 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 19 | secret: process.env.MIGRATIONS_HASURA_SECRET, 20 | }); 21 | 22 | export const up = async () => { 23 | log('up'); 24 | await api.sql(sql`CREATE OR REPLACE FUNCTION create_btree_index(schema_name text, table_name text, column_name text) RETURNS void 25 | AS $$ 26 | BEGIN 27 | EXECUTE 'CREATE INDEX IF NOT EXISTS ' || table_name || '__' || column_name || '_btree ON ' || schema_name || '.' || table_name || ' USING btree (' || column_name || ');'; 28 | END; 29 | $$ LANGUAGE plpgsql;`); 30 | await api.sql(sql`CREATE OR REPLACE FUNCTION create_btree_indexes_for_all_columns(schema__name text, table__name text) RETURNS void 31 | AS $$ 32 | BEGIN 33 | PERFORM create_btree_index(table_schema, table_name, column_name) FROM information_schema.columns 34 | WHERE 35 | table_schema = schema__name 36 | AND table_name = table__name 37 | AND data_type != 'text' -- It is not recommended to index 'text' columns with 'btree' index (this index has a limit of key size - 2712 bytes, and 'text' columns can contain larger text strings) 38 | ; 39 | END; 40 | $$ LANGUAGE plpgsql;`); 41 | }; 42 | 43 | export const down = async () => { 44 | log('down'); 45 | await api.sql(sql`DROP FUNCTION create_btree_index;`); 46 | await api.sql(sql`DROP FUNCTION create_btree_indexes_for_all_columns;`); 47 | }; 48 | -------------------------------------------------------------------------------- /migrations/1621815803592-type-mp.ts: -------------------------------------------------------------------------------- 1 | import { HasuraApi } from '@deep-foundation/hasura/api.js'; 2 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 3 | import { Trigger } from '@deep-foundation/materialized-path/trigger.js'; 4 | import Debug from 'debug'; 5 | import { DeepClient } from '../imports/client.js'; 6 | import { TABLE_NAME as LINKS_TABLE_NAME } from './1616701513782-links.js'; 7 | import { _ids } from '../imports/client.js'; 8 | 9 | const debug = Debug('deeplinks:migrations:type-mp'); 10 | const log = debug.extend('log'); 11 | const error = debug.extend('error'); 12 | 13 | const client = generateApolloClient({ 14 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 15 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 16 | secret: process.env.MIGRATIONS_HASURA_SECRET, 17 | }); 18 | 19 | const deep = new DeepClient({ 20 | apolloClient: client, 21 | }); 22 | 23 | const api = new HasuraApi({ 24 | path: process.env.MIGRATIONS_HASURA_PATH, 25 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 26 | secret: process.env.MIGRATIONS_HASURA_SECRET, 27 | }); 28 | 29 | export const MP_TABLE_NAME = 'mp'; 30 | 31 | const createTrigger = async () => { 32 | return Trigger({ 33 | from_field: 'type_id', 34 | 35 | mpTableName: MP_TABLE_NAME, 36 | graphTableName: LINKS_TABLE_NAME, 37 | id_type: 'bigint', 38 | iteratorInsertDeclare: `groupRow bigint DEFAULT ${_ids?.['@deep-foundation/core']?.typesTree};`, 39 | iteratorDeleteArgumentSend: 'groupRow', 40 | iteratorDeleteArgumentGet: `groupRow bigint = ${_ids?.['@deep-foundation/core']?.typesTree}`, 41 | iteratorInsertBegin: ``, 42 | iteratorInsertEnd: '', 43 | groupInsert: 'groupRow', 44 | iteratorDeleteDeclare: `groupRow bigint DEFAULT ${_ids?.['@deep-foundation/core']?.typesTree};`, 45 | iteratorDeleteBegin: ``, 46 | iteratorDeleteEnd: '', 47 | groupDelete: 'groupRow', 48 | 49 | // TODO optimize duplicating equal selects 50 | 51 | isAllowSpreadFromCurrent: `TRUE`, 52 | isAllowSpreadCurrentTo: `FALSE`, 53 | 54 | isAllowSpreadToCurrent: `FALSE`, 55 | isAllowSpreadCurrentFrom: `FALSE`, 56 | 57 | isAllowSpreadToInCurrent: `FALSE`, 58 | isAllowSpreadCurrentFromOut: `FALSE`, 59 | 60 | isAllowSpreadFromOutCurrent: `FALSE`, 61 | isAllowSpreadCurrentToIn: `FALSE`, 62 | 63 | postfix: '__type', 64 | }); 65 | }; 66 | 67 | export const up = async () => { 68 | log('up'); 69 | const trigger = await createTrigger(); 70 | await api.sql(trigger.upFunctionInsertNode()); 71 | await api.sql(trigger.upFunctionDeleteNode()); 72 | await api.sql(trigger.upTriggerDelete()); 73 | await api.sql(trigger.upTriggerInsert()); 74 | }; 75 | 76 | export const down = async () => { 77 | const trigger = await createTrigger(); 78 | log('down'); 79 | log('dropTrigger'); 80 | await api.sql(trigger.downFunctionInsertNode()); 81 | await api.sql(trigger.downFunctionDeleteNode()); 82 | await api.sql(trigger.downTriggerDelete()); 83 | await api.sql(trigger.downTriggerInsert()); 84 | }; 85 | -------------------------------------------------------------------------------- /migrations/1622230000000-reserved-links.ts: -------------------------------------------------------------------------------- 1 | import Debug from 'debug'; 2 | import { api, TABLE_NAME as LINKS_TABLE_NAME } from './1616701513782-links.js'; 3 | import { sql } from '@deep-foundation/hasura/sql.js'; 4 | 5 | const debug = Debug('deeplinks:migrations:reserved-links'); 6 | const log = debug.extend('log'); 7 | const error = debug.extend('error'); 8 | 9 | const DEFAULT_SCHEMA = process.env.MIGRATIONS_SCHEMA || 'public'; 10 | const DEFAULT_RL_TABLE = process.env.MIGRATIONS_RL_TABLE || 'rl_example__links__reserved'; 11 | const DEFAULT_DATE_TYPE_SQL = process.env.MIGRATIONS_DATE_TYPE_SQL || 'timestamp'; 12 | const DEFAULT_RL_CRON_SCHEDULE = process.env.DEFAULT_RL_CRON_SCHEDULE || '0 * * * *'; 13 | const MIGRATIONS_DEEPLINKS_URL = process.env.MIGRATIONS_DEEPLINKS_URL || 'http://localhost:3006'; 14 | 15 | export const RL_TABLE_NAME = 'reserved'; 16 | 17 | export const upTable = async ({ 18 | SCHEMA = DEFAULT_SCHEMA, RL_TABLE = DEFAULT_RL_TABLE, DATE_TYPE = DEFAULT_DATE_TYPE_SQL, customColumns = '' 19 | } = {}) => { 20 | await api.sql(sql` 21 | CREATE TABLE ${SCHEMA}."${RL_TABLE}" (id bigint PRIMARY KEY, created_at ${DEFAULT_DATE_TYPE_SQL}, reserved_ids jsonb, user_id bigint${customColumns}); 22 | CREATE SEQUENCE ${RL_TABLE}_id_seq 23 | AS bigint START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; 24 | ALTER SEQUENCE ${RL_TABLE}_id_seq OWNED BY ${SCHEMA}."${RL_TABLE}".id; 25 | ALTER TABLE ONLY ${SCHEMA}."${RL_TABLE}" ALTER COLUMN id SET DEFAULT nextval('${RL_TABLE}_id_seq'::regclass); 26 | `); 27 | await api.query({ 28 | type: 'track_table', 29 | args: { 30 | schema: SCHEMA, 31 | name: RL_TABLE, 32 | }, 33 | }); 34 | }; 35 | 36 | export const downTable = async ({ 37 | SCHEMA = DEFAULT_SCHEMA, RL_TABLE = DEFAULT_RL_TABLE 38 | } = {}) => { 39 | await api.query({ 40 | type: 'untrack_table', 41 | args: { 42 | table: { 43 | schema: SCHEMA, 44 | name: RL_TABLE, 45 | }, 46 | }, 47 | }); 48 | await api.sql(sql` 49 | DROP TABLE ${SCHEMA}."${RL_TABLE}"; 50 | `); 51 | }; 52 | 53 | export const up = async () => { 54 | log('up'); 55 | log('table'); 56 | await upTable({ 57 | RL_TABLE: RL_TABLE_NAME, 58 | }); 59 | 60 | log('trigger'); 61 | await api.sql(sql`CREATE OR REPLACE FUNCTION ${LINKS_TABLE_NAME}__reserved__instead_of_insert__function() RETURNS TRIGGER AS $trigger$ 62 | BEGIN 63 | IF NEW.id IS NOT NULL THEN 64 | IF EXISTS( SELECT FROM ${RL_TABLE_NAME} as RL, ${LINKS_TABLE_NAME} AS LINKS WHERE RL.reserved_ids @> NEW.id::text::jsonb AND LINKS.type_id = 0 AND LINKS.id = NEW.id) THEN 65 | DELETE FROM ${LINKS_TABLE_NAME} WHERE id = NEW.id; 66 | RETURN NEW; 67 | ELSE 68 | IF (NOT EXISTS (SELECT * FROM "links_id_seq" WHERE last_value >= NEW.id)) THEN 69 | RAISE EXCEPTION 'Illegal insert link with custom id with { id: %, type_id: %, from_id: %, to_id: % } %.', NEW.id, NEW.type_id, NEW.from_id, NEW.to_id, (SELECT row_to_json(t) FROM "links_id_seq" as t) USING HINT = 'Use reserve action before inserting link with id'; 70 | END IF; 71 | END IF; 72 | END IF; 73 | RETURN NEW; 74 | END; $trigger$ LANGUAGE plpgsql;`); 75 | await api.sql(sql`CREATE TRIGGER ${LINKS_TABLE_NAME}__reserved__instead_of_insert__trigger BEFORE INSERT ON "${LINKS_TABLE_NAME}" FOR EACH ROW WHEN (pg_trigger_depth() < 1 AND NEW.type_id != 0) EXECUTE PROCEDURE ${LINKS_TABLE_NAME}__reserved__instead_of_insert__function();`); 76 | 77 | log('cron_trigger'); 78 | await api.query({ 79 | type: 'create_cron_trigger', 80 | args: { 81 | name: 'reserved_links_cleaner', 82 | webhook: `${MIGRATIONS_DEEPLINKS_URL}/api/reserved-cleaner`, 83 | schedule: DEFAULT_RL_CRON_SCHEDULE, 84 | include_in_metadata: true, 85 | payload: {}, 86 | retry_conf: { 87 | num_retries: 3, 88 | timeout_seconds: 120, 89 | tolerance_seconds: 21675, 90 | retry_interval_seconds: 12 91 | }, 92 | comment: 'clean reserved' 93 | } 94 | }); 95 | 96 | log('action_types'); 97 | await api.query({ 98 | type: 'set_custom_types', 99 | args: { 100 | scalars: [], 101 | enums: [], 102 | input_objects: [], 103 | objects: [ 104 | { 105 | name: 'reserveResponse', 106 | fields: [ 107 | { 108 | name: 'ids', 109 | type: '[String!]!' 110 | } 111 | ] 112 | } 113 | ] 114 | } 115 | }); 116 | log('action'); 117 | await api.query({ 118 | type: 'create_action', 119 | args: { 120 | name: 'reserve', 121 | definition: { 122 | kind: 'synchronous', 123 | type: 'mutation', 124 | forward_client_headers: true, 125 | arguments: [ 126 | { 127 | name: 'count', 128 | type: 'Int!' 129 | }, 130 | ], 131 | output_type: 'reserveResponse', 132 | handler: `${MIGRATIONS_DEEPLINKS_URL}/api/reserved` 133 | } 134 | } 135 | }); 136 | await api.metadata({ 137 | type: 'create_action_permission', 138 | args: { 139 | action: 'reserve', 140 | role: 'link', 141 | } 142 | }); 143 | }; 144 | 145 | export const down = async () => { 146 | log('down'); 147 | log('action'); 148 | await api.query({ 149 | type:'drop_action', 150 | args:{ 151 | name:'reserve', 152 | clear_data: true 153 | } 154 | }); 155 | log('cron_trigger'); 156 | await api.query({ 157 | type: 'delete_cron_trigger', 158 | args: { 159 | name: 'reserved_links_cleaner', 160 | } 161 | }); 162 | log('trigger'); 163 | await api.sql(sql` 164 | DROP TRIGGER IF EXISTS ${LINKS_TABLE_NAME}__reserved__instead_of_insert__trigger ON ${LINKS_TABLE_NAME} CASCADE; 165 | DROP FUNCTION IF EXISTS ${LINKS_TABLE_NAME}__reserved__instead_of_insert__function() CASCADE; 166 | `); 167 | log('table'); 168 | await downTable({ 169 | RL_TABLE: RL_TABLE_NAME, 170 | }); 171 | }; 172 | -------------------------------------------------------------------------------- /migrations/1622421760250-values.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { generateMutation, generateSerial, insertMutation } from '../imports/gql/index.js'; 4 | import { TABLE_NAME as LINKS_TABLE_NAME } from './1616701513782-links.js'; 5 | import times from 'lodash/times'; 6 | import { time } from 'console'; 7 | import { Packager, Package } from '../imports/packager.js'; 8 | import { DeepClient } from '../imports/client.js'; 9 | import { HasuraApi } from '@deep-foundation/hasura/api.js'; 10 | import { sql } from '@deep-foundation/hasura/sql.js'; 11 | import { generateUp, generateDown } from '../imports/type-table.js'; 12 | 13 | export const BOOL_EXP_TABLE_NAME = 'bool_exp'; 14 | 15 | const debug = Debug('deeplinks:migrations:values'); 16 | const log = debug.extend('log'); 17 | const error = debug.extend('error'); 18 | 19 | const apolloClient = generateApolloClient({ 20 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 21 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 22 | secret: process.env.MIGRATIONS_HASURA_SECRET, 23 | }); 24 | 25 | export const api = new HasuraApi({ 26 | path: process.env.MIGRATIONS_HASURA_PATH, 27 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 28 | secret: process.env.MIGRATIONS_HASURA_SECRET, 29 | }); 30 | 31 | const deep = new DeepClient({ apolloClient }); 32 | 33 | export const up = async () => { 34 | log('up'); 35 | await (generateUp({ 36 | schemaName: 'public', 37 | tableName: 'strings', 38 | valueType: 'TEXT NOT NULL DEFAULT ""', 39 | customColumnsSql: 'value text', 40 | linkRelation: 'string', 41 | linksTableName: 'links', 42 | api, 43 | deep, 44 | })()); 45 | await (generateUp({ 46 | schemaName: 'public', 47 | tableName: 'numbers', 48 | valueType: 'numeric NOT NULL DEFAULT 0', 49 | customColumnsSql: 'value numeric', 50 | linkRelation: 'number', 51 | linksTableName: 'links', 52 | api, 53 | deep, 54 | })()); 55 | await (generateUp({ 56 | schemaName: 'public', 57 | tableName: 'objects', 58 | valueType: 'jsonb NOT NULL DEFAULT \'{}\'::jsonb', 59 | customColumnsSql: 'value jsonb', 60 | linkRelation: 'object', 61 | linksTableName: 'links', 62 | api, 63 | deep, 64 | })()); 65 | await (generateUp({ 66 | schemaName: 'public', 67 | tableName: BOOL_EXP_TABLE_NAME, 68 | valueType: 'TEXT', 69 | customColumnsSql: 'value text', 70 | api, 71 | deep, 72 | })()); 73 | }; 74 | 75 | export const down = async () => { 76 | log('down'); 77 | await (generateDown({ 78 | schemaName: 'public', 79 | tableName: 'strings', 80 | valueType: 'TEXT', 81 | customColumnsSql: 'value text', 82 | linkRelation: 'string', 83 | linksTableName: 'links', 84 | api, 85 | deep, 86 | })()); 87 | await (generateDown({ 88 | schemaName: 'public', 89 | tableName: 'numbers', 90 | valueType: 'float8', 91 | customColumnsSql: 'value bigint', 92 | linkRelation: 'number', 93 | linksTableName: 'links', 94 | api, 95 | deep, 96 | })()); 97 | await (generateDown({ 98 | schemaName: 'public', 99 | tableName: 'objects', 100 | valueType: 'jsonb', 101 | customColumnsSql: 'value jsonb', 102 | linkRelation: 'object', 103 | linksTableName: 'links', 104 | api, 105 | deep, 106 | })()); 107 | await (generateDown({ 108 | schemaName: 'public', 109 | tableName: BOOL_EXP_TABLE_NAME, 110 | valueType: 'TEXT', 111 | customColumnsSql: 'value text', 112 | api, 113 | deep, 114 | })()); 115 | }; -------------------------------------------------------------------------------- /migrations/1623023831753-jwt.ts: -------------------------------------------------------------------------------- 1 | import { HasuraApi } from '@deep-foundation/hasura/api.js'; 2 | import Debug from 'debug'; 3 | import { typeDefsString as gs } from '../imports/router/guest.js'; 4 | import { typeDefsString as js } from '../imports/router/jwt.js'; 5 | 6 | const debug = Debug('deeplinks:migrations:jwt'); 7 | const log = debug.extend('log'); 8 | const error = debug.extend('error'); 9 | 10 | const api = new HasuraApi({ 11 | path: process.env.MIGRATIONS_HASURA_PATH, 12 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 13 | secret: process.env.MIGRATIONS_HASURA_SECRET, 14 | }); 15 | 16 | export const up = async () => { 17 | log('up'); 18 | await api.query({ 19 | type: 'add_remote_schema', 20 | args: { 21 | name: 'jwt', 22 | definition: { 23 | url: `${process.env.MIGRATIONS_DEEPLINKS_URL}/api/jwt`, 24 | headers: [{ name: 'x-hasura-client', value: 'deeplinks-jwt' }], 25 | forward_client_headers: true, 26 | timeout_seconds: 120 27 | }, 28 | } 29 | }); 30 | await api.query({ 31 | type: 'add_remote_schema', 32 | args: { 33 | name: 'guest', 34 | definition: { 35 | url: `${process.env.MIGRATIONS_DEEPLINKS_URL}/api/guest`, 36 | headers: [{ name: 'x-hasura-client', value: 'deeplinks-guest' }], 37 | forward_client_headers: true, 38 | timeout_seconds: 60 39 | }, 40 | } 41 | }); 42 | await api.metadata({ 43 | type: "add_remote_schema_permissions", 44 | args: { 45 | remote_schema: 'guest', 46 | role: 'link', 47 | definition: { 48 | schema: gs, 49 | }, 50 | }, 51 | }); 52 | await api.metadata({ 53 | type: "add_remote_schema_permissions", 54 | args: { 55 | remote_schema: 'guest', 56 | role: 'undefined', 57 | definition: { 58 | schema: gs, 59 | }, 60 | }, 61 | }); 62 | await api.metadata({ 63 | type: "add_remote_schema_permissions", 64 | args: { 65 | remote_schema: 'jwt', 66 | role: 'link', 67 | definition: { 68 | schema: js, 69 | }, 70 | }, 71 | }); 72 | await api.metadata({ 73 | type: "add_remote_schema_permissions", 74 | args: { 75 | remote_schema: 'jwt', 76 | role: 'undefined', 77 | definition: { 78 | schema: js, 79 | }, 80 | }, 81 | }); 82 | }; 83 | 84 | export const down = async () => { 85 | log('down'); 86 | await api.query({ 87 | type: 'remove_remote_schema', 88 | args: { 89 | name: 'jwt', 90 | }, 91 | }); 92 | await api.query({ 93 | type: 'remove_remote_schema', 94 | args: { 95 | name: 'guest', 96 | }, 97 | }); 98 | }; 99 | -------------------------------------------------------------------------------- /migrations/1637975150573-packager.ts: -------------------------------------------------------------------------------- 1 | import { HasuraApi } from '@deep-foundation/hasura/api.js'; 2 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 3 | import Debug from 'debug'; 4 | import { DeepClient } from '../imports/client.js'; 5 | import { typeDefsString } from '../imports/router/packager.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:packager'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | const api = new HasuraApi({ 12 | path: process.env.MIGRATIONS_HASURA_PATH, 13 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 14 | secret: process.env.MIGRATIONS_HASURA_SECRET, 15 | }); 16 | 17 | const client = generateApolloClient({ 18 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 19 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 20 | secret: process.env.MIGRATIONS_HASURA_SECRET, 21 | }); 22 | 23 | const deep = new DeepClient({ 24 | apolloClient: client, 25 | }) 26 | 27 | export const up = async () => { 28 | log('up'); 29 | await api.query({ 30 | type: 'add_remote_schema', 31 | args: { 32 | name: 'packager', 33 | definition: { 34 | url: `${process.env.MIGRATIONS_DEEPLINKS_URL}/api/packager`, 35 | headers: [{ name: 'x-hasura-client', value: 'deeplinks-packager' }], 36 | forward_client_headers: true, 37 | timeout_seconds: 60 38 | }, 39 | } 40 | }); 41 | await api.metadata({ 42 | type: "add_remote_schema_permissions", 43 | args: { 44 | remote_schema: 'packager', 45 | role: 'link', 46 | definition: { 47 | schema: typeDefsString, 48 | }, 49 | }, 50 | }); 51 | }; 52 | 53 | export const down = async () => { 54 | log('down'); 55 | await api.query({ 56 | type: 'remove_remote_schema', 57 | args: { 58 | name: 'packager', 59 | }, 60 | }); 61 | }; 62 | -------------------------------------------------------------------------------- /migrations/1657720948151-handlers.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import { sql } from '@deep-foundation/hasura/sql.js'; 3 | import Debug from 'debug'; 4 | import { DeepClient } from '../imports/client.js'; 5 | import { permissions } from '../imports/permission.js'; 6 | import { api, SCHEMA } from './1616701513782-links.js'; 7 | import { linksPermissions } from './1622421760260-permissions.js'; 8 | 9 | const debug = Debug('deeplinks:migrations:handlers'); 10 | const log = debug.extend('log'); 11 | const error = debug.extend('error'); 12 | 13 | export const HANDLERS_TABLE_NAME = 'handlers'; 14 | export const TABLE_NAME = 'links'; 15 | 16 | const client = generateApolloClient({ 17 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 18 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 19 | secret: process.env.MIGRATIONS_HASURA_SECRET, 20 | }); 21 | 22 | const deep = new DeepClient({ 23 | apolloClient: client, 24 | }) 25 | 26 | export const up = async () => { 27 | log('up'); 28 | log('view'); 29 | 30 | await api.sql(sql` 31 | CREATE VIEW ${HANDLERS_TABLE_NAME} AS 32 | SELECT DISTINCT 33 | coalesce((SELECT gl.from_id as id FROM links gl WHERE (gl.type_id = ${deep.idLocal('@deep-foundation/core', 'GeneratedFrom')}) AND (gl.from_id = handling_link.id OR gl.to_id = handling_link.id) LIMIT 1), handling_link.id) AS dist_id, 34 | coalesce((SELECT gl.to_id as id FROM links gl WHERE (gl.type_id = ${deep.idLocal('@deep-foundation/core', 'GeneratedFrom')}) AND (gl.from_id = handling_link.id OR gl.to_id = handling_link.id) LIMIT 1), handling_link.id) AS src_id, 35 | handler_link.id AS handler_id, 36 | supports_link.from_id AS isolation_provider_id, 37 | supports_link.to_id AS execution_provider_id 38 | FROM links handling_link, 39 | links handler_link, 40 | links supports_link 41 | WHERE ( 42 | (handler_link.type_id = ${deep.idLocal('@deep-foundation/core', 'Handler')}) 43 | AND (handler_link.from_id = supports_link.id) 44 | AND (supports_link.type_id = ${deep.idLocal('@deep-foundation/core', 'Supports')}) 45 | AND (handler_link.to_id = handling_link.id) 46 | ); 47 | `); 48 | await api.query({ 49 | type: 'track_table', 50 | args: { 51 | schema: SCHEMA, 52 | name: HANDLERS_TABLE_NAME, 53 | }, 54 | }); 55 | const relationsFields = ['src', 'dist', 'handler', 'isolation_provider', 'execution_provider']; 56 | for (const field of relationsFields) { 57 | await api.query({ 58 | type: 'create_object_relationship', 59 | args: { 60 | table: HANDLERS_TABLE_NAME, 61 | name: field, 62 | using: { 63 | manual_configuration: { 64 | remote_table: { 65 | schema: SCHEMA, 66 | name: TABLE_NAME, 67 | }, 68 | column_mapping: { 69 | [`${field}_id`]: 'id', 70 | }, 71 | }, 72 | }, 73 | }, 74 | }); 75 | } 76 | const permissionFields = ['dist']; 77 | const selectPermissionsLinks: any[] = []; 78 | for (const field of permissionFields) { 79 | selectPermissionsLinks.push({ 80 | [field]: (await linksPermissions(['$','link_id'], 'X-Hasura-User-Id', 'link')).select, 81 | }); 82 | } 83 | await permissions(api, HANDLERS_TABLE_NAME, { 84 | role: 'link', 85 | 86 | select: { 87 | _and: selectPermissionsLinks, 88 | }, 89 | insert: {}, 90 | update: {}, 91 | delete: {}, 92 | 93 | columns: '*', 94 | computed_fields: [], 95 | }); 96 | await permissions(api, HANDLERS_TABLE_NAME, { 97 | role: 'undefined', 98 | 99 | select: { 100 | dist_id: { _is_null: true }, 101 | }, 102 | insert: {}, 103 | update: {}, 104 | delete: {}, 105 | 106 | columns: '*', 107 | computed_fields: [], 108 | }); 109 | }; 110 | 111 | export const down = async () => { 112 | log('down'); 113 | log('view'); 114 | await api.query({ 115 | type: 'untrack_table', 116 | args: { 117 | table: { 118 | schema: SCHEMA, 119 | name: HANDLERS_TABLE_NAME, 120 | }, 121 | cascade: true, 122 | }, 123 | }); 124 | await api.sql(sql` 125 | DROP VIEW IF EXISTS ${HANDLERS_TABLE_NAME} CASCADE; 126 | `); 127 | }; -------------------------------------------------------------------------------- /migrations/1658622099992-unvalue.ts: -------------------------------------------------------------------------------- 1 | import { HasuraApi } from '@deep-foundation/hasura/api.js'; 2 | import { sql } from '@deep-foundation/hasura/sql.js'; 3 | import Debug from 'debug'; 4 | 5 | const debug = Debug('deeplinks:migrations:links'); 6 | const log = debug.extend('log'); 7 | const error = debug.extend('error'); 8 | 9 | export const api = new HasuraApi({ 10 | path: process.env.MIGRATIONS_HASURA_PATH, 11 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 12 | secret: process.env.MIGRATIONS_HASURA_SECRET, 13 | }); 14 | 15 | export const SCHEMA = 'public'; 16 | export const TABLE_NAME = 'links'; 17 | 18 | export const up = async () => { 19 | log('up'); 20 | await api.sql(sql` 21 | CREATE OR REPLACE FUNCTION public.${TABLE_NAME}__unvalue__delete_links__function() 22 | RETURNS trigger 23 | LANGUAGE plpgsql 24 | AS $function$ 25 | BEGIN 26 | DELETE FROM strings WHERE "link_id" = OLD."id"; 27 | DELETE FROM numbers WHERE "link_id" = OLD."id"; 28 | DELETE FROM objects WHERE "link_id" = OLD."id"; 29 | RETURN OLD; 30 | END; 31 | $function$; 32 | CREATE TRIGGER ${TABLE_NAME}__unvalue__delete_links__trigger AFTER DELETE ON "${TABLE_NAME}" FOR EACH ROW EXECUTE PROCEDURE ${TABLE_NAME}__unvalue__delete_links__function(); 33 | `); 34 | }; 35 | 36 | export const down = async () => { 37 | log('down'); 38 | await api.sql(sql` 39 | DROP TRIGGER IF EXISTS ${TABLE_NAME}__unvalue__delete_links__trigger ON ${TABLE_NAME} CASCADE; 40 | DROP FUNCTION IF EXISTS ${TABLE_NAME}__unvalue__delete_links__function() CASCADE; 41 | `); 42 | }; 43 | -------------------------------------------------------------------------------- /migrations/1661392616544-core-symbols.ts: -------------------------------------------------------------------------------- 1 | import Debug from 'debug'; 2 | import { coreSymbolsPckg } from '../imports/core-symbols.js'; 3 | import { importPackage, packageExists } from './1664940577200-tsx.js'; 4 | 5 | const debug = Debug('deeplinks:migrations:core-symbols'); 6 | const log = debug.extend('log'); 7 | const error = debug.extend('error'); 8 | 9 | export const up = async () => { 10 | log('up'); 11 | if (!await packageExists('@deep-foundation/core-symbols')) { 12 | const importResult = await importPackage(coreSymbolsPckg); 13 | log(importResult); 14 | } 15 | }; 16 | 17 | export const down = async () => { 18 | log('down'); 19 | }; -------------------------------------------------------------------------------- /migrations/1664940577200-tsx.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import tsxPckg from '@deep-foundation/tsx/deep.json' assert { type: 'json'}; 5 | import { Packager } from '../imports/packager.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:tsx'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | const rootClient = generateApolloClient({ 12 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 13 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 14 | secret: process.env.MIGRATIONS_HASURA_SECRET, 15 | }); 16 | 17 | const root = new DeepClient({ 18 | apolloClient: rootClient, 19 | }); 20 | 21 | export const importPackage = async (pckg) => { 22 | const packager = new Packager(root); 23 | const importResult = await packager.import(pckg); 24 | const { errors, packageId, namespaceId } = importResult; 25 | if (errors?.length) { 26 | const error = errors[0]?.graphQLErrors?.[0]?.extensions?.internal?.error; 27 | throw new Error(`Import error: ${String(errors[0]?.graphQLErrors?.[0]?.message || errors?.[0])}${error?.message ? ` ${error?.message} ${error?.request?.method} ${error?.request?.host}:${error?.request?.port}${error?.request?.path}` : ''}`); 28 | } 29 | return importResult; 30 | } 31 | 32 | export const packageExists = async (name) => { 33 | try { 34 | const result = (await root.id(name)) > 0; 35 | // console.log('packageExists', 'result', result); 36 | return result; 37 | } catch { 38 | // console.log('packageExists', 'result', false); 39 | return false; 40 | } 41 | } 42 | 43 | export const sharePermissions = async (userId, packageId) => { 44 | return await root.insert({ 45 | type_id: await root.id('@deep-foundation/core', 'Join'), 46 | from_id: packageId, 47 | to_id: userId, 48 | }); 49 | } 50 | 51 | export const containWithin = async (containerId, containedId) => { 52 | return await root.insert({ 53 | type_id: await root.id('@deep-foundation/core', 'Contain'), 54 | from_id: containerId, 55 | to_id: containedId, 56 | }); 57 | } 58 | 59 | export const up = async () => { 60 | log('up'); 61 | if (!await packageExists('@deep-foundation/tsx')) { 62 | const importResult = await importPackage(tsxPckg); 63 | log(importResult); 64 | const packageId = importResult.packageId; 65 | const adminId = await root.id('deep', 'admin'); 66 | await sharePermissions(adminId, packageId); 67 | await containWithin(adminId, packageId); 68 | } 69 | }; 70 | 71 | export const down = async () => { 72 | log('down'); 73 | }; -------------------------------------------------------------------------------- /migrations/1664940577209-deepcase.ts: -------------------------------------------------------------------------------- 1 | import Debug from 'debug'; 2 | 3 | const debug = Debug('deeplinks:migrations:deepcase'); 4 | const log = debug.extend('log'); 5 | const error = debug.extend('error'); 6 | 7 | export const up = async () => { 8 | log('up'); 9 | // disable support deepcase and make migrations in gh-actions faster 10 | // moved to packages/deeplinks/migrations/1678940577209-deepcase.ts 11 | }; 12 | 13 | export const down = async () => { 14 | log('down'); 15 | }; -------------------------------------------------------------------------------- /migrations/1677340638677-npm-packager.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import npmPackagerPckg from '@deep-foundation/npm-packager/deep.json' assert { type: 'json'}; 5 | import { importPackage, sharePermissions, containWithin, packageExists } from './1664940577200-tsx.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:npm-packager'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | const rootClient = generateApolloClient({ 12 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 13 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 14 | secret: process.env.MIGRATIONS_HASURA_SECRET, 15 | }); 16 | 17 | const root = new DeepClient({ 18 | apolloClient: rootClient, 19 | }); 20 | 21 | export const up = async () => { 22 | log('up'); 23 | if (!await packageExists('@deep-foundation/npm-packager')) { 24 | const importResult = await importPackage(npmPackagerPckg); 25 | log(importResult); 26 | const packageId = importResult.packageId; 27 | const adminId = await root.id('deep', 'admin'); 28 | await sharePermissions(adminId, packageId); 29 | await containWithin(adminId, packageId); 30 | } 31 | }; 32 | 33 | export const down = async () => { 34 | log('down'); 35 | }; -------------------------------------------------------------------------------- /migrations/1678940577209-deepcase.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import { packageExists, sharePermissions } from './1664940577200-tsx.js'; 5 | 6 | const debug = Debug('deeplinks:migrations:deepcase'); 7 | const log = debug.extend('log'); 8 | const error = debug.extend('error'); 9 | 10 | const rootClient = generateApolloClient({ 11 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 12 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 13 | secret: process.env.MIGRATIONS_HASURA_SECRET, 14 | }); 15 | 16 | const root = new DeepClient({ 17 | apolloClient: rootClient, 18 | }); 19 | 20 | export const installPackage = async (deep, packageName) => { 21 | const adminId = await deep.id('deep', 'admin'); 22 | const promiseTree = deep.idLocal('@deep-foundation/core', 'promiseTree'); 23 | 24 | log('adminId', adminId); 25 | const packageQueryTypeId = deep.idLocal('@deep-foundation/core', 'PackageQuery'); 26 | log('packageQueryTypeId', packageQueryTypeId); 27 | const installTypeId = await deep.id('@deep-foundation/npm-packager', 'Install'); 28 | log('installTypeId', installTypeId); 29 | 30 | const { data: [{ id: installId = undefined } = {}] = []} = {} = await deep.insert({ 31 | from_id: adminId, 32 | type_id: installTypeId, 33 | to: { 34 | data: { 35 | type_id: packageQueryTypeId, 36 | string: { data: { value: packageName } } 37 | } 38 | } 39 | }); 40 | 41 | await deep.await(installId); 42 | console.log(JSON.stringify((await deep.select({ 43 | up: { tree_id: promiseTree, parent_id: installId }, 44 | })).data, null, 2)); 45 | 46 | const packageId = await deep.id(packageName); 47 | log(`${packageName} package is installed as ${packageId} link.`) 48 | return packageId; 49 | } 50 | 51 | export const up = async () => { 52 | log('up'); 53 | // disable support deepcase and make migrations in gh-actions faster 54 | // const packageName = '@deep-foundation/deepcase'; 55 | // if (!await packageExists(packageName)) { 56 | // const adminId = await root.id('deep', 'admin'); 57 | 58 | // const admin = await root.login({ linkId: adminId }); 59 | // const deep = new DeepClient({ deep: root, ...admin }); 60 | 61 | // log('adminId', adminId); 62 | // const packageId = await installPackage(deep, packageName); 63 | // await sharePermissions(adminId, packageId); 64 | 65 | // const usersCanInsertSafeLinks = await deep.id('deep', 'admin', 'usersCanInsertSafeLinks'); 66 | // const usersCanUpdateSafeLinks = await deep.id('deep', 'admin', 'usersCanUpdateSafeLinks'); 67 | // const usersCanDeleteSafeLinks = await deep.id('deep', 'admin', 'usersCanDeleteSafeLinks'); 68 | 69 | // const { data: rules } = await deep.select({ 70 | // 'up': { 71 | // 'parent_id': { _in: [usersCanInsertSafeLinks, usersCanUpdateSafeLinks, usersCanDeleteSafeLinks] }, 72 | // } 73 | // }); 74 | // deep.minilinks.apply([...rules]); 75 | // const insertSelector = deep.minilinks.byId[usersCanInsertSafeLinks]?.outByType?.[deep.idLocal('@deep-foundation/core', 'RuleObject')]?.[0]?.to?.id; 76 | // const updateSelector = deep.minilinks.byId[usersCanUpdateSafeLinks]?.outByType?.[deep.idLocal('@deep-foundation/core', 'RuleObject')]?.[0]?.to?.id; 77 | // const deleteSelector = deep.minilinks.byId[usersCanDeleteSafeLinks]?.outByType?.[deep.idLocal('@deep-foundation/core', 'RuleObject')]?.[0]?.to?.id; 78 | // await deep.insert([ 79 | // { 80 | // type_id: deep.idLocal('@deep-foundation/core', 'SelectorInclude'), 81 | // from_id: insertSelector, 82 | // to_id: await deep.id(packageName, 'Traveler'), 83 | // out: { data: { 84 | // type_id: deep.idLocal('@deep-foundation/core', 'SelectorTree'), 85 | // to_id: deep.idLocal('@deep-foundation/core', 'containTree'), 86 | // } }, 87 | // }, 88 | // { 89 | // type_id: deep.idLocal('@deep-foundation/core', 'SelectorInclude'), 90 | // from_id: updateSelector, 91 | // to_id: await deep.id(packageName, 'Traveler'), 92 | // out: { data: { 93 | // type_id: deep.idLocal('@deep-foundation/core', 'SelectorTree'), 94 | // to_id: deep.idLocal('@deep-foundation/core', 'containTree'), 95 | // } }, 96 | // }, 97 | // { 98 | // type_id: deep.idLocal('@deep-foundation/core', 'SelectorInclude'), 99 | // from_id: deleteSelector, 100 | // to_id: await deep.id(packageName, 'Traveler'), 101 | // out: { data: { 102 | // type_id: deep.idLocal('@deep-foundation/core', 'SelectorTree'), 103 | // to_id: deep.idLocal('@deep-foundation/core', 'containTree'), 104 | // } }, 105 | // }, 106 | // ]); 107 | // } 108 | }; 109 | 110 | export const down = async () => { 111 | log('down'); 112 | }; -------------------------------------------------------------------------------- /migrations/1680017137379-npm-packager-ui.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import { installPackage } from './1678940577209-deepcase.js'; 5 | import { packageExists, sharePermissions } from './1664940577200-tsx.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:npm-packager-ui'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | const rootClient = generateApolloClient({ 12 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 13 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 14 | secret: process.env.MIGRATIONS_HASURA_SECRET, 15 | }); 16 | 17 | const root = new DeepClient({ 18 | apolloClient: rootClient, 19 | }); 20 | 21 | export const up = async () => { 22 | log('up'); 23 | // disable support deepcase and make migrations in gh-actions faster 24 | // const packageName = '@deep-foundation/npm-packager-ui'; 25 | // if (!await packageExists(packageName)) { 26 | // const adminId = await root.id('deep', 'admin'); 27 | // const admin = await root.login({ linkId: adminId }); 28 | // const deep = new DeepClient({ deep: root, ...admin }); 29 | 30 | // const packageId = await installPackage(deep, packageName); 31 | // await sharePermissions(adminId, packageId); 32 | // } 33 | }; 34 | 35 | export const down = async () => { 36 | log('down'); 37 | }; -------------------------------------------------------------------------------- /migrations/1682327540225-mathjs-schema-packages.ts: -------------------------------------------------------------------------------- 1 | 2 | import Debug from 'debug'; 3 | import { api, TABLE_NAME as LINKS_TABLE_NAME } from './1616701513782-links.js'; 4 | import { sql } from '@deep-foundation/hasura/sql.js'; 5 | import { createRequire } from "module"; 6 | const require = createRequire(import.meta.url); 7 | 8 | const debug = Debug('deeplinks:migrations:plv8'); 9 | const log = debug.extend('log'); 10 | const error = debug.extend('error'); 11 | 12 | const mathjsPackage = require('../bundles/mathjs.js').code; 13 | const jsonschemaPackage = require('../bundles/jsonschema.js').code; 14 | 15 | export const createMathjsFabric = sql`CREATE OR REPLACE FUNCTION ${LINKS_TABLE_NAME}__sync__handlers__mathjs__package() RETURNS jsonb AS $package$ const sync__handlers__package = ${mathjsPackage}; return sync__handlers__package(); $package$ LANGUAGE plv8;`; 16 | 17 | export const dropMathjsFabric = sql`DROP FUNCTION IF EXISTS ${LINKS_TABLE_NAME}__sync__handlers__mathjs__package CASCADE;`; 18 | 19 | export const createJsonschemaFabric = sql`CREATE OR REPLACE FUNCTION ${LINKS_TABLE_NAME}__sync__handlers__jsonschema__package() RETURNS jsonb AS $package$ const sync__handlers__package = ${jsonschemaPackage}; return sync__handlers__package(); $package$ LANGUAGE plv8;`; 20 | 21 | export const dropJsonschemaFabric = sql`DROP FUNCTION IF EXISTS ${LINKS_TABLE_NAME}__sync__handlers__jsonschema__package CASCADE;`; 22 | 23 | export const up = async () => { 24 | log('up'); 25 | 26 | await api.sql(createMathjsFabric); 27 | await api.sql(createJsonschemaFabric); 28 | }; 29 | 30 | export const down = async () => { 31 | log('down'); 32 | 33 | await api.sql(dropJsonschemaFabric); 34 | await api.sql(dropMathjsFabric); 35 | }; -------------------------------------------------------------------------------- /migrations/1687790060025-finder.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import { installPackage } from './1678940577209-deepcase.js'; 5 | import { packageExists, sharePermissions } from './1664940577200-tsx.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:finder'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | const rootClient = generateApolloClient({ 12 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 13 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 14 | secret: process.env.MIGRATIONS_HASURA_SECRET, 15 | }); 16 | 17 | const root = new DeepClient({ 18 | apolloClient: rootClient, 19 | }); 20 | 21 | export const up = async () => { 22 | log('up'); 23 | // disable support deepcase and make migrations in gh-actions faster 24 | // const packageName = '@deep-foundation/finder'; 25 | // if (!await packageExists(packageName)) { 26 | // const adminId = await root.id('deep', 'admin'); 27 | // const admin = await root.login({ linkId: adminId }); 28 | // const deep = new DeepClient({ deep: root, ...admin }); 29 | 30 | // const packageId = await installPackage(deep, '@deep-foundation/finder'); 31 | // await sharePermissions(adminId, packageId); 32 | // } 33 | }; 34 | 35 | export const down = async () => { 36 | log('down'); 37 | }; -------------------------------------------------------------------------------- /migrations/1689630159473-healthz.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import { sql } from '@deep-foundation/hasura/sql.js'; 3 | import Debug from 'debug'; 4 | import { DeepClient } from '../imports/client.js'; 5 | import { api, SCHEMA } from './1616701513782-links.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:healthz'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | export const HEALTHZ_VIEW_NAME = 'healthz'; 12 | 13 | const client = generateApolloClient({ 14 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 15 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 16 | secret: process.env.MIGRATIONS_HASURA_SECRET, 17 | }); 18 | 19 | const deep = new DeepClient({ 20 | apolloClient: client, 21 | }) 22 | 23 | export const up = async () => { 24 | log('up'); 25 | log('view'); 26 | 27 | await api.sql(sql`CREATE VIEW ${HEALTHZ_VIEW_NAME} AS SELECT 'ok'::text as "status";`); 28 | await api.query({ 29 | type: 'track_table', 30 | args: { 31 | schema: SCHEMA, 32 | name: HEALTHZ_VIEW_NAME, 33 | }, 34 | }); 35 | await api.query({ 36 | type: 'create_select_permission', 37 | args: { 38 | table: HEALTHZ_VIEW_NAME, 39 | role: 'undefined', 40 | permission: { 41 | columns: '*', 42 | filter: {}, 43 | limit: 999, 44 | allow_aggregations: true 45 | } 46 | } 47 | }); 48 | }; 49 | 50 | export const down = async () => { 51 | log('down'); 52 | log('view'); 53 | await api.query({ 54 | type: 'drop_select_permission', 55 | args: { 56 | table: HEALTHZ_VIEW_NAME, 57 | role: 'undefined', 58 | } 59 | }); 60 | await api.query({ 61 | type: 'untrack_table', 62 | args: { 63 | table: { 64 | schema: SCHEMA, 65 | name: HEALTHZ_VIEW_NAME, 66 | }, 67 | cascade: true, 68 | }, 69 | }); 70 | await api.sql(sql` 71 | DROP VIEW IF EXISTS ${HEALTHZ_VIEW_NAME} CASCADE; 72 | `); 73 | }; -------------------------------------------------------------------------------- /migrations/1690737739096-deepcase-opened.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import { installPackage } from './1678940577209-deepcase.js'; 5 | import { packageExists } from './1664940577200-tsx.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:deepcase-opened'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | const rootClient = generateApolloClient({ 12 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 13 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 14 | secret: process.env.MIGRATIONS_HASURA_SECRET, 15 | }); 16 | 17 | const root = new DeepClient({ 18 | apolloClient: rootClient, 19 | }); 20 | 21 | export const up = async () => { 22 | log('up'); 23 | // disable support deepcase and make migrations in gh-actions faster 24 | // const packageName = '@deep-foundation/deepcase-opened'; 25 | // if (!await packageExists(packageName)) { 26 | // const adminId = await root.id('deep', 'admin'); 27 | // const admin = await root.login({ linkId: adminId }); 28 | // const deep = new DeepClient({ deep: root, ...admin }); 29 | 30 | // const packageId = await installPackage(deep, '@deep-foundation/deepcase-opened'); 31 | // } 32 | }; 33 | 34 | export const down = async () => { 35 | log('down'); 36 | }; -------------------------------------------------------------------------------- /migrations/1690737739096-main-port.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import { installPackage } from './1678940577209-deepcase.js'; 5 | import { packageExists } from './1664940577200-tsx.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:deepcase-opened'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | const rootClient = generateApolloClient({ 12 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 13 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 14 | secret: process.env.MIGRATIONS_HASURA_SECRET, 15 | }); 16 | 17 | const root = new DeepClient({ 18 | apolloClient: rootClient, 19 | }); 20 | 21 | export const up = async () => { 22 | log('up'); 23 | const packageName = '@deep-foundation/main-port'; 24 | if (!await packageExists(packageName)) { 25 | const adminId = await root.id('deep', 'admin'); 26 | const admin = await root.login({ linkId: adminId }); 27 | const deep = new DeepClient({ deep: root, ...admin }); 28 | 29 | const packageId = await installPackage(deep, '@deep-foundation/main-port'); 30 | } 31 | }; 32 | 33 | export const down = async () => { 34 | log('down'); 35 | }; -------------------------------------------------------------------------------- /migrations/1693460330175-sync-handlers-update.ts: -------------------------------------------------------------------------------- 1 | import { createPrepareFunction, createDeepClientFunction, createSyncInsertTriggerFunction, createSyncDeleteTriggerFunction, createSyncUpdateTriggerFunction, createSyncDeleteStringsTriggerFunction, createSyncInsertStringsTriggerFunction, createSyncUpdateStringsTriggerFunction, createSyncDeleteNumbersTriggerFunction, createSyncInsertNumbersTriggerFunction, createSyncUpdateNumbersTriggerFunction, createSyncDeleteObjectsTriggerFunction, createSyncInsertObjectsTriggerFunction, createSyncUpdateObjectsTriggerFunction } from "../migrations/1655979260869-sync-handlers.js"; 2 | import Debug from 'debug'; 3 | import { api } from '../migrations/1616701513782-links.js'; 4 | import { sql } from '@deep-foundation/hasura/sql.js'; 5 | 6 | const debug = Debug('deeplinks:migrations:plv8'); 7 | const log = debug.extend('log'); 8 | 9 | 10 | export const up = async () => { 11 | log('up'); 12 | 13 | await api.sql(createPrepareFunction); 14 | await api.sql(createDeepClientFunction); 15 | await api.sql(createSyncInsertTriggerFunction); 16 | await api.sql(createSyncUpdateTriggerFunction); 17 | await api.sql(createSyncDeleteTriggerFunction); 18 | await api.sql(createSyncInsertStringsTriggerFunction); 19 | await api.sql(createSyncUpdateStringsTriggerFunction); 20 | await api.sql(createSyncDeleteStringsTriggerFunction); 21 | await api.sql(createSyncInsertNumbersTriggerFunction); 22 | await api.sql(createSyncUpdateNumbersTriggerFunction); 23 | await api.sql(createSyncDeleteNumbersTriggerFunction); 24 | await api.sql(createSyncInsertObjectsTriggerFunction); 25 | await api.sql(createSyncUpdateObjectsTriggerFunction); 26 | await api.sql(createSyncDeleteObjectsTriggerFunction); 27 | }; 28 | 29 | export const down = async () => { 30 | log('down'); 31 | }; 32 | -------------------------------------------------------------------------------- /migrations/1694386912180-sync-handlers-update-new.ts: -------------------------------------------------------------------------------- 1 | import { createPrepareFunction, createDeepClientFunction, createSyncInsertTriggerFunction, createSyncDeleteTriggerFunction, createSyncUpdateTriggerFunction, createSyncDeleteStringsTriggerFunction, createSyncInsertStringsTriggerFunction, createSyncUpdateStringsTriggerFunction, createSyncDeleteNumbersTriggerFunction, createSyncInsertNumbersTriggerFunction, createSyncUpdateNumbersTriggerFunction, createSyncDeleteObjectsTriggerFunction, createSyncInsertObjectsTriggerFunction, createSyncUpdateObjectsTriggerFunction } from "./1655979260869-sync-handlers.js"; 2 | import Debug from 'debug'; 3 | import { api } from './1616701513782-links.js'; 4 | import { sql } from '@deep-foundation/hasura/sql.js'; 5 | 6 | const debug = Debug('deeplinks:migrations:plv8'); 7 | const log = debug.extend('log'); 8 | 9 | 10 | export const up = async () => { 11 | log('up'); 12 | 13 | await api.sql(createPrepareFunction); 14 | await api.sql(createDeepClientFunction); 15 | await api.sql(createSyncInsertTriggerFunction); 16 | await api.sql(createSyncUpdateTriggerFunction); 17 | await api.sql(createSyncDeleteTriggerFunction); 18 | await api.sql(createSyncInsertStringsTriggerFunction); 19 | await api.sql(createSyncUpdateStringsTriggerFunction); 20 | await api.sql(createSyncDeleteStringsTriggerFunction); 21 | await api.sql(createSyncInsertNumbersTriggerFunction); 22 | await api.sql(createSyncUpdateNumbersTriggerFunction); 23 | await api.sql(createSyncDeleteNumbersTriggerFunction); 24 | await api.sql(createSyncInsertObjectsTriggerFunction); 25 | await api.sql(createSyncUpdateObjectsTriggerFunction); 26 | await api.sql(createSyncDeleteObjectsTriggerFunction); 27 | }; 28 | 29 | export const down = async () => { 30 | log('down'); 31 | }; 32 | -------------------------------------------------------------------------------- /migrations/1725896812219-unsafe.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import { installPackage } from './1678940577209-deepcase.js'; 5 | import { containWithin, packageExists, sharePermissions } from './1664940577200-tsx.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:unsafe'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | const rootClient = generateApolloClient({ 12 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 13 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 14 | secret: process.env.MIGRATIONS_HASURA_SECRET, 15 | }); 16 | 17 | const root = new DeepClient({ 18 | apolloClient: rootClient, 19 | }); 20 | 21 | export const up = async () => { 22 | log('up'); 23 | const packageName = '@deep-foundation/unsafe'; 24 | if (!await packageExists(packageName)) { 25 | const adminId = await root.id('deep', 'admin'); 26 | const admin = await root.login({ linkId: adminId }); 27 | const deep = new DeepClient({ deep: root, ...admin }); 28 | 29 | await installPackage(deep, '@deep-foundation/unsafe'); 30 | const packageId = await root.id('@deep-foundation/unsafe'); 31 | await sharePermissions(adminId, packageId); 32 | await containWithin(adminId, packageId); 33 | } 34 | }; 35 | 36 | export const down = async () => { 37 | log('down'); 38 | }; -------------------------------------------------------------------------------- /migrations/1725896812619-files.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import { installPackage } from './1678940577209-deepcase.js'; 5 | import { packageExists, sharePermissions } from './1664940577200-tsx.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:files'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | const rootClient = generateApolloClient({ 12 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 13 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 14 | secret: process.env.MIGRATIONS_HASURA_SECRET, 15 | }); 16 | 17 | const root = new DeepClient({ 18 | apolloClient: rootClient, 19 | }); 20 | 21 | export const up = async () => { 22 | log('up'); 23 | const packageName = '@deep-foundation/files'; 24 | if (!await packageExists(packageName)) { 25 | const adminId = await root.id('deep', 'admin'); 26 | const admin = await root.login({ linkId: adminId }); 27 | const deep = new DeepClient({ deep: root, ...admin }); 28 | 29 | await installPackage(deep, '@deep-foundation/files'); 30 | } 31 | }; 32 | 33 | export const down = async () => { 34 | log('down'); 35 | }; -------------------------------------------------------------------------------- /migrations/1725896842619-readme.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import { installPackage } from './1678940577209-deepcase.js'; 5 | import { containWithin, packageExists, sharePermissions } from './1664940577200-tsx.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:readme'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | const rootClient = generateApolloClient({ 12 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 13 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 14 | secret: process.env.MIGRATIONS_HASURA_SECRET, 15 | }); 16 | 17 | const root = new DeepClient({ 18 | apolloClient: rootClient, 19 | }); 20 | 21 | export const up = async () => { 22 | log('up'); 23 | const packageName = '@deep-foundation/readme'; 24 | if (!await packageExists(packageName)) { 25 | const adminId = await root.id('deep', 'admin'); 26 | const admin = await root.login({ linkId: adminId }); 27 | const deep = new DeepClient({ deep: root, ...admin }); 28 | 29 | await installPackage(deep, '@deep-foundation/readme'); 30 | const packageId = await root.id('@deep-foundation/readme'); 31 | await sharePermissions(adminId, packageId); 32 | await containWithin(adminId, packageId); 33 | } 34 | }; 35 | 36 | export const down = async () => { 37 | log('down'); 38 | }; -------------------------------------------------------------------------------- /migrations/1725896857619-perception.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import { installPackage } from './1678940577209-deepcase.js'; 5 | import { packageExists, sharePermissions } from './1664940577200-tsx.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:perception-links'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | const rootClient = generateApolloClient({ 12 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 13 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 14 | secret: process.env.MIGRATIONS_HASURA_SECRET, 15 | }); 16 | 17 | const root = new DeepClient({ 18 | apolloClient: rootClient, 19 | }); 20 | 21 | export const up = async () => { 22 | log('up'); 23 | const packageName = '@deep-foundation/perception-links'; 24 | if (!await packageExists(packageName)) { 25 | const adminId = await root.id('deep', 'admin'); 26 | const admin = await root.login({ linkId: adminId }); 27 | const deep = new DeepClient({ deep: root, ...admin }); 28 | 29 | await installPackage(deep, '@deep-foundation/perception-links'); 30 | } 31 | }; 32 | 33 | export const down = async () => { 34 | log('down'); 35 | }; -------------------------------------------------------------------------------- /migrations/1726136790617-tsx.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import tsxPckg from '@deep-foundation/tsx/deep.json' assert { type: 'json'}; 5 | import { Packager } from '../imports/packager.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:tsx'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | const rootClient = generateApolloClient({ 12 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 13 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 14 | secret: process.env.MIGRATIONS_HASURA_SECRET, 15 | }); 16 | 17 | var dc = '@deep-foundation/core'; 18 | 19 | const deep = new DeepClient({ 20 | apolloClient: rootClient, 21 | }); 22 | 23 | export const demoRules = ['usersCanSeeAll', 'usersCanInsertSafeLinks', 'usersCanUpdateSafeLinks', 'usersCanDeleteSafeLinks']; 24 | 25 | export const up = async () => { 26 | log('up'); 27 | const TSX = await deep.id('@deep-foundation/tsx', 'TSX'); 28 | 29 | const { data: rules } = await deep.select({ from_id: await deep.id('deep', 'admin'), type_id: deep.idLocal(dc, 'Contain'), string: { value: { _in: demoRules } } }, { apply: 'rules' }) 30 | 31 | const { data: selectors } = await deep.Traveler(rules).to().out({ type_id: deep.idLocal(dc, 'RuleObject') }).to().select(); 32 | 33 | await deep.insert(selectors.map(s => ({ 34 | from_id: s.id, 35 | type_id: deep.idLocal('@deep-foundation/core', 'SelectorInclude'), 36 | to_id: TSX, 37 | out: { 38 | type_id: deep.idLocal('@deep-foundation/core', 'SelectorTree'), 39 | to_id: deep.idLocal('@deep-foundation/core', 'typesTree'), 40 | }, 41 | }))); 42 | }; 43 | 44 | export const down = async () => { 45 | log('down'); 46 | }; -------------------------------------------------------------------------------- /migrations/1726165806644-authorization.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import { installPackage } from './1678940577209-deepcase.js'; 5 | import { containWithin, packageExists, sharePermissions } from './1664940577200-tsx.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:authorization'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | const rootClient = generateApolloClient({ 12 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 13 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 14 | secret: process.env.MIGRATIONS_HASURA_SECRET, 15 | }); 16 | 17 | const root = new DeepClient({ 18 | apolloClient: rootClient, 19 | }); 20 | 21 | export const up = async () => { 22 | log('up'); 23 | const packageName = '@deep-foundation/authorization'; 24 | if (!await packageExists(packageName)) { 25 | const adminId = await root.id('deep', 'admin'); 26 | const admin = await root.login({ linkId: adminId }); 27 | const deep = new DeepClient({ deep: root, ...admin }); 28 | 29 | await installPackage(deep, '@deep-foundation/authorization'); 30 | const packageId = await root.id('@deep-foundation/authorization'); 31 | await sharePermissions(adminId, packageId); 32 | await containWithin(adminId, packageId); 33 | } 34 | }; 35 | 36 | export const down = async () => { 37 | log('down'); 38 | }; -------------------------------------------------------------------------------- /migrations/1726165830140-passport-username-password.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import { installPackage } from './1678940577209-deepcase.js'; 5 | import { containWithin, packageExists, sharePermissions } from './1664940577200-tsx.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:passport-username-password'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | const rootClient = generateApolloClient({ 12 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 13 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 14 | secret: process.env.MIGRATIONS_HASURA_SECRET, 15 | }); 16 | 17 | const root = new DeepClient({ 18 | apolloClient: rootClient, 19 | }); 20 | 21 | export const up = async () => { 22 | log('up'); 23 | const packageName = '@deep-foundation/passport-username-password'; 24 | if (!await packageExists(packageName)) { 25 | const adminId = await root.id('deep', 'admin'); 26 | const admin = await root.login({ linkId: adminId }); 27 | const deep = new DeepClient({ deep: root, ...admin }); 28 | 29 | await installPackage(deep, '@deep-foundation/passport-username-password'); 30 | const packageId = await root.id('@deep-foundation/passport-username-password'); 31 | await sharePermissions(adminId, packageId); 32 | await containWithin(adminId, packageId); 33 | } 34 | }; 35 | 36 | export const down = async () => { 37 | log('down'); 38 | }; -------------------------------------------------------------------------------- /migrations/1726413155878-jsonschema.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import { installPackage } from './1678940577209-deepcase.js'; 5 | import { containWithin, packageExists, sharePermissions } from './1664940577200-tsx.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:jsonschema'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | const rootClient = generateApolloClient({ 12 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 13 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 14 | secret: process.env.MIGRATIONS_HASURA_SECRET, 15 | }); 16 | 17 | const root = new DeepClient({ 18 | apolloClient: rootClient, 19 | }); 20 | 21 | const dc = '@deep-foundation/core'; 22 | const du = '@deep-foundation/unsafe'; 23 | 24 | export const up = async () => { 25 | log('up'); 26 | const packageName = '@deep-foundation/jsonschema'; 27 | if (!await packageExists(packageName)) { 28 | const adminId = await root.id('deep', 'admin'); 29 | const admin = await root.login({ linkId: adminId }); 30 | const deep = new DeepClient({ deep: root, ...admin }); 31 | 32 | await installPackage(deep, '@deep-foundation/jsonschema'); 33 | const packageId = await root.id('@deep-foundation/jsonschema'); 34 | await sharePermissions(adminId, packageId); 35 | await containWithin(adminId, packageId); 36 | await deep.insert({ 37 | in: { 38 | type_id: deep.idLocal(dc, 'Contain'), 39 | from_id: adminId, 40 | }, 41 | type_id: deep.idLocal(dc, 'Rule'), 42 | out: { data: [ 43 | { 44 | type_id: deep.idLocal(dc, 'RuleObject'), 45 | to: { 46 | type_id: deep.idLocal(dc, 'Selector'), 47 | out: { 48 | type_id: deep.idLocal(dc, 'SelectorInclude'), 49 | to_id: await deep.id(packageName), 50 | out: { 51 | type_id: deep.idLocal(dc, 'SelectorTree'), 52 | to_id: deep.idLocal(dc, 'joinTree'), 53 | }, 54 | }, 55 | }, 56 | }, 57 | { 58 | type_id: deep.idLocal(dc, 'RuleSubject'), 59 | to: { 60 | type_id: deep.idLocal(dc, 'Selector'), 61 | out: { 62 | type_id: deep.idLocal(dc, 'SelectorInclude'), 63 | to_id: await deep.id(packageName), 64 | out: { 65 | type_id: deep.idLocal(dc, 'SelectorTree'), 66 | to_id: deep.idLocal(dc, 'joinTree'), 67 | }, 68 | }, 69 | }, 70 | }, 71 | { 72 | type_id: deep.idLocal(dc, 'RuleAction'), 73 | to: { 74 | type_id: deep.idLocal(dc, 'Selector'), 75 | out: { 76 | type_id: deep.idLocal(dc, 'SelectorInclude'), 77 | to_id: await deep.id(du, 'AllowUnsafe'), 78 | out: { 79 | type_id: deep.idLocal(dc, 'SelectorTree'), 80 | to_id: deep.idLocal(dc, 'typesTree'), 81 | }, 82 | }, 83 | }, 84 | }, 85 | ] }, 86 | }); 87 | } 88 | }; 89 | 90 | export const down = async () => { 91 | log('down'); 92 | }; -------------------------------------------------------------------------------- /migrations/1726413249768-jsonschema-form.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import { installPackage } from './1678940577209-deepcase.js'; 5 | import { containWithin, packageExists, sharePermissions } from './1664940577200-tsx.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:jsonschema-form'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | const rootClient = generateApolloClient({ 12 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 13 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 14 | secret: process.env.MIGRATIONS_HASURA_SECRET, 15 | }); 16 | 17 | const root = new DeepClient({ 18 | apolloClient: rootClient, 19 | }); 20 | 21 | export const up = async () => { 22 | log('up'); 23 | const packageName = '@deep-foundation/jsonschema-form'; 24 | if (!await packageExists(packageName)) { 25 | const adminId = await root.id('deep', 'admin'); 26 | const admin = await root.login({ linkId: adminId }); 27 | const deep = new DeepClient({ deep: root, ...admin }); 28 | 29 | await installPackage(deep, '@deep-foundation/jsonschema-form'); 30 | const packageId = await root.id('@deep-foundation/jsonschema-form'); 31 | await sharePermissions(adminId, packageId); 32 | await containWithin(adminId, packageId); 33 | } 34 | }; 35 | 36 | export const down = async () => { 37 | log('down'); 38 | }; -------------------------------------------------------------------------------- /migrations/1726588950095-semantic.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import { installPackage } from './1678940577209-deepcase.js'; 5 | import { containWithin, packageExists, sharePermissions } from './1664940577200-tsx.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:semantic'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | const rootClient = generateApolloClient({ 12 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 13 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 14 | secret: process.env.MIGRATIONS_HASURA_SECRET, 15 | }); 16 | 17 | const root = new DeepClient({ 18 | apolloClient: rootClient, 19 | }); 20 | 21 | export const up = async () => { 22 | log('up'); 23 | const packageName = '@deep-foundation/semantic'; 24 | if (!await packageExists(packageName)) { 25 | const adminId = await root.id('deep', 'admin'); 26 | const admin = await root.login({ linkId: adminId }); 27 | const deep = new DeepClient({ deep: root, ...admin }); 28 | 29 | await installPackage(deep, '@deep-foundation/semantic'); 30 | const packageId = await root.id('@deep-foundation/semantic'); 31 | await sharePermissions(adminId, packageId); 32 | await containWithin(adminId, packageId); 33 | } 34 | }; 35 | 36 | export const down = async () => { 37 | log('down'); 38 | }; -------------------------------------------------------------------------------- /migrations/1726588950120-bots.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import { installPackage } from './1678940577209-deepcase.js'; 5 | import { containWithin, packageExists, sharePermissions } from './1664940577200-tsx.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:bots'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | const rootClient = generateApolloClient({ 12 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 13 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 14 | secret: process.env.MIGRATIONS_HASURA_SECRET, 15 | }); 16 | 17 | const root = new DeepClient({ 18 | apolloClient: rootClient, 19 | }); 20 | 21 | export const up = async () => { 22 | log('up'); 23 | const packageName = '@deep-foundation/bots'; 24 | if (!await packageExists(packageName)) { 25 | const adminId = await root.id('deep', 'admin'); 26 | const admin = await root.login({ linkId: adminId }); 27 | const deep = new DeepClient({ deep: root, ...admin }); 28 | 29 | await installPackage(deep, '@deep-foundation/bots'); 30 | const packageId = await root.id('@deep-foundation/bots'); 31 | await sharePermissions(adminId, packageId); 32 | await containWithin(adminId, packageId); 33 | } 34 | }; 35 | 36 | export const down = async () => { 37 | log('down'); 38 | }; -------------------------------------------------------------------------------- /migrations/1727169148530-payments.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import { installPackage } from './1678940577209-deepcase.js'; 5 | import { containWithin, packageExists, sharePermissions } from './1664940577200-tsx.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:payments'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | const rootClient = generateApolloClient({ 12 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 13 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 14 | secret: process.env.MIGRATIONS_HASURA_SECRET, 15 | }); 16 | 17 | const root = new DeepClient({ 18 | apolloClient: rootClient, 19 | }); 20 | 21 | export const up = async () => { 22 | log('up'); 23 | const packageName = '@deep-foundation/payments'; 24 | if (!await packageExists(packageName)) { 25 | const adminId = await root.id('deep', 'admin'); 26 | const admin = await root.login({ linkId: adminId }); 27 | const deep = new DeepClient({ deep: root, ...admin }); 28 | 29 | await installPackage(deep, '@deep-foundation/payments'); 30 | const packageId = await root.id('@deep-foundation/payments'); 31 | await sharePermissions(adminId, packageId); 32 | await containWithin(adminId, packageId); 33 | } 34 | }; 35 | 36 | export const down = async () => { 37 | log('down'); 38 | }; -------------------------------------------------------------------------------- /migrations/1727169149950-payments-tinkoff-c2b.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import { installPackage } from './1678940577209-deepcase.js'; 5 | import { containWithin, packageExists, sharePermissions } from './1664940577200-tsx.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:payments-tinkoff-c2b'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | const rootClient = generateApolloClient({ 12 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 13 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 14 | secret: process.env.MIGRATIONS_HASURA_SECRET, 15 | }); 16 | 17 | const root = new DeepClient({ 18 | apolloClient: rootClient, 19 | }); 20 | 21 | export const up = async () => { 22 | log('up'); 23 | const packageName = '@deep-foundation/payments-tinkoff-c2b'; 24 | if (!await packageExists(packageName)) { 25 | const adminId = await root.id('deep', 'admin'); 26 | const admin = await root.login({ linkId: adminId }); 27 | const deep = new DeepClient({ deep: root, ...admin }); 28 | 29 | await installPackage(deep, '@deep-foundation/payments-tinkoff-c2b'); 30 | const packageId = await root.id('@deep-foundation/payments-tinkoff-c2b'); 31 | await sharePermissions(adminId, packageId); 32 | await containWithin(adminId, packageId); 33 | } 34 | }; 35 | 36 | export const down = async () => { 37 | log('down'); 38 | }; -------------------------------------------------------------------------------- /migrations/1727224344983-promise-cleaner.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import { installPackage } from './1678940577209-deepcase.js'; 5 | import { containWithin, packageExists, sharePermissions } from './1664940577200-tsx.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:promise-cleaner'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | const rootClient = generateApolloClient({ 12 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 13 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 14 | secret: process.env.MIGRATIONS_HASURA_SECRET, 15 | }); 16 | 17 | const root = new DeepClient({ 18 | apolloClient: rootClient, 19 | }); 20 | 21 | export const up = async () => { 22 | log('up'); 23 | const packageName = '@deep-foundation/promise-cleaner'; 24 | if (!await packageExists(packageName)) { 25 | const adminId = await root.id('deep', 'admin'); 26 | const admin = await root.login({ linkId: adminId }); 27 | const deep = new DeepClient({ deep: root, ...admin }); 28 | 29 | await installPackage(deep, '@deep-foundation/promise-cleaner'); 30 | const packageId = await root.id('@deep-foundation/promise-cleaner'); 31 | await sharePermissions(adminId, packageId); 32 | await containWithin(adminId, packageId); 33 | } 34 | }; 35 | 36 | export const down = async () => { 37 | log('down'); 38 | }; -------------------------------------------------------------------------------- /migrations/1727234538431-rules.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import { installPackage } from './1678940577209-deepcase.js'; 5 | import { containWithin, packageExists, sharePermissions } from './1664940577200-tsx.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:rules'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | const rootClient = generateApolloClient({ 12 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 13 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 14 | secret: process.env.MIGRATIONS_HASURA_SECRET, 15 | }); 16 | 17 | const root = new DeepClient({ 18 | apolloClient: rootClient, 19 | }); 20 | 21 | export const up = async () => { 22 | log('up'); 23 | const packageName = '@deep-foundation/rules'; 24 | if (!await packageExists(packageName)) { 25 | const adminId = await root.id('deep', 'admin'); 26 | const admin = await root.login({ linkId: adminId }); 27 | const deep = new DeepClient({ deep: root, ...admin }); 28 | 29 | await installPackage(deep, '@deep-foundation/rules'); 30 | const packageId = await root.id('@deep-foundation/rules'); 31 | await sharePermissions(adminId, packageId); 32 | await containWithin(adminId, packageId); 33 | } 34 | }; 35 | 36 | export const down = async () => { 37 | log('down'); 38 | }; -------------------------------------------------------------------------------- /migrations/1727618396605-preload.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import { installPackage } from './1678940577209-deepcase.js'; 5 | import { containWithin, packageExists, sharePermissions } from './1664940577200-tsx.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:preload'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | const rootClient = generateApolloClient({ 12 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 13 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 14 | secret: process.env.MIGRATIONS_HASURA_SECRET, 15 | }); 16 | 17 | const root = new DeepClient({ 18 | apolloClient: rootClient, 19 | }); 20 | 21 | export const up = async () => { 22 | log('up'); 23 | const packageName = '@deep-foundation/preload'; 24 | if (!await packageExists(packageName)) { 25 | const adminId = await root.id('deep', 'admin'); 26 | const admin = await root.login({ linkId: adminId }); 27 | const deep = new DeepClient({ deep: root, ...admin }); 28 | 29 | await installPackage(deep, '@deep-foundation/preload'); 30 | const packageId = await root.id('@deep-foundation/preload'); 31 | await sharePermissions(adminId, packageId); 32 | await containWithin(adminId, packageId); 33 | 34 | const Pckg = await deep.id('@deep-foundation/core', 'Package'); 35 | const Preload = await deep.id('@deep-foundation/preload', 'Preload'); 36 | const { data: packages } = await deep.select({ type_id: Pckg, string: { value: { _neq: 'deep' } } }); 37 | await deep.insert(packages.map(p => ({ type_id: Preload, from_id: p.id, to_id: p.id }))); 38 | } 39 | }; 40 | 41 | export const down = async () => { 42 | log('down'); 43 | }; -------------------------------------------------------------------------------- /migrations/1727714840428-timezone.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import { installPackage } from './1678940577209-deepcase.js'; 5 | import { containWithin, packageExists, sharePermissions } from './1664940577200-tsx.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:timezone'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | const rootClient = generateApolloClient({ 12 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 13 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 14 | secret: process.env.MIGRATIONS_HASURA_SECRET, 15 | }); 16 | 17 | const root = new DeepClient({ 18 | apolloClient: rootClient, 19 | }); 20 | 21 | export const up = async () => { 22 | log('up'); 23 | const packageName = '@deep-foundation/timezone'; 24 | if (!await packageExists(packageName)) { 25 | const adminId = await root.id('deep', 'admin'); 26 | const admin = await root.login({ linkId: adminId }); 27 | const deep = new DeepClient({ deep: root, ...admin }); 28 | 29 | await installPackage(deep, '@deep-foundation/timezone'); 30 | const packageId = await root.id('@deep-foundation/timezone'); 31 | await sharePermissions(adminId, packageId); 32 | await containWithin(adminId, packageId); 33 | } 34 | }; 35 | 36 | export const down = async () => { 37 | log('down'); 38 | }; -------------------------------------------------------------------------------- /migrations/1727714840450-coordinates.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import Debug from 'debug'; 3 | import { DeepClient } from '../imports/client.js'; 4 | import { installPackage } from './1678940577209-deepcase.js'; 5 | import { containWithin, packageExists, sharePermissions } from './1664940577200-tsx.js'; 6 | 7 | const debug = Debug('deeplinks:migrations:coordinates'); 8 | const log = debug.extend('log'); 9 | const error = debug.extend('error'); 10 | 11 | const rootClient = generateApolloClient({ 12 | path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, 13 | ssl: !!+(process.env.MIGRATIONS_HASURA_SSL || 0), 14 | secret: process.env.MIGRATIONS_HASURA_SECRET, 15 | }); 16 | 17 | const root = new DeepClient({ 18 | apolloClient: rootClient, 19 | }); 20 | 21 | export const up = async () => { 22 | log('up'); 23 | const packageName = '@deep-foundation/coordinates'; 24 | if (!await packageExists(packageName)) { 25 | const adminId = await root.id('deep', 'admin'); 26 | const admin = await root.login({ linkId: adminId }); 27 | const deep = new DeepClient({ deep: root, ...admin }); 28 | 29 | await installPackage(deep, '@deep-foundation/coordinates'); 30 | const packageId = await root.id('@deep-foundation/coordinates'); 31 | await sharePermissions(adminId, packageId); 32 | await containWithin(adminId, packageId); 33 | } 34 | }; 35 | 36 | export const down = async () => { 37 | log('down'); 38 | }; -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["imports", "index.ts"], 3 | "ext": "ts", 4 | "exec": "ts-node ./index.ts" 5 | } -------------------------------------------------------------------------------- /open-deep.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Deep 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@deep-foundation/deeplinks", 3 | "version": "0.0.620", 4 | "license": "Unlicense", 5 | "type": "module", 6 | "main": "import.js", 7 | "scripts": { 8 | "dev": "(cd local && docker compose down || true) && cross-env PORT=3006 npm run launch", 9 | "dev-local": "(cd local && docker compose down || true) && cross-env npm_config_yes=true JWT_SECRET=\"{\\\"type\\\":\\\"HS256\\\",\\\"key\\\":\\\"3EK6FD+o0+c7tzBNVfjpMkNDi2yARAAKzQlk8O2IKoxQu4nF7EdAh8s3TwpHwrdWT6R\\\"}\" MIGRATIONS_HASURA_PATH=localhost:8080 MIGRATIONS_HASURA_SSL=0 MIGRATIONS_HASURA_SECRET=myadminsecretkey MIGRATIONS_DEEPLINKS_URL=http://host.docker.internal:3006 DEEPLINKS_HASURA_PATH=localhost:8080 DEEPLINKS_HASURA_SSL=0 DEEPLINKS_HASURA_SECRET=myadminsecretkey PORT=3006 npm run launch", 10 | "launch": "npm run package:build && node ./index.js;", 11 | "bundle-mathjs": "export LIBNAME=mathjs; node ./bundles/deeplinks-bundler-prepare.js && webpack && node ./bundles/deeplinks-bundler-postbundle.js", 12 | "bundle-mathjs-test": "node ./bundles/mathjs.js", 13 | "bundle-jsonschema": "export LIBNAME=jsonschema; node ./bundles/deeplinks-bundler-prepare.js && webpack && node ./bundles/deeplinks-bundler-postbundle.js", 14 | "bundle-jsonschema-test": "node ./bundles/jsonschema.js", 15 | "bundle-all": "npm run bundle-mathjs && npm run bundle-jsonschema && npm run bundle-jsonschema-test && npm run bundle-mathjs-test", 16 | "package:refresh": "rm -rf node_modules; rm -f package-lock.json; npm i --workspaces false", 17 | "package:build": "tsc --project tsconfig.json", 18 | "build": "npm run package:build", 19 | "package:watch": "tsc -w --project tsconfig.json", 20 | "package:unbuild": "rimraf ./*.js && rimraf ./*.js.map && rimraf ./*.d.ts && (cd ./migrations && rimraf ./*.js && rimraf ./*.js.map && rimraf ./*.d.ts && cd ../) && (cd ./imports && rimraf \"./!(engine-server).js\" -g && rimraf ./*.js.map && rimraf ./*.d.ts && cd ../) && (cd ./imports/gql && rimraf ./*.js && rimraf ./*.js.map && rimraf ./*.d.ts && cd ../..) && (cd ./benchmarks && rm -f ./*.js; rm -f ./*.js.map; rm -f ./*.d.ts && cd ..) && (cd ./imports/router && rimraf ./*.js && rimraf ./*.js.map && rimraf ./*.d.ts && cd ../..) && (cd ./tests && rimraf ./*.js && rimraf ./*.js.map && rimraf ./*.d.ts && cd ../)", 21 | "unbuild": "npm run package:unbuild", 22 | "package:publish": "npm run package:build && npm publish --access public && npm run package:unbuild", 23 | "package:release": "npm version patch && git push", 24 | "test": "npm run package:build && cross-env DEEPLINKS_HASURA_PATH=localhost:8080 DEEPLINKS_HASURA_SSL=0 DEEPLINKS_HASURA_SECRET=myadminsecretkey DOCKER_DEEPLINKS_URL=http://localhost:3006 NODE_OPTIONS=--experimental-vm-modules jest --config jest.config.js *.js --testTimeout=50000", 25 | "benchmark": "export DEEPLINKS_HASURA_PATH=localhost:8080; export DEEPLINKS_HASURA_SSL=0; export DEEPLINKS_HASURA_SECRET=myadminsecretkey; ts-node benchmarks/index.ts", 26 | "migrate": "npm run package:build && npx migrate@latest up --matches '*.js'", 27 | "unmigrate": "npm run package:build && npx migrate@latest down --matches '*.js'", 28 | "migrate-s-h": "npm run package:build && npx migrate@latest up 1693460330175-sync-handlers-update --matches '*.js'", 29 | "unmigrate-s-h": "npm run package:build && npx migrate@latest down 1693460330175-sync-handlers-update --matches '*.js'", 30 | "remigrate-s-h": "npm run unmigrate-s-h && npm run migrate", 31 | "start-engine-docker": "npm run start-engine-docker-core", 32 | "start-deeplinks-docker": "docker compose pull && docker compose -p deep up -d", 33 | "stop-deeplinks-docker": "docker compose -p deep down", 34 | "snapshot:create": "node snapshots/create.js", 35 | "snapshot:last": "node snapshots/last.js", 36 | "snapshot:dump": "node snapshots/dump.js", 37 | "warmup": "node warmup.js", 38 | "start": "node call.mjs", 39 | "nexe": "cat call.mjs | nexe -t 18.20.4 --build" 40 | }, 41 | "bin": { 42 | "deeplinks": "./call.mjs" 43 | }, 44 | "dependencies": { 45 | "@cybercongress/cyber-js": "^0.3.6", 46 | "@deep-foundation/hasura": "^0.0.68", 47 | "@deep-foundation/npm-packager": "^0.0.43", 48 | "@deep-foundation/tsx": "^0.0.5", 49 | "@helia/strings": "^3.0.3", 50 | "@react-hook/debounce": "^4.0.0", 51 | "@types/node": "^20.2.4", 52 | "@types/react": "^18.2.15", 53 | "async": "^3.2.5", 54 | "atob": "^2.1.2", 55 | "axios": "^1.4.0", 56 | "buffer": "^6.0.3", 57 | "chai": "^4.3.7", 58 | "command-line-args": "^6.0.0", 59 | "cookie-parser": "^1.4.6", 60 | "cross-env": "^7.0.3", 61 | "debug": "^4.3.4", 62 | "dotenv": "^16.0.3", 63 | "fix-path": "^4.0.0", 64 | "get-port": "^5.1.1", 65 | "get-value": "^3.0.1", 66 | "gists": "^2.0.0", 67 | "helia": "^2.1.0", 68 | "internal-ip": "^6.2.0", 69 | "json5": "^2.2.3", 70 | "jsonwebtoken": "^9.0.0", 71 | "match-sorter": "^6.3.4", 72 | "moesif-nodejs": "^3.3.3", 73 | "nexe": "^4.0.0-rc.6", 74 | "node-fetch": "^2.6.11", 75 | "react-dom": "^18.2.0", 76 | "react-dropzone": "^14.2.3", 77 | "root-path-electron": "^1.3.3", 78 | "serialize-error": "^11.0.3", 79 | "sudo-prompt": "^9.2.1", 80 | "wait-on": "^7.0.1" 81 | }, 82 | "peerDependencies": { 83 | "@apollo/client": "^3.7.14", 84 | "@deep-foundation/materialized-path": "0.0.x", 85 | "@deep-foundation/react-hasura": "0.0.x", 86 | "@deep-foundation/store": "^0.0.32", 87 | "apollo-server": "^3.13.0", 88 | "apollo-server-core": "^3.13.0", 89 | "apollo-server-express": "^3.13.0", 90 | "graphql": "^15.8.0", 91 | "graphql-playground-middleware-express": "^1.7.23", 92 | "graphql-tag": "^2.12.6", 93 | "http-proxy-middleware": "^2.0.6", 94 | "react": "^18.0.0" 95 | }, 96 | "engines": { 97 | "node": "^18" 98 | }, 99 | "devDependencies": { 100 | "@testing-library/jest-dom": "^5.16.5", 101 | "@testing-library/react": "^14.0.0", 102 | "@types/debug": "^4.1.8", 103 | "@types/fs-extra": "^11.0.1", 104 | "@types/get-value": "^3.0.3", 105 | "clean-webpack-plugin": "^4.0.0", 106 | "fs-extra": "^11.1.1", 107 | "jest": "^29.5.0", 108 | "jest-environment-jsdom": "^29.5.0", 109 | "migrate": "^2.0.0", 110 | "react-dom": "^18.2.0", 111 | "ts-jest": "^29.1.0", 112 | "ts-node": "^10.9.1", 113 | "typescript": "5.0.4", 114 | "webpack": "^5.84.1", 115 | "webpack-cli": "^5.1.1" 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /postgresql/dockerfile: -------------------------------------------------------------------------------- 1 | FROM postgres:12 2 | 3 | ENV PLV8_VERSION=3.1.5 4 | 5 | RUN buildDependencies="build-essential \ 6 | ninja-build \ 7 | ca-certificates \ 8 | curl \ 9 | git-core \ 10 | python3 \ 11 | gpp \ 12 | cpp \ 13 | pkg-config \ 14 | apt-transport-https \ 15 | cmake \ 16 | libc++-dev \ 17 | libc++abi-dev \ 18 | postgresql-server-dev-$PG_MAJOR" \ 19 | && runtimeDependencies="libc++1 \ 20 | libtinfo5 \ 21 | libc++abi1" \ 22 | && apt-get update \ 23 | && apt-get install -y --no-install-recommends ${buildDependencies} ${runtimeDependencies} \ 24 | && mkdir -p /tmp/build \ 25 | && curl -o /tmp/build/v$PLV8_VERSION.tar.gz -SL "https://github.com/plv8/plv8/archive/v${PLV8_VERSION}.tar.gz" \ 26 | && cd /tmp/build \ 27 | && tar -xzf /tmp/build/v$PLV8_VERSION.tar.gz -C /tmp/build/ \ 28 | && cd /tmp/build/plv8-$PLV8_VERSION \ 29 | && make static \ 30 | && make install \ 31 | && strip /usr/lib/postgresql/${PG_MAJOR}/lib/plv8-${PLV8_VERSION}.so \ 32 | && rm -rf /root/.vpython_cipd_cache /root/.vpython-root \ 33 | && apt-get clean \ 34 | && apt-get remove -y ${buildDependencies} \ 35 | && apt-get autoremove -y \ 36 | && rm -rf /tmp/build /var/lib/apt/lists/* -------------------------------------------------------------------------------- /react.test.tsx: -------------------------------------------------------------------------------- 1 | import { createRequire } from 'module'; 2 | import { jest } from '@jest/globals'; 3 | const require = createRequire(import.meta.url); 4 | 5 | require('dotenv').config(); 6 | 7 | jest.setTimeout(120000); 8 | 9 | import './tests/client.react' -------------------------------------------------------------------------------- /snapshots/create.js: -------------------------------------------------------------------------------- 1 | import { createRequire } from 'module'; 2 | import { fileURLToPath } from 'url'; 3 | const require = createRequire(import.meta.url); 4 | const { promisify } = require('util'); 5 | const { exec } = require('child_process'); 6 | const { promises: fs } = require('fs'); 7 | const execP = promisify(exec); 8 | const path = require('path'); 9 | 10 | const __filename = fileURLToPath(import.meta.url); 11 | const __dirname = path.dirname(__filename); 12 | 13 | const _deeplinks = path.normalize(`${__dirname}/..`); 14 | 15 | const create = async () => { 16 | const files = await fs.readdir('./snapshots'); 17 | const snapshots = files.filter(file => /^-?[0-9]+$/.test(file)); 18 | const last = snapshots[snapshots.length - 1]; 19 | const time = (new Date()).getTime(); 20 | const { stdout, stderr } = await execP(`(docker compose -f docker-compose.yml -p deep stop || true) && docker run -v ${_deeplinks}:/deeplinks --volumes-from deep-postgres --rm --name links --entrypoint \"sh\" deepf/deeplinks:main -c \"cd / && ls var/lib/postgresql && tar -c -v -C /var/lib/postgresql -f /deeplinks/snapshots/${time} ./data" && docker compose -f docker-compose.yml -p deep start && cp .migrate snapshots/${time}.migrate`); 21 | console.log('stdout',stdout); 22 | console.log('stderr',stderr); 23 | } 24 | 25 | create(); 26 | -------------------------------------------------------------------------------- /snapshots/last.js: -------------------------------------------------------------------------------- 1 | import { createRequire } from 'module'; 2 | import { fileURLToPath } from 'url'; 3 | const require = createRequire(import.meta.url); 4 | const { promisify } = require('util'); 5 | const { exec } = require('child_process'); 6 | const { promises: fs } = require('fs'); 7 | const execP = promisify(exec); 8 | const path = require('path'); 9 | 10 | const __filename = fileURLToPath(import.meta.url); 11 | const __dirname = path.dirname(__filename); 12 | 13 | const _deeplinks = path.normalize(`${__dirname}/..`); 14 | 15 | const last = async () => { 16 | const files = await fs.readdir('./snapshots'); 17 | const snapshots = files.filter(file => /^-?[0-9]+$/.test(file)); 18 | const last = snapshots[snapshots.length - 1]; 19 | console.log(`docker run -v ${_deeplinks}:/deeplinks -v deep-db-data:/data --rm --name links --entrypoint "sh" deepf/deeplinks:main -c "cd / && tar xf snapshots/${last} --strip 1 && cp snapshots/${last}.migrate .migrate"`); 20 | const { stdout, stderr } = await execP(`docker run -v ${_deeplinks}:/deeplinks -v deep-db-data:/data --rm --name links --entrypoint "sh" deepf/deeplinks:main -c "cd / && tar xf snapshots/${last} --strip 1 && cp snapshots/${last}.migrate .migrate"`); 21 | console.log('stdout',stdout); 22 | console.log('stderr',stderr); 23 | } 24 | 25 | last(); -------------------------------------------------------------------------------- /tests/cyber_client.tsx: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import { DeepClient, SerialOperation, useDeepSubscription } from "../imports/client"; 3 | import { assert } from 'chai'; 4 | import { BoolExpLink, MutationInputLink } from "../imports/client_types"; 5 | import { inspect } from 'util' 6 | import { createSerialOperation } from "../imports/gql"; 7 | import { render, screen, waitFor } from '@testing-library/react' 8 | import { DeepProvider } from '../imports/client'; 9 | import React, { useEffect } from "react"; 10 | import { ApolloClient, ApolloProvider } from '@apollo/client/index.js'; 11 | import '@testing-library/jest-dom'; 12 | import { useDeep } from '../imports/client'; 13 | import { IApolloClientGeneratorOptions } from '@deep-foundation/hasura/client'; 14 | import { ApolloClientTokenizedProvider } from '@deep-foundation/react-hasura/apollo-client-tokenized-provider' 15 | import { TokenProvider } from '../imports/react-token'; 16 | import { LocalStoreProvider } from '@deep-foundation/store/local'; 17 | import { QueryStoreProvider } from '@deep-foundation/store/query'; 18 | import { CookiesStoreProvider } from '@deep-foundation/store/cookies'; 19 | import { CapacitorStoreProvider } from "@deep-foundation/store/capacitor"; 20 | import { generateCyberDeepClient } from '../imports/cyber.js' 21 | import type { CyberDeepClient } from '../imports/cyber.js' 22 | 23 | import * as cyberConfig from '../imports/cyber/config'; 24 | // function Main({ options }: { options: IApolloClientGeneratorOptions }): JSX.Element { 25 | // return 26 | //
27 | //
28 | // } 29 | /* 30 | const graphQlPath = `${process.env.DEEPLINKS_HASURA_PATH}/v1/graphql`; 31 | const ssl = !!+process.env.DEEPLINKS_HASURA_SSL; 32 | const secret = process.env.DEEPLINKS_HASURA_SECRET; 33 | const ws = true; 34 | */ 35 | let deepClient: CyberDeepClient; 36 | 37 | beforeAll(async () => { 38 | deepClient = await generateCyberDeepClient({ 39 | config: cyberConfig.CYBER, 40 | }); 41 | }) 42 | 43 | describe('CyberDeepClient', () => { 44 | describe(`generators`, async () => { 45 | it(`particles`, async () => {}); 46 | it(`cyberlinks`, async () => {}); 47 | it(`ps by cbls`, async () => {}); 48 | it(`ps by cbls with ps`, async () => {}); 49 | it(`cbls by ps`, async () => {}); 50 | it(`cbls by ps with cbls`, async () => {}); 51 | it(`cbls by ps with cbls with ps`, async () => {}); 52 | }); 53 | }); -------------------------------------------------------------------------------- /tests/demo.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import { DeepClient } from "../imports/client"; 3 | import { assert, expect } from 'chai'; 4 | import { stringify } from "querystring"; 5 | import { delay } from "../imports/promise"; 6 | 7 | const apolloClient = generateApolloClient({ 8 | path: `${process.env.DEEPLINKS_HASURA_PATH}/v1/graphql`, 9 | ssl: !!+process.env.DEEPLINKS_HASURA_SSL, 10 | secret: process.env.DEEPLINKS_HASURA_SECRET, 11 | }); 12 | 13 | const deep = new DeepClient({ apolloClient }); 14 | 15 | describe('demo', () => { 16 | it(`user can basic dc operations`, async () => { 17 | const g1 = await deep.guest(); 18 | const d1 = new DeepClient({ deep, ...g1 }); 19 | const i1s1 = await d1.insert({ type_id: await d1.id('@deep-foundation/core', 'Space') }); 20 | const i1c1 = await d1.insert({ type_id: await d1.id('@deep-foundation/core', 'Contain'), from_id: g1.linkId, to_id: i1s1?.data?.[0]?.id }); 21 | expect(i1s1?.data?.[0]?.id).is.not.undefined; 22 | const i1q1 = await d1.insert({ type_id: await d1.id('@deep-foundation/core', 'Query'), object: { data: { value: { limit: 0 } } } }); 23 | expect(i1q1?.data?.[0]?.id).is.not.undefined; 24 | const i1c2 = await d1.insert({ type_id: await d1.id('@deep-foundation/core', 'Contain'), from_id: i1s1?.data?.[0]?.id, to_id: i1q1?.data?.[0]?.id }); 25 | expect(i1c1?.data?.[0]?.id).is.not.undefined; 26 | const u1q1 = await d1.update({ link_id: { _eq: i1q1?.data?.[0]?.id } }, { value: { limit: 1 } }, { table: 'objects' }); 27 | const s1q1 = await d1.select(i1q1?.data?.[0]?.id); 28 | expect(s1q1?.data?.[0]?.value?.value?.limit).is.equal(1); 29 | const i1f1 = await d1.insert({ type_id: await d1.id('@deep-foundation/core', 'Focus'), from_id: i1s1?.data?.[0]?.id, to_id: i1q1?.data?.[0]?.id }); 30 | const i1c3 = await d1.insert({ type_id: await d1.id('@deep-foundation/core', 'Contain'), from_id: i1s1?.data?.[0]?.id, to_id: i1f1?.data?.[0]?.id }); 31 | const s1f1 = await d1.select(i1f1?.data?.[0]?.id); 32 | expect(s1f1?.data?.[0]?.id).is.not.undefined; 33 | }); 34 | }); -------------------------------------------------------------------------------- /tests/experimental/client.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import { ApolloClient} from '@apollo/client/index.js'; 3 | import '@testing-library/jest-dom'; 4 | import { SerialTransitionsBuilder, Transition } from '../../imports/experimental/serial-transitions-builder'; 5 | import { DeepClient, Table } from '../../imports/client'; 6 | import assert from 'assert'; 7 | 8 | 9 | const graphQlPath = `${process.env.DEEPLINKS_HASURA_PATH}/v1/graphql`; 10 | const ssl = !!+process.env.DEEPLINKS_HASURA_SSL; 11 | const secret = process.env.DEEPLINKS_HASURA_SECRET; 12 | const ws = true; 13 | 14 | let apolloClient: ApolloClient; 15 | let deepClient: DeepClient; 16 | 17 | beforeAll(async () => { 18 | apolloClient = generateApolloClient({ 19 | path: graphQlPath, 20 | ssl, 21 | secret, 22 | ws 23 | }); 24 | deepClient = new DeepClient({ apolloClient }); 25 | 26 | const {data: corePackageLinks} = await deepClient.select({ 27 | up: { 28 | tree_id: { 29 | _id: ["@deep-foundation/core" ,"containTree"] 30 | }, 31 | parent_id: { 32 | _id: ["@deep-foundation/core"] 33 | } 34 | } 35 | }) 36 | deepClient.minilinks.apply(corePackageLinks) 37 | }) 38 | 39 | describe('SerialTransitionsBuilder', () => { 40 | it('should instantiate correctly', () => { 41 | const builder = new SerialTransitionsBuilder({ deep: deepClient }); 42 | expect(builder).toBeInstanceOf(SerialTransitionsBuilder); 43 | }); 44 | 45 | it('should append insert transition', async () => { 46 | const builder = new SerialTransitionsBuilder({ deep: deepClient }); 47 | const table = 'links'; 48 | const transition = [null, { 49 | type_id: deepClient.idLocal("@deep-foundation/core", "Type") 50 | }]; 51 | builder.append({ table, transition }); 52 | expect(builder.actions().length).toBe(1); 53 | }); 54 | 55 | it('should clear transitions', () => { 56 | const builder = new SerialTransitionsBuilder({ deep: deepClient }); 57 | const table = 'links'; 58 | const transition = [null, { 59 | type_id: deepClient.idLocal("@deep-foundation/core", "Type") 60 | }]; 61 | builder.append({ table, transition }); 62 | builder.clear(); 63 | expect(builder.actions().length).toBe(0); 64 | }); 65 | 66 | it('should get correct transition type for insert', () => { 67 | const builder = new SerialTransitionsBuilder({ deep: deepClient }); 68 | const transition: Transition = [null, { 69 | type_id: deepClient.idLocal("@deep-foundation/core", "Type") 70 | }]; 71 | const type = builder.getTransitionType(transition); 72 | if(type !== 'insert') { 73 | throw new Error('Incorrect transition type'); 74 | } 75 | }); 76 | 77 | it('should get correct transition type for update', () => { 78 | const builder = new SerialTransitionsBuilder({ deep: deepClient }); 79 | const transition: Transition = [{ 80 | id: 0 81 | }, { 82 | id: 0 83 | }]; 84 | const type = builder.getTransitionType(transition); 85 | if(type !== 'update') { 86 | throw new Error('Incorrect transition type'); 87 | } 88 | }); 89 | 90 | it('should get correct transition type for delete', () => { 91 | const builder = new SerialTransitionsBuilder({ deep: deepClient }); 92 | const transition: Transition = [{ 93 | type_id: deepClient.idLocal("@deep-foundation/core", "Type") 94 | }, null]; 95 | const type = builder.getTransitionType(transition); 96 | if(type !== 'delete') { 97 | throw new Error('Incorrect transition type'); 98 | } 99 | }); 100 | 101 | it('should execute transition', async () => { 102 | const builder = new SerialTransitionsBuilder({ deep: deepClient }); 103 | const transition = [null, { 104 | type_id: deepClient.idLocal("@deep-foundation/core", "Type") 105 | }]; 106 | const result = await builder 107 | .append({ transition }) 108 | .execute(); 109 | 110 | assert.equal(Object.keys(result.data).length, 2) 111 | }); 112 | 113 | it('should execute transitions', async () => { 114 | const builder = new SerialTransitionsBuilder({ deep: deepClient }); 115 | const transition = [null, { 116 | type_id: deepClient.idLocal("@deep-foundation/core", "Type") 117 | }]; 118 | const result = await builder 119 | .append({ transition }) 120 | .append({ transition }) 121 | .execute(); 122 | assert.equal(Object.keys(result.data).length, 4) 123 | }); 124 | 125 | it('should execute multiple transitions', async () => { 126 | const builder = new SerialTransitionsBuilder({ deep: deepClient }); 127 | const transition = [null, { 128 | type_id: deepClient.idLocal("@deep-foundation/core", "Type") 129 | }]; 130 | const result = await builder 131 | .appendMultiple([ 132 | { transition }, 133 | { transition } 134 | ]) 135 | .execute(); 136 | assert.equal(Object.keys(result.data).length, 4) 137 | }); 138 | 139 | it('should execute transition and result link be accessible by both alias and index', async () => { 140 | const builder = new SerialTransitionsBuilder({ deep: deepClient }); 141 | const transition = [null, { 142 | type_id: deepClient.idLocal("@deep-foundation/core", "Type") 143 | }]; 144 | const alias = 'link'; 145 | const {data} = await builder 146 | .append({ transition, alias }) 147 | .execute(); 148 | expect(data[alias]).toBeDefined(); 149 | expect(data[0]).toBeDefined(); 150 | }); 151 | 152 | it('should execute transitions and result links be accessible by both alias and index', async () => { 153 | const builder = new SerialTransitionsBuilder({ deep: deepClient }); 154 | const transition = [null, { 155 | type_id: deepClient.idLocal("@deep-foundation/core", "Type") 156 | }]; 157 | const firstLinkAlias = 'link0'; 158 | const secondLinkAlias = 'link1'; 159 | const {data} = await builder 160 | .append({ transition, alias: firstLinkAlias }) 161 | .append({ transition, alias: secondLinkAlias }) 162 | .execute(); 163 | expect(data[firstLinkAlias]).toBeDefined(); 164 | expect(data[0]).toBeDefined(); 165 | expect(data[secondLinkAlias]).toBeDefined(); 166 | expect(data[1]).toBeDefined(); 167 | }); 168 | }); -------------------------------------------------------------------------------- /tests/join-insert.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import { DeepClient } from "../imports/client"; 3 | import { assert } from 'chai'; 4 | 5 | const apolloClient = generateApolloClient({ 6 | path: `${process.env.DEEPLINKS_HASURA_PATH}/v1/graphql`, 7 | ssl: !!+process.env.DEEPLINKS_HASURA_SSL, 8 | secret: process.env.DEEPLINKS_HASURA_SECRET, 9 | }); 10 | 11 | const deep = new DeepClient({ apolloClient }); 12 | 13 | describe('join-insert', () => { 14 | it(`{ type_id: 1, string: { data: { value: 'abc' } }, from: { data: { type_id: 1 } }, to: { data: { type_id: 1 } } }`, async () => { 15 | const r: any = await deep.insert({ type_id: 1, string: { data: { value: 'abc' } }, from: { data: { type_id: 1 } }, to: { data: { type_id: 1 } } }, { returning: `id type_id value from_id from { id type_id } to_id to { id type_id }` }); 16 | const rId = r?.data?.[0]?.id; 17 | const vId = r?.data?.[0]?.value?.id; 18 | assert.equal(r?.data, [{ 19 | id: rId, type_id: 1, 20 | value: { id: vId, link_id: rId, value: 'abc' }, 21 | from_id: rId - 1, from: { id: rId - 1, type_id: 1, __typename: 'links' }, 22 | to_id: rId - 2, to: { id: rId - 2, type_id: 1, __typename: 'links' }, 23 | }]); 24 | }); 25 | }); -------------------------------------------------------------------------------- /tests/minilinks-query.ts: -------------------------------------------------------------------------------- 1 | import { assert, expect } from 'chai'; 2 | import { MinilinkCollection, MinilinksGeneratorOptionsDefault } from '../imports/minilinks.js'; 3 | 4 | describe('minilinks-query', () => { 5 | it(`minilinks.query { _or: [{ id: { _eq: 1 } }, { id: { _eq: 2 } }] }`, async () => { 6 | const mlc = new MinilinkCollection(MinilinksGeneratorOptionsDefault); 7 | mlc.add([ 8 | { id: 1, type_id: 1 }, 9 | { id: 2, type_id: 1 }, 10 | { id: 3, type_id: 1 }, 11 | ]); 12 | expect(mlc.query({ _or: [{ id: { _eq: 1 } }, { id: { _eq: 2 } }] })).to.have.lengthOf(2); 13 | }); 14 | it(`minilinks.query { _and: [{ type_id: { _eq: 1 } }, { id: { _eq: 2 } }] }`, async () => { 15 | const mlc = new MinilinkCollection(MinilinksGeneratorOptionsDefault); 16 | mlc.add([ 17 | { id: 1, type_id: 1 }, 18 | { id: 2, type_id: 1 }, 19 | { id: 3, type_id: 1 }, 20 | ]); 21 | expect(mlc.query({ _and: [{ type_id: { _eq: 1 } }, { id: { _eq: 2 } }] })).to.have.lengthOf(1); 22 | }); 23 | it(`minilinks.query { _not: { id: { _eq: 1 } } }`, async () => { 24 | const mlc = new MinilinkCollection(MinilinksGeneratorOptionsDefault); 25 | mlc.add([ 26 | { id: 1, type_id: 1 }, 27 | { id: 2, type_id: 1 }, 28 | { id: 3, type_id: 1 }, 29 | ]); 30 | expect(mlc.query({ _not: { id: { _eq: 3 } } })).to.have.lengthOf(2); 31 | }); 32 | it(`minilinks.query { id: { _in: [2] } }`, async () => { 33 | const mlc = new MinilinkCollection(MinilinksGeneratorOptionsDefault); 34 | mlc.add([ 35 | { id: 1, type_id: 1 }, 36 | { id: 2, type_id: 1 }, 37 | { id: 3, type_id: 1 }, 38 | ]); 39 | expect(mlc.query({ id: { _in: [2] } })).to.have.lengthOf(1); 40 | }); 41 | it(`minilinks.query { id: { _gt: 2 } }`, async () => { 42 | const mlc = new MinilinkCollection(MinilinksGeneratorOptionsDefault); 43 | mlc.add([ 44 | { id: 1, type_id: 3, }, 45 | { id: 3, type_id: 3, from_id: 1, to_id: 2 }, 46 | { id: 5, type_id: 3, from_id: 7, to_id: 3 }, 47 | ]); 48 | expect(mlc.query({ id: { _gt: 2 } })).to.have.lengthOf(2); 49 | }); 50 | it(`minilinks.query { typed: { from_id: { _eq: 7 } } }`, async () => { 51 | const mlc = new MinilinkCollection(MinilinksGeneratorOptionsDefault); 52 | mlc.add([ 53 | { id: 1, type_id: 3, }, 54 | { id: 3, type_id: 3, from_id: 1, to_id: 2 }, 55 | { id: 5, type_id: 3, from_id: 7, to_id: 3 }, 56 | ]); 57 | expect(mlc.query({ typed: { from_id: { _eq: 7 } } })).to.have.lengthOf(1); 58 | }); 59 | it(`minilinks.query { type_id: 3, to: { type_id: 2 } }`, async () => { 60 | const mlc = new MinilinkCollection(MinilinksGeneratorOptionsDefault); 61 | mlc.add([ 62 | { id: 1, type_id: 2, }, 63 | { id: 3, type_id: 3, from_id: 4, to_id: 4 }, 64 | { id: 6, type_id: 3, from_id: 1, to_id: 1 }, 65 | ]); 66 | expect(mlc.query({ 67 | type_id: 3, 68 | to: { type_id: 2 }, 69 | })).to.have.lengthOf(1); 70 | }); 71 | it(`minilinks.query { from_id: undefined }`, async () => { 72 | const mlc = new MinilinkCollection(MinilinksGeneratorOptionsDefault); 73 | mlc.add([ 74 | { id: 1, type_id: 2, }, 75 | ]); 76 | assert.throws(() => { 77 | mlc.query({ from_id: undefined }); 78 | }, 'from_id === undefined'); 79 | }); 80 | it(`minilinks.query { value: { value: { _eq: 'abc' } } }`, async () => { 81 | const mlc = new MinilinkCollection(MinilinksGeneratorOptionsDefault); 82 | mlc.add([ 83 | { id: 1, type_id: 2, }, 84 | { id: 2, type_id: 2, string: { value: 'abc' } }, 85 | ]); 86 | expect(mlc.query({ 87 | string: { value: { _eq: 'abc' } } 88 | })).to.have.lengthOf(1); 89 | }); 90 | it(`minilinks.query { value: { value: { _like: 'bc' } } }`, async () => { 91 | const mlc = new MinilinkCollection(MinilinksGeneratorOptionsDefault); 92 | mlc.add([ 93 | { id: 1, type_id: 2, }, 94 | { id: 2, type_id: 2, value: { value: 'abc' } }, 95 | ]); 96 | expect(mlc.query({ 97 | value: { value: { _like: 'abc' } } 98 | })).to.have.lengthOf(1); 99 | }); 100 | it(`minilinks.query { in: { id: 2 } }`, async () => { 101 | const mlc = new MinilinkCollection(MinilinksGeneratorOptionsDefault); 102 | mlc.add([ 103 | { id: 1, type_id: 2, }, 104 | { id: 2, type_id: 2, to_id: 1, from_id: 1 }, 105 | ]); 106 | expect(mlc.query({ in: { id: 2 } })).to.have.lengthOf(1); 107 | }); 108 | }); -------------------------------------------------------------------------------- /tests/query.ts: -------------------------------------------------------------------------------- 1 | import { assert, expect } from 'chai'; 2 | import { MinilinkCollection, MinilinksGeneratorOptionsDefault } from '../imports/minilinks.js'; 3 | import { generateQuery, generateQueryData } from '../imports/gql/query.js'; 4 | import { serializeQuery } from '../imports/client.js'; 5 | 6 | describe('generateQuery', () => { 7 | it(`generateQueryData`, async () => { 8 | const name = 'SELECT'; 9 | const exp = { return: { abc: { relation: 'out', type_id: 123, return: { qwe: { relation: 'in', type_id: 234, }, xyz: { relation: 'to' } } } } }; 10 | const query = serializeQuery(exp, 'links'); 11 | const variables = {}; 12 | const queryDataInput = { 13 | tableName: 'links', 14 | tableNamePostfix: '', 15 | returning: `id type_id from_id to_id value`, 16 | variables: { 17 | ...variables, 18 | ...query, 19 | }, 20 | }; 21 | const queryData = generateQueryData(queryDataInput); 22 | const generatedQuery = generateQuery({ 23 | queries: [ 24 | queryData 25 | ], 26 | name: name, 27 | }); 28 | const _queryData = queryData('q', 0); 29 | // console.log(JSON.stringify(_queryData.resultVariables, null, 2), _queryData, generatedQuery); // DEBUG 30 | expect(generatedQuery.queryString).to.be.equal(`query SELECT ($distinct_on0: [links_select_column!],$limit0: Int,$offset0: Int,$order_by0: [links_order_by!],$where0: links_bool_exp!,$distinct_on0_abc_qwe: [links_select_column!],$limit0_abc_qwe: Int,$offset0_abc_qwe: Int,$order_by0_abc_qwe: [links_order_by!],$where0_abc_qwe: links_bool_exp!,$distinct_on0_abc: [links_select_column!],$limit0_abc: Int,$offset0_abc: Int,$order_by0_abc: [links_order_by!],$where0_abc: links_bool_exp!) { q0: links(distinct_on: $distinct_on0,limit: $limit0,offset: $offset0,order_by: $order_by0,where: $where0) { id type_id from_id to_id value abc: out(distinct_on: $distinct_on0_abc,limit: $limit0_abc,offset: $offset0_abc,order_by: $order_by0_abc,where: $where0_abc) { id type_id from_id to_id value qwe: in(distinct_on: $distinct_on0_abc_qwe,limit: $limit0_abc_qwe,offset: $offset0_abc_qwe,order_by: $order_by0_abc_qwe,where: $where0_abc_qwe) { id type_id from_id to_id value } } } }`); 31 | expect(_queryData.resultVariables).to.be.deep.equal({ 32 | "where0": {}, 33 | "where0_abc_qwe": { 34 | "type_id": { 35 | "_eq": 234 36 | } 37 | }, 38 | "where0_abc": { 39 | "type_id": { 40 | "_eq": 123 41 | } 42 | } 43 | }); 44 | }); 45 | }); -------------------------------------------------------------------------------- /tests/reserve.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import { DeepClient } from "../imports/client"; 3 | import { assert } from 'chai'; 4 | 5 | const apolloClient = generateApolloClient({ 6 | path: `${process.env.DEEPLINKS_HASURA_PATH}/v1/graphql`, 7 | ssl: !!+process.env.DEEPLINKS_HASURA_SSL, 8 | secret: process.env.DEEPLINKS_HASURA_SECRET, 9 | }); 10 | 11 | const root = new DeepClient({ apolloClient }); 12 | 13 | let adminToken: string; 14 | let deep: any; 15 | 16 | beforeAll(async () => { 17 | const { linkId, token } = await root.jwt({ linkId: await root.id('deep', 'admin') }); 18 | adminToken = token; 19 | deep = new DeepClient({ deep: root, token: adminToken, linkId }); 20 | }); 21 | 22 | describe('reserve', () => { 23 | it(`root reserve`, async () => { 24 | const ids = await root.reserve(3); 25 | assert.equal(ids.length, 3); 26 | }); 27 | it(`admin reserve`, async () => { 28 | const ids = await deep.reserve(3); 29 | assert.equal(ids.length, 3); 30 | }); 31 | }); -------------------------------------------------------------------------------- /tests/tree.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import { HasuraApi } from '@deep-foundation/hasura/api.js'; 3 | import { DeepClient } from "../imports/client"; 4 | import { assert } from 'chai'; 5 | import { insertHandler, deleteHandler } from "../imports/handlers"; 6 | import { _ids } from '../imports/client.js'; 7 | import { delay } from "../imports/promise"; 8 | 9 | export const api = new HasuraApi({ 10 | path: process.env.DEEPLINKS_HASURA_PATH, 11 | ssl: !!+process.env.DEEPLINKS_HASURA_SSL, 12 | secret: process.env.DEEPLINKS_HASURA_SECRET, 13 | }); 14 | 15 | const apolloClient = generateApolloClient({ 16 | path: `${process.env.DEEPLINKS_HASURA_PATH}/v1/graphql`, 17 | ssl: !!+process.env.DEEPLINKS_HASURA_SSL, 18 | secret: process.env.DEEPLINKS_HASURA_SECRET, 19 | }); 20 | 21 | const deep = new DeepClient({ apolloClient }); 22 | 23 | describe('tree', () => { 24 | it(`promiseTree`, async () => { 25 | let linksToDelete = []; 26 | const typeTypeLinkId = await deep.id("@deep-foundation/core", "Type"); 27 | const handleInsertTypeLinkId = await deep.id("@deep-foundation/core", "HandleInsert"); 28 | const { data: [{ id: customTypeLinkId }] } = await deep.insert({ 29 | type_id: typeTypeLinkId, 30 | }); 31 | linksToDelete.push(customTypeLinkId); 32 | const handler = await insertHandler(handleInsertTypeLinkId, customTypeLinkId, `async () => {}`); 33 | const { data: [{ id: customLinkId }] } = await deep.insert({ 34 | type_id: customTypeLinkId 35 | }); 36 | linksToDelete.push(customLinkId); 37 | await delay(5000); 38 | try { 39 | const { data: promiseTreeLinksDownToCustomLink } = await deep.select({ 40 | up: { 41 | parent_id: {_eq: customLinkId}, 42 | tree_id: {_eq: await deep.id("@deep-foundation/core", "promiseTree")} 43 | } 44 | }); 45 | console.log({promiseTreeLinksDownToCustomLink}) 46 | linksToDelete = [...linksToDelete, ...promiseTreeLinksDownToCustomLink.map(link => link.id)]; 47 | const thenTypeLinkId = await deep.id("@deep-foundation/core", "Then"); 48 | const thenLinkId = promiseTreeLinksDownToCustomLink.find(link => link.type_id === thenTypeLinkId); 49 | assert.notEqual(thenLinkId, undefined); 50 | 51 | const promiseTypeLinkId = await deep.id("@deep-foundation/core", "Promise"); 52 | const promiseLinkId = promiseTreeLinksDownToCustomLink.find(link => link.type_id === promiseTypeLinkId); 53 | assert.notEqual(promiseLinkId, undefined); 54 | 55 | const resolvedTypeLinkId = await deep.id("@deep-foundation/core", "Resolved"); 56 | const resolvedLinkId = promiseTreeLinksDownToCustomLink.find(link => link.type_id === resolvedTypeLinkId); 57 | assert.notEqual(resolvedLinkId, undefined); 58 | } finally { 59 | await deleteHandler(handler); 60 | await deep.delete(linksToDelete); 61 | } 62 | }); 63 | }); -------------------------------------------------------------------------------- /tests/typing.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import { DeepClient } from "../imports/client"; 3 | import { assert } from 'chai'; 4 | 5 | const apolloClient = generateApolloClient({ 6 | path: `${process.env.DEEPLINKS_HASURA_PATH}/v1/graphql`, 7 | ssl: !!+process.env.DEEPLINKS_HASURA_SSL, 8 | secret: process.env.DEEPLINKS_HASURA_SECRET, 9 | }); 10 | 11 | const root = new DeepClient({ apolloClient }); 12 | 13 | let adminToken: string; 14 | let deep: any; 15 | 16 | describe('typing', () => { 17 | beforeAll(async () => { 18 | const { linkId, token } = await root.jwt({ linkId: await root.id('deep', 'admin') }); 19 | adminToken = token; 20 | deep = new DeepClient({ deep: root, token: adminToken, linkId }); 21 | }); 22 | it(`type required`, async () => { 23 | let throwed = false; 24 | try { await deep.insert({ type_id: 999999 }); } 25 | catch (error) { throwed = true; } 26 | assert.equal(throwed, true); 27 | }); 28 | it(`particular links`, async () => { 29 | let throwed = false; 30 | try { await deep.insert({ type_id: 1, from_id: 1 }); } 31 | catch (error) { throwed = true; } 32 | assert.equal(throwed, true); 33 | }); 34 | it(`type 1`, async () => { 35 | const { data: [{ id }] } = await deep.insert({ type_id: 1 }); 36 | assert.equal(typeof(id), 'number'); 37 | }); 38 | it(`custom id restricted`, async () => { 39 | let throwed = false; 40 | try { await deep.insert({ id: 8888888, type_id: 1 }); } 41 | catch (error) { throwed = true; } 42 | assert.equal(throwed, true); 43 | }); 44 | it(`link equal from/to type`, async () => { 45 | const { data: [{ id: typeFromId }] } = await deep.insert({ type_id: 1 }); 46 | const { data: [{ id: typeToId }] } = await deep.insert({ type_id: 1 }); 47 | const { data: [{ id: typeId }] } = await deep.insert({ type_id: 1, from_id: typeFromId, to_id: typeToId }); 48 | const { data: [{ id: fromId }] } = await deep.insert({ type_id: typeFromId }); 49 | const { data: [{ id: toId }] } = await deep.insert({ type_id: typeToId }); 50 | await deep.insert({ type_id: typeId, from_id: fromId, to_id: toId }); 51 | }); 52 | it(`link invalid from type`, async () => { 53 | const { data: [{ id: typeFromToId }] } = await deep.insert({ type_id: 1 }); 54 | const { data: [{ id: invalidTypeId }] } = await deep.insert({ type_id: 1 }); 55 | const { data: [{ id: typeId }] } = await deep.insert({ type_id: 1, from_id: typeFromToId, to_id: typeFromToId }); 56 | const { data: [{ id: fromId }] } = await deep.insert({ type_id: typeFromToId }); 57 | const { data: [{ id: toId }] } = await deep.insert({ type_id: invalidTypeId }); 58 | 59 | let throwed = false; 60 | try { await deep.insert({ type_id: typeId, from_id: fromId, to_id: toId }); } 61 | catch (error) { 62 | assert.isTrue(error.message.startsWith('Type conflict link: { id: ')); 63 | assert.isTrue(error.message.endsWith(`type: ${typeId}, from: ${fromId}, to: ${toId} } expected type: { type: ${typeId}, from: ${typeFromToId}, to: ${typeFromToId} } received type: { type: ${typeId}, from: ${typeFromToId}, to: ${invalidTypeId} }`)); 64 | throwed = true; 65 | } 66 | assert.equal(throwed, true); 67 | }); 68 | }); -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "experimentalDecorators": true, 5 | "sourceMap": true, 6 | "noImplicitAny": false, 7 | "removeComments": true, 8 | "jsx": "react", 9 | "module": "ESNext", 10 | "moduleResolution": "node", 11 | "target": "es2015", 12 | "skipLibCheck": true, 13 | "lib": [ 14 | "dom", 15 | "es6", 16 | "es7", 17 | "es2022.error" 18 | ], 19 | "resolveJsonModule": true, 20 | "esModuleInterop": true, 21 | "isolatedModules": true, 22 | "declaration": true, 23 | } 24 | } -------------------------------------------------------------------------------- /unit.test.ts: -------------------------------------------------------------------------------- 1 | import { createRequire } from 'module'; 2 | import { jest } from '@jest/globals'; 3 | const require = createRequire(import.meta.url); 4 | 5 | require('dotenv').config(); 6 | 7 | jest.setTimeout(120000); 8 | 9 | import './tests/minilinks'; 10 | import './tests/minilinks-query'; 11 | import './tests/query'; -------------------------------------------------------------------------------- /unix-start.sh: -------------------------------------------------------------------------------- 1 | curl https://raw.githubusercontent.com/deep-foundation/deeplinks/main/docker-compose.yml > ./docker-compose.yml 2 | echo 'Clean and starting containers' 3 | docker compose -p deep down -v --remove-orphans 4 | docker compose pull 5 | docker compose -p deep up -d 6 | sleep 5 7 | echo 'Migrating data' 8 | sleep 5 9 | echo 'It will be faster soon, but now please wait...' 10 | curl -s -X POST http://localhost:3007/api/deeplinks --data '{"operation":"run"}' --header "Content-Type: application/json" > /dev/null 11 | echo 'Migrations done!' 12 | # (macos) || (linux) open default browser 13 | (open http://localhost:3007/ 1>/dev/null 2>&1) || (xdg-open http://localhost:3007/ 1>/dev/null 2>&1) 14 | -------------------------------------------------------------------------------- /warmup.ts: -------------------------------------------------------------------------------- 1 | import { generateApolloClient } from '@deep-foundation/hasura/client.js'; 2 | import { DeepClient } from "./imports/client.js"; 3 | import axios from 'axios'; 4 | import Debug from 'debug'; 5 | import { serializeError } from 'serialize-error'; 6 | const debug = Debug('deeplinks:warmup'); 7 | 8 | const apolloClient = generateApolloClient({ 9 | path: `${process.env.DEEPLINKS_HASURA_PATH}/v1/graphql`, 10 | ssl: !!+process.env.DEEPLINKS_HASURA_SSL, 11 | secret: process.env.DEEPLINKS_HASURA_SECRET, 12 | }); 13 | 14 | const deep = new DeepClient({ apolloClient }); 15 | 16 | export const checkSystemStatus = async (): Promise<{ result?: any; error?: any }> => { 17 | try { 18 | const status = await axios.post(`http://localhost:3006/gql`, { "query": "{ healthz { status } }" }, { validateStatus: status => true, timeout: 7000 }); 19 | // console.log('system status result', JSON.stringify(status?.data)); 20 | return { result: status?.data?.data?.healthz?.[0].status }; 21 | } catch (e) { 22 | // console.log('system status error', e); 23 | const serializedError = serializeError(e); 24 | return { error: serializedError }; 25 | } 26 | }; 27 | 28 | (async () => { 29 | let systemIsReady = false; 30 | while(!systemIsReady) { 31 | systemIsReady = (await checkSystemStatus()).result === 'ok'; 32 | } 33 | const stringClientHandlerId = await deep.id("@deep-foundation/deepcase", "stringClientHandler"); 34 | const stringClientHandler = (await deep.select(stringClientHandlerId)); 35 | // console.log(JSON.stringify(stringClientHandler, null, 2)); 36 | let jsx = stringClientHandler?.data?.[0]?.value?.value; 37 | // console.log(jsx); 38 | if (jsx) { 39 | var lastChar = jsx.substr(jsx.length - 1); 40 | // console.log('lastChar', lastChar); 41 | if (lastChar == '}') { 42 | jsx += " "; 43 | } else { 44 | jsx = jsx.trim(); 45 | } 46 | await deep.update({ link_id: stringClientHandlerId }, { value: jsx }, { table: `strings` }); 47 | console.log('JavaScript Docker Isolation Provider startup triggered.'); 48 | } else { 49 | console.log('Link value to trigger JavaScript Docker Isolation Provider startup is not found.'); 50 | } 51 | })(); -------------------------------------------------------------------------------- /windows-start.ps1: -------------------------------------------------------------------------------- 1 | wget -Uri "https://raw.githubusercontent.com/deep-foundation/deeplinks/main/docker-compose.yml" -OutFile ".\docker-compose.yml" 2 | wget -Uri "https://raw.githubusercontent.com/deep-foundation/deeplinks/main/open-deep.html" -OutFile ".\open-deep.html" 3 | docker compose -p deep down -v --remove-orphans; docker compose pull; docker compose -p deep up -d 4 | sleep 5 5 | echo 'Migrating data' 6 | echo 'It will be faster soon, but now please wait...' 7 | $postParams = @{operation='run'}; wget -Method POST -Uri "http://localhost:3007/api/deeplinks" -Body $postParams | Out-Null 8 | echo 'Migrations done!"' 9 | sleep 1 10 | Invoke-Expression .\open-deep.html 11 | --------------------------------------------------------------------------------