@opengeni/contracts 5.5.0 → 5.5.2-canary.40656248492001
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-authored-durable-text.d.ts +9 -9
- package/dist/agent-config.d.ts +148 -37
- package/dist/api-key-presets.d.ts +19 -0
- package/dist/app-action-catalog-types.d.ts +32 -0
- package/dist/app-action-catalog.gen.d.ts +2 -0
- package/dist/app-actions.d.ts +91 -0
- package/dist/app-actions.js +10130 -0
- package/dist/app-actions.js.map +1 -0
- package/dist/app-shot.d.ts +55 -0
- package/dist/artifact-catalog.d.ts +33 -0
- package/dist/artifacts.d.ts +166 -0
- package/dist/atlassian-native-retirement.d.ts +16 -0
- package/dist/atlassian-native-retirement.js +30 -0
- package/dist/atlassian-native-retirement.js.map +1 -0
- package/dist/atlassian.js +48 -23
- package/dist/automation-templates.d.ts +434 -0
- package/dist/automation-templates.js +31 -0
- package/dist/automation-templates.js.map +1 -0
- package/dist/billing-subscriptions.d.ts +61 -0
- package/dist/bots.d.ts +583 -0
- package/dist/bundled-skills.d.ts +58 -2
- package/dist/canonical-human-identities.js +3 -1
- package/dist/child-lifecycle-notice-protocol.d.ts +45 -0
- package/dist/chunk-2QKIEAGY.js +396 -0
- package/dist/chunk-2QKIEAGY.js.map +1 -0
- package/dist/chunk-2WOWEWXV.js +426 -0
- package/dist/chunk-2WOWEWXV.js.map +1 -0
- package/dist/chunk-2ZIHHL72.js +153 -0
- package/dist/chunk-2ZIHHL72.js.map +1 -0
- package/dist/chunk-32U6NKQQ.js +389 -0
- package/dist/chunk-32U6NKQQ.js.map +1 -0
- package/dist/chunk-4YDDHSO2.js +799 -0
- package/dist/chunk-4YDDHSO2.js.map +1 -0
- package/dist/chunk-56SNIMDO.js +9 -0
- package/dist/chunk-56SNIMDO.js.map +1 -0
- package/dist/chunk-5RWB6QVL.js +101 -0
- package/dist/chunk-5RWB6QVL.js.map +1 -0
- package/dist/chunk-5UGUOT3L.js +243 -0
- package/dist/chunk-5UGUOT3L.js.map +1 -0
- package/dist/chunk-6LAT33BH.js +130 -0
- package/dist/chunk-6LAT33BH.js.map +1 -0
- package/dist/chunk-7272KVWZ.js +60 -0
- package/dist/chunk-7272KVWZ.js.map +1 -0
- package/dist/chunk-72QC2GTL.js +36 -0
- package/dist/chunk-72QC2GTL.js.map +1 -0
- package/dist/chunk-7EQGGZBY.js +105 -0
- package/dist/chunk-7EQGGZBY.js.map +1 -0
- package/dist/chunk-A3LXWVYU.js +382 -0
- package/dist/chunk-A3LXWVYU.js.map +1 -0
- package/dist/chunk-AQYDZUJV.js +34 -0
- package/dist/chunk-AQYDZUJV.js.map +1 -0
- package/dist/chunk-BHCEVVUP.js +93 -0
- package/dist/chunk-BHCEVVUP.js.map +1 -0
- package/dist/chunk-BMUTAZX5.js +486 -0
- package/dist/chunk-BMUTAZX5.js.map +1 -0
- package/dist/chunk-DGG6IBBK.js +150 -0
- package/dist/chunk-DGG6IBBK.js.map +1 -0
- package/dist/{chunk-YBW5JSA3.js → chunk-FBCTUMHI.js} +123 -102
- package/dist/chunk-FBCTUMHI.js.map +1 -0
- package/dist/chunk-G2KJTFIC.js +97 -0
- package/dist/chunk-G2KJTFIC.js.map +1 -0
- package/dist/chunk-G4RKARXZ.js +1167 -0
- package/dist/chunk-G4RKARXZ.js.map +1 -0
- package/dist/chunk-H6UJJIUM.js +350 -0
- package/dist/chunk-H6UJJIUM.js.map +1 -0
- package/dist/chunk-JOTHDW4H.js +18 -0
- package/dist/chunk-JOTHDW4H.js.map +1 -0
- package/dist/chunk-L4GUEP7K.js +56480 -0
- package/dist/chunk-L4GUEP7K.js.map +1 -0
- package/dist/{chunk-SIOU2K4U.js → chunk-LJH2EYGT.js} +23 -23
- package/dist/chunk-MTJKSIGA.js +161 -0
- package/dist/chunk-MTJKSIGA.js.map +1 -0
- package/dist/chunk-MYHMB6GZ.js +202 -0
- package/dist/chunk-MYHMB6GZ.js.map +1 -0
- package/dist/chunk-PI5VIKJY.js +660 -0
- package/dist/chunk-PI5VIKJY.js.map +1 -0
- package/dist/{chunk-RIY7GYQS.js → chunk-PTSROVDG.js} +2 -2
- package/dist/chunk-PUIUXIOL.js +189 -0
- package/dist/chunk-PUIUXIOL.js.map +1 -0
- package/dist/chunk-R6VXJIMB.js +19 -0
- package/dist/chunk-R6VXJIMB.js.map +1 -0
- package/dist/chunk-RLJUNVAM.js +15 -0
- package/dist/chunk-RLJUNVAM.js.map +1 -0
- package/dist/chunk-RR6APCBK.js +213 -0
- package/dist/chunk-RR6APCBK.js.map +1 -0
- package/dist/{chunk-TXTQIYCV.js → chunk-RUQ4I6DO.js} +59 -13
- package/dist/chunk-RUQ4I6DO.js.map +1 -0
- package/dist/{chunk-WYBDERIY.js → chunk-RWEEUDHD.js} +53 -42
- package/dist/chunk-RWEEUDHD.js.map +1 -0
- package/dist/chunk-S7MSQQL6.js +158 -0
- package/dist/chunk-S7MSQQL6.js.map +1 -0
- package/dist/{chunk-5Q54VCCH.js → chunk-SHO4YO4B.js} +20 -36
- package/dist/chunk-SHO4YO4B.js.map +1 -0
- package/dist/{chunk-HHZSZYYF.js → chunk-T3HSWMA5.js} +20 -4
- package/dist/chunk-T3HSWMA5.js.map +1 -0
- package/dist/{chunk-YRKRZ6OF.js → chunk-TOOJIBJC.js} +2 -2
- package/dist/chunk-UCUOTRS6.js +431 -0
- package/dist/chunk-UCUOTRS6.js.map +1 -0
- package/dist/chunk-UH4K7GP7.js +424 -0
- package/dist/chunk-UH4K7GP7.js.map +1 -0
- package/dist/chunk-UIZWGPHV.js +120 -0
- package/dist/chunk-UIZWGPHV.js.map +1 -0
- package/dist/chunk-VLUKBV6Y.js +1170 -0
- package/dist/chunk-VLUKBV6Y.js.map +1 -0
- package/dist/chunk-W2QQ5Y6K.js +55 -0
- package/dist/chunk-W2QQ5Y6K.js.map +1 -0
- package/dist/{chunk-Y3KNXVTD.js → chunk-W3NBPR4E.js} +131 -31
- package/dist/chunk-W3NBPR4E.js.map +1 -0
- package/dist/{chunk-NRMCVHAU.js → chunk-WMGO4HJU.js} +11 -9
- package/dist/chunk-WMGO4HJU.js.map +1 -0
- package/dist/{chunk-MYBZRDTY.js → chunk-WQI7VNN4.js} +19 -9
- package/dist/chunk-WQI7VNN4.js.map +1 -0
- package/dist/{chunk-PX3VCKAU.js → chunk-WVCFSOUZ.js} +1 -1
- package/dist/{chunk-PX3VCKAU.js.map → chunk-WVCFSOUZ.js.map} +1 -1
- package/dist/chunk-XB6GQLBH.js +76 -0
- package/dist/chunk-XB6GQLBH.js.map +1 -0
- package/dist/{chunk-D2HKF2EF.js → chunk-XP5M3IKT.js} +2 -2
- package/dist/{chunk-O77VSFYZ.js → chunk-ZMPU5BWE.js} +12 -2
- package/dist/chunk-ZMPU5BWE.js.map +1 -0
- package/dist/{chunk-3SIZGL3O.js → chunk-ZYVEJFNU.js} +22 -18
- package/dist/chunk-ZYVEJFNU.js.map +1 -0
- package/dist/claude-provider-account-authority.d.ts +18 -0
- package/dist/claude-subscription-oauth.d.ts +25 -0
- package/dist/claude-subscription-usage.d.ts +95 -1
- package/dist/client-error-report.d.ts +154 -8
- package/dist/client-error-report.js +157 -2
- package/dist/client-error-report.js.map +1 -1
- package/dist/code-search.d.ts +1 -1
- package/dist/code-search.js.map +1 -1
- package/dist/codex-provider-account-authority.js +23 -12
- package/dist/codex-provider-account-authority.js.map +1 -1
- package/dist/company-profile.d.ts +144 -0
- package/dist/connect.d.ts +38 -0
- package/dist/connect.js +244 -164
- package/dist/connect.js.map +1 -1
- package/dist/connection-account-label.d.ts +2 -0
- package/dist/connection-account-label.js +34 -0
- package/dist/connection-account-label.js.map +1 -0
- package/dist/connection-authority.js +49 -24
- package/dist/connector-attachments.js +3 -1
- package/dist/connector-destinations.js +3 -1
- package/dist/connector-tool-permissions.d.ts +68 -4
- package/dist/credential-profiles.d.ts +166 -0
- package/dist/cua-desktop-tools.d.ts +19 -0
- package/dist/define-schema.d.ts +8 -0
- package/dist/desktop-app.d.ts +28 -0
- package/dist/direct-model-provider.d.ts +38 -0
- package/dist/document-artifact-commands.js +2 -2
- package/dist/document-artifact-query.js +2 -2
- package/dist/editable-artifact-binary.d.ts +7 -0
- package/dist/editable-artifact-causal-frontier.js +2 -2
- package/dist/editable-artifact-codec-registry.js +7 -7
- package/dist/editable-artifact-committed-transaction.js +4 -2
- package/dist/editable-artifact-live.js +8 -8
- package/dist/editable-artifact-serialized-commit.js +9 -9
- package/dist/editable-artifact-versions.d.ts +7 -1
- package/dist/editable-artifact-versions.js +1 -1
- package/dist/editable-artifacts.js +49 -43
- package/dist/event-preview.d.ts +22 -0
- package/dist/event-triggers.d.ts +1253 -0
- package/dist/event-triggers.js +148 -0
- package/dist/event-triggers.js.map +1 -0
- package/dist/external-identities.d.ts +34 -0
- package/dist/external-identities.js +4 -2
- package/dist/feedback.d.ts +181 -0
- package/dist/file-access.d.ts +23 -0
- package/dist/first-party-tool-authorization.d.ts +852 -0
- package/dist/github-repository-contracts.js +64 -43
- package/dist/github-repository-contracts.js.map +1 -1
- package/dist/google-drive-native-retirement.d.ts +12 -0
- package/dist/google-drive-native-retirement.js +20 -0
- package/dist/google-drive-native-retirement.js.map +1 -0
- package/dist/google-drive.d.ts +1 -1
- package/dist/google-drive.js +323 -239
- package/dist/google-drive.js.map +1 -1
- package/dist/host-mcp-bindings.js +250 -198
- package/dist/host-mcp-bindings.js.map +1 -1
- package/dist/index.d.ts +29072 -20593
- package/dist/index.js +2432 -366
- package/dist/inference-residency.d.ts +22 -0
- package/dist/insights-usage.d.ts +948 -0
- package/dist/insights-usage.js +608 -0
- package/dist/insights-usage.js.map +1 -0
- package/dist/integration-capability-id.d.ts +8 -0
- package/dist/interaction-action-deadline.d.ts +48 -0
- package/dist/interaction-driver-diagnostic.d.ts +28 -0
- package/dist/interaction.d.ts +2126 -881
- package/dist/knowledge-entries.d.ts +38 -0
- package/dist/knowledge-preparation.d.ts +4 -0
- package/dist/learning-refusal.d.ts +43 -0
- package/dist/learning-refusal.js +49 -0
- package/dist/learning-refusal.js.map +1 -0
- package/dist/managed-auth-session-sets.d.ts +1 -0
- package/dist/managed-auth-session-sets.js +207 -146
- package/dist/managed-auth-session-sets.js.map +1 -1
- package/dist/managed-sign-in-methods.d.ts +96 -0
- package/dist/managed-sign-in-methods.js +157 -35
- package/dist/managed-sign-in-methods.js.map +1 -1
- package/dist/mcp-structured-content.d.ts +25 -0
- package/dist/message-time-zone.d.ts +38 -0
- package/dist/message-time-zone.js +19 -0
- package/dist/message-time-zone.js.map +1 -0
- package/dist/modal-native-proof-v2.d.ts +587 -0
- package/dist/modal-native-proof-v2.js +39 -0
- package/dist/modal-native-proof-v2.js.map +1 -0
- package/dist/model-account-choice.d.ts +237 -0
- package/dist/model-account-settings.d.ts +422 -0
- package/dist/model-availability.d.ts +15 -0
- package/dist/model-connection-access.d.ts +13 -0
- package/dist/model-context-inspector.d.ts +8 -0
- package/dist/model-display.d.ts +43 -0
- package/dist/model-display.js +17 -0
- package/dist/model-display.js.map +1 -0
- package/dist/model-picker-order.js.map +1 -1
- package/dist/model-residency.d.ts +304 -0
- package/dist/organization-access.d.ts +416 -0
- package/dist/organization-access.js +40 -0
- package/dist/organization-access.js.map +1 -0
- package/dist/organization-integration-policy.d.ts +18 -0
- package/dist/organization-membership-lifecycle.d.ts +66 -0
- package/dist/organization-model-usage.d.ts +146 -0
- package/dist/organization-model-usage.js +87 -0
- package/dist/organization-model-usage.js.map +1 -0
- package/dist/organization-recovery.js +3 -1
- package/dist/organization-usage.d.ts +12 -0
- package/dist/passkey-only-account.d.ts +14 -0
- package/dist/passkey-only-account.js +13 -0
- package/dist/passkey-only-account.js.map +1 -0
- package/dist/permissions.d.ts +19 -0
- package/dist/personal-github.js +220 -148
- package/dist/personal-github.js.map +1 -1
- package/dist/personal-integration-setup.d.ts +81 -0
- package/dist/personal-integration-setup.js +42 -0
- package/dist/personal-integration-setup.js.map +1 -0
- package/dist/prepared-mcp-setup.d.ts +54 -0
- package/dist/prepared-mcp-setup.js +13 -0
- package/dist/prepared-mcp-setup.js.map +1 -0
- package/dist/presentation-artifact-commands.js +2 -2
- package/dist/presentation-artifact-query.js +2 -2
- package/dist/primitives.d.ts +18 -0
- package/dist/product-analytics.d.ts +27 -10
- package/dist/product-lifecycle-facts.d.ts +26 -3
- package/dist/prompt-cache-diagnostics.d.ts +92 -0
- package/dist/recordings.d.ts +1371 -0
- package/dist/remember.d.ts +98 -2
- package/dist/retained-output.d.ts +4 -2
- package/dist/sandbox-file-artifacts.d.ts +19 -2
- package/dist/sandbox-provider-command.d.ts +5 -44
- package/dist/sandbox-recovery.d.ts +139 -8
- package/dist/secret-generator.d.ts +44 -0
- package/dist/secret-mask-strings.d.ts +32 -0
- package/dist/secret-references.d.ts +90 -0
- package/dist/secret-resolution.d.ts +143 -0
- package/dist/secret-shape-masking.d.ts +52 -0
- package/dist/secret-shape-masking.js +19 -0
- package/dist/secret-shape-masking.js.map +1 -0
- package/dist/secret-totp.d.ts +41 -0
- package/dist/secret-use-approval-question.d.ts +11 -0
- package/dist/secret-use-approval-question.js +11 -0
- package/dist/secret-use-approval-question.js.map +1 -0
- package/dist/secret-use-approvals.d.ts +106 -0
- package/dist/secret-value-masking.d.ts +299 -0
- package/dist/secret-value-masking.js +62 -0
- package/dist/secret-value-masking.js.map +1 -0
- package/dist/secret-value.d.ts +19 -0
- package/dist/session-history-import.d.ts +444 -8
- package/dist/session-history.d.ts +111 -0
- package/dist/session-history.js +35 -0
- package/dist/session-history.js.map +1 -0
- package/dist/session-list-entries.d.ts +8 -0
- package/dist/session-list-entries.js +61 -0
- package/dist/session-list-entries.js.map +1 -0
- package/dist/session-mcp-projections.d.ts +13 -1
- package/dist/session-mcp-tool-permissions.d.ts +62 -0
- package/dist/session-target.d.ts +18 -0
- package/dist/session-titles.d.ts +8 -0
- package/dist/session-titles.js +5 -1
- package/dist/session-usage-limit-refusal.d.ts +34 -0
- package/dist/session-usage-limit-refusal.js +15 -0
- package/dist/session-usage-limit-refusal.js.map +1 -0
- package/dist/session-usage-limits.d.ts +127 -0
- package/dist/session-usage-limits.js +43 -0
- package/dist/session-usage-limits.js.map +1 -0
- package/dist/site-data.d.ts +286 -0
- package/dist/site-operations.d.ts +39 -0
- package/dist/site-publishing.d.ts +813 -0
- package/dist/site-session-http.d.ts +103 -4
- package/dist/site-session-http.js +27 -1
- package/dist/site-sql.d.ts +425 -0
- package/dist/skills.d.ts +38 -0
- package/dist/slack-bot-scopes.d.ts +9 -7
- package/dist/slack-bot-scopes.js +6 -3
- package/dist/slack-rest-mcp.d.ts +36 -0
- package/dist/slack-rest-mcp.js +25 -0
- package/dist/slack-rest-mcp.js.map +1 -0
- package/dist/spreadsheet-artifact-commands.d.ts +14 -1
- package/dist/spreadsheet-artifact-commands.js +9 -2
- package/dist/spreadsheet-artifact-query.d.ts +13 -6
- package/dist/spreadsheet-artifact-query.js +4 -3
- package/dist/subscription-account-actions.d.ts +83 -0
- package/dist/subscription-account-pools.d.ts +326 -0
- package/dist/subscription-personal-authority-v2.d.ts +28 -0
- package/dist/subscription-personal-authority-v2.js +13 -0
- package/dist/subscription-personal-authority-v2.js.map +1 -0
- package/dist/subscription-plans.d.ts +38 -0
- package/dist/subscription-usage-pool.d.ts +89 -0
- package/dist/tool-action-review.d.ts +141 -0
- package/dist/tool-catalog.d.ts +479 -0
- package/dist/tool-policy.d.ts +5 -0
- package/dist/tool-review-presentation.d.ts +29 -0
- package/dist/tool-review-presentation.js +21 -0
- package/dist/tool-review-presentation.js.map +1 -0
- package/dist/turn-serving-policy.d.ts +223 -0
- package/dist/usage-allowances.d.ts +30 -0
- package/dist/usage-allowances.js +5 -1
- package/dist/values.d.ts +11 -0
- package/dist/values.js +19 -0
- package/dist/values.js.map +1 -0
- package/dist/vault-provider-wire.d.ts +907 -0
- package/dist/vault-provider-wire.js +74 -0
- package/dist/vault-provider-wire.js.map +1 -0
- package/dist/vaults.d.ts +2185 -0
- package/dist/video-generation.d.ts +75 -0
- package/dist/video-generation.js +8 -1
- package/dist/workspace-control-event.d.ts +18 -0
- package/dist/workspace-control-event.js +18 -0
- package/dist/workspace-control-event.js.map +1 -0
- package/dist/workspace-governance-context.d.ts +18 -0
- package/dist/workspace-integration-wire.d.ts +1 -1
- package/dist/workspace-integration-wire.js +1 -1
- package/dist/workspace-integrations.d.ts +212 -12
- package/dist/xai-provider-account-authority.js +3 -1
- package/package.json +121 -4
- package/src/agent-authored-durable-text.ts +14 -11
- package/src/agent-config.ts +382 -203
- package/src/agent-instruction-changes.ts +94 -74
- package/src/agent-learning.ts +94 -66
- package/src/agent-topology.ts +58 -52
- package/src/api-key-presets.ts +28 -0
- package/src/app-action-catalog-types.ts +32 -0
- package/src/app-action-catalog.gen.ts +9905 -0
- package/src/app-actions.ts +365 -0
- package/src/app-shot.ts +176 -0
- package/src/artifact-catalog.ts +81 -46
- package/src/artifacts.ts +360 -227
- package/src/atlassian-native-retirement.ts +33 -0
- package/src/atlassian.ts +136 -108
- package/src/automation-templates.ts +201 -0
- package/src/billing-subscriptions.ts +48 -0
- package/src/bots.ts +238 -0
- package/src/bundled-skills.ts +79 -22
- package/src/canonical-human-identities.ts +65 -62
- package/src/child-lifecycle-notice-protocol.gen.json +1310 -0
- package/src/child-lifecycle-notice-protocol.ts +224 -0
- package/src/claude-provider-account-authority.ts +12 -0
- package/src/claude-subscription-oauth.ts +61 -19
- package/src/claude-subscription-usage.ts +64 -35
- package/src/client-error-report.ts +344 -9
- package/src/code-search.ts +1 -1
- package/src/codex-provider-account-authority.ts +22 -16
- package/src/company-brain-governed-writes.ts +265 -244
- package/src/company-brain.ts +165 -154
- package/src/company-profile.ts +362 -297
- package/src/connect.ts +222 -173
- package/src/connection-account-label.ts +45 -0
- package/src/connection-authority.ts +172 -152
- package/src/connector-attachments.ts +139 -123
- package/src/connector-destinations.ts +48 -43
- package/src/connector-tool-permissions.ts +104 -46
- package/src/credential-profiles.ts +161 -0
- package/src/cua-desktop-tools.gen.json +6722 -0
- package/src/cua-desktop-tools.linux.gen.json +6798 -0
- package/src/cua-desktop-tools.ts +42 -0
- package/src/cua-desktop-tools.windows.gen.json +6739 -0
- package/src/define-schema.ts +10 -0
- package/src/desktop-app.ts +50 -0
- package/src/direct-model-provider.ts +112 -0
- package/src/editable-artifact-binary.ts +29 -8
- package/src/editable-artifact-committed-transaction.ts +19 -18
- package/src/editable-artifact-serialized-commit.ts +8 -2
- package/src/editable-artifact-versions.ts +7 -1
- package/src/event-preview.ts +53 -3
- package/src/event-triggers.ts +891 -0
- package/src/external-identities.ts +239 -195
- package/src/feedback.ts +125 -30
- package/src/file-access.ts +26 -0
- package/src/first-party-tool-authorization.ts +383 -0
- package/src/github-repository-contracts.ts +78 -64
- package/src/google-drive-native-retirement.ts +20 -0
- package/src/google-drive.ts +286 -234
- package/src/governed-learning-activation.ts +159 -137
- package/src/governed-learning-evaluator.ts +141 -133
- package/src/host-mcp-bindings.ts +244 -221
- package/src/image-generation.ts +105 -95
- package/src/index.ts +13928 -9899
- package/src/inference-residency.ts +36 -0
- package/src/insights-usage.ts +774 -0
- package/src/integration-capability-id.ts +16 -0
- package/src/interaction-action-deadline.ts +143 -0
- package/src/interaction-driver-diagnostic.ts +89 -0
- package/src/interaction.ts +3527 -2427
- package/src/knowledge-entries.ts +386 -299
- package/src/knowledge-preparation.ts +49 -41
- package/src/knowledge.ts +242 -213
- package/src/learning-refusal.ts +90 -0
- package/src/managed-auth-session-sets.ts +180 -133
- package/src/managed-sign-in-methods.ts +156 -34
- package/src/mcp-oauth.ts +58 -46
- package/src/mcp-receipts.ts +199 -194
- package/src/mcp-structured-content.ts +81 -0
- package/src/memory-slack-delivery.ts +172 -142
- package/src/message-time-zone.ts +109 -0
- package/src/modal-native-proof-v2.ts +430 -0
- package/src/model-account-choice.ts +178 -0
- package/src/model-account-settings.ts +210 -0
- package/src/model-availability.ts +22 -0
- package/src/model-connection-access.ts +62 -14
- package/src/model-context-inspector.ts +130 -109
- package/src/model-display.ts +221 -0
- package/src/model-picker-order.ts +1 -1
- package/src/model-residency.ts +535 -0
- package/src/organization-access.ts +216 -0
- package/src/organization-integration-policy.ts +47 -28
- package/src/organization-membership-lifecycle.ts +558 -384
- package/src/organization-model-usage.ts +82 -0
- package/src/organization-recovery.ts +148 -135
- package/src/organization-usage.ts +84 -52
- package/src/passkey-only-account.ts +29 -0
- package/src/permissions.ts +94 -52
- package/src/personal-github.ts +211 -171
- package/src/personal-integration-setup.ts +150 -0
- package/src/preference-registry.ts +319 -263
- package/src/prepared-mcp-setup.ts +190 -0
- package/src/primitives.ts +22 -0
- package/src/product-analytics.ts +40 -15
- package/src/product-lifecycle-facts.ts +50 -20
- package/src/prompt-cache-diagnostics.ts +212 -0
- package/src/recordings.ts +1153 -0
- package/src/remember.ts +184 -172
- package/src/retained-output.ts +181 -168
- package/src/sandbox-file-artifacts.ts +78 -57
- package/src/sandbox-provider-command.ts +55 -72
- package/src/sandbox-recovery.ts +163 -77
- package/src/scoped-knowledge.ts +62 -43
- package/src/secret-generator.ts +156 -0
- package/src/secret-mask-strings.ts +175 -0
- package/src/secret-references.ts +414 -0
- package/src/secret-resolution.ts +322 -0
- package/src/secret-shape-masking.ts +573 -0
- package/src/secret-totp.ts +188 -0
- package/src/secret-use-approval-question.ts +22 -0
- package/src/secret-use-approvals.ts +219 -0
- package/src/secret-value-masking.ts +1283 -0
- package/src/secret-value.ts +22 -0
- package/src/session-goal-reports.ts +55 -49
- package/src/session-history-import.ts +84 -68
- package/src/session-history.ts +197 -0
- package/src/session-list-entries.ts +106 -0
- package/src/session-mcp-projections.ts +21 -3
- package/src/session-mcp-tool-permissions.ts +109 -0
- package/src/session-message-search.ts +46 -38
- package/src/session-target.ts +58 -0
- package/src/session-titles.ts +40 -4
- package/src/session-topology-primitives.ts +20 -12
- package/src/session-usage-limit-refusal.ts +101 -0
- package/src/session-usage-limits.ts +169 -0
- package/src/signup-attribution.ts +20 -15
- package/src/site-data.ts +337 -0
- package/src/site-operations.ts +154 -0
- package/src/site-publishing.ts +903 -0
- package/src/site-session-http.ts +344 -16
- package/src/site-sql.ts +1120 -0
- package/src/skill-use.ts +18 -17
- package/src/skills.ts +105 -50
- package/src/slack-bot-scopes.ts +18 -35
- package/src/slack-rest-mcp.ts +222 -0
- package/src/slack-task-policy.ts +97 -73
- package/src/spreadsheet-artifact-commands.ts +74 -13
- package/src/spreadsheet-artifact-query.ts +188 -50
- package/src/subscription-account-actions.ts +103 -0
- package/src/subscription-account-pools.ts +74 -0
- package/src/subscription-personal-authority-v2.ts +69 -0
- package/src/subscription-plans.ts +51 -0
- package/src/subscription-usage-pool.ts +44 -0
- package/src/task-notes.ts +70 -68
- package/src/tool-action-review.ts +271 -0
- package/src/tool-catalog.ts +694 -379
- package/src/tool-policy.ts +82 -0
- package/src/tool-result-spill.ts +13 -9
- package/src/tool-review-presentation.ts +643 -0
- package/src/turn-serving-policy.ts +167 -0
- package/src/usage-allowances.ts +212 -136
- package/src/values.ts +25 -0
- package/src/vault-provider-wire.ts +580 -0
- package/src/vaults.ts +1296 -0
- package/src/video-generation.ts +430 -361
- package/src/work-claims.ts +183 -166
- package/src/workspace-control-event.ts +134 -0
- package/src/workspace-governance-context.ts +63 -0
- package/src/workspace-instruction-policies.ts +346 -279
- package/src/workspace-integration-wire.ts +10 -0
- package/src/workspace-integrations.ts +488 -398
- package/src/workspace-learning-administration.ts +58 -44
- package/src/workspace-learning-policy.ts +126 -99
- package/src/workspace-state.ts +239 -220
- package/src/xai-provider-account-authority.ts +23 -17
- package/dist/chunk-3SIZGL3O.js.map +0 -1
- package/dist/chunk-566IFOVP.js +0 -26133
- package/dist/chunk-566IFOVP.js.map +0 -1
- package/dist/chunk-5IXYF6XN.js +0 -728
- package/dist/chunk-5IXYF6XN.js.map +0 -1
- package/dist/chunk-5Q54VCCH.js.map +0 -1
- package/dist/chunk-BCS7WHCK.js +0 -139
- package/dist/chunk-BCS7WHCK.js.map +0 -1
- package/dist/chunk-EK7NDGFR.js +0 -128
- package/dist/chunk-EK7NDGFR.js.map +0 -1
- package/dist/chunk-GRG7PHWQ.js +0 -25
- package/dist/chunk-GRG7PHWQ.js.map +0 -1
- package/dist/chunk-HHZSZYYF.js.map +0 -1
- package/dist/chunk-ICDZGUX2.js +0 -186
- package/dist/chunk-ICDZGUX2.js.map +0 -1
- package/dist/chunk-JUFK3T4Q.js +0 -80
- package/dist/chunk-JUFK3T4Q.js.map +0 -1
- package/dist/chunk-MYBZRDTY.js.map +0 -1
- package/dist/chunk-NRMCVHAU.js.map +0 -1
- package/dist/chunk-O77VSFYZ.js.map +0 -1
- package/dist/chunk-P3HLDT77.js +0 -59
- package/dist/chunk-P3HLDT77.js.map +0 -1
- package/dist/chunk-TXTQIYCV.js.map +0 -1
- package/dist/chunk-WYBDERIY.js.map +0 -1
- package/dist/chunk-Y3KNXVTD.js.map +0 -1
- package/dist/chunk-YBW5JSA3.js.map +0 -1
- package/dist/chunk-YRXAWWCU.js +0 -45
- package/dist/chunk-YRXAWWCU.js.map +0 -1
- package/dist/chunk-YWMOIZ6A.js +0 -185
- package/dist/chunk-YWMOIZ6A.js.map +0 -1
- package/dist/site-request-headers.test.d.ts +0 -1
- package/dist/transcription-recordings.d.ts +0 -300
- package/src/transcription-recordings.ts +0 -169
- /package/dist/{chunk-SIOU2K4U.js.map → chunk-LJH2EYGT.js.map} +0 -0
- /package/dist/{chunk-RIY7GYQS.js.map → chunk-PTSROVDG.js.map} +0 -0
- /package/dist/{chunk-YRKRZ6OF.js.map → chunk-TOOJIBJC.js.map} +0 -0
- /package/dist/{chunk-D2HKF2EF.js.map → chunk-XP5M3IKT.js.map} +0 -0
package/src/site-sql.ts
ADDED
|
@@ -0,0 +1,1120 @@
|
|
|
1
|
+
import * as primitives from "./primitives";
|
|
2
|
+
import { defineSchema } from "./define-schema";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Site SQL: optional per-Site SQLite storage on Cloudflare (one SQLite-backed
|
|
7
|
+
* Durable Object per Site, `idFromName(siteId)`), offered next to Site Data.
|
|
8
|
+
* Postgres keeps the definitions (settings, migrations and named queries) so
|
|
9
|
+
* the database can always be rebuilt; the Durable Object holds the data.
|
|
10
|
+
* Shared by the API, the Site gateway Worker (which runs the Durable Object)
|
|
11
|
+
* and the SDK. See docs/design/published-sites-2026-10-14.md (PR B).
|
|
12
|
+
*
|
|
13
|
+
* Sites never send SQL: they call named, parameterized queries an owner
|
|
14
|
+
* defined (`site.sql.run(name, params)`). Every statement, whoever wrote it,
|
|
15
|
+
* passes `compileSiteSql` on the API and again in the Durable Object.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Statement checking
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
/** A value a statement parameter may take. Booleans bind as 1 and 0. */
|
|
23
|
+
export type SiteSqlValue = string | number | boolean | null;
|
|
24
|
+
|
|
25
|
+
export type SiteSqlStatementKind = "read" | "write" | "schema";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Who wrote the SQL, which decides what it may do:
|
|
29
|
+
* - `named`: a named query Site pages call. One read or write statement; no
|
|
30
|
+
* schema changes, PRAGMA, EXPLAIN or recursive CTEs (no RECURSIVE keyword,
|
|
31
|
+
* no CTE that refers to itself and no UNION/INTERSECT/EXCEPT inside a CTE).
|
|
32
|
+
* - `agent`: one read or write statement an owner's agent runs (query,
|
|
33
|
+
* execute), plus read-only PRAGMA and EXPLAIN. Schema changes go through
|
|
34
|
+
* migrations so the database can always be rebuilt from Postgres.
|
|
35
|
+
* - `script`: migrations and SQL imports. Many statements, run together,
|
|
36
|
+
* schema changes included.
|
|
37
|
+
*/
|
|
38
|
+
export type SiteSqlProfile = "named" | "agent" | "script";
|
|
39
|
+
|
|
40
|
+
export type SiteSqlProblem =
|
|
41
|
+
| "empty"
|
|
42
|
+
| "too_long"
|
|
43
|
+
| "syntax"
|
|
44
|
+
| "multiple_statements"
|
|
45
|
+
| "too_many_statements"
|
|
46
|
+
| "statement_not_allowed"
|
|
47
|
+
| "attach"
|
|
48
|
+
| "vacuum"
|
|
49
|
+
| "transaction"
|
|
50
|
+
| "pragma"
|
|
51
|
+
| "load_extension"
|
|
52
|
+
| "reserved_name"
|
|
53
|
+
| "parameter_style"
|
|
54
|
+
| "parameter_name"
|
|
55
|
+
| "too_many_parameters"
|
|
56
|
+
| "recursive";
|
|
57
|
+
|
|
58
|
+
export type SiteSqlStatement = {
|
|
59
|
+
/** The statement with every `:name` replaced by `?`, comments removed, no trailing semicolon. */
|
|
60
|
+
sql: string;
|
|
61
|
+
kind: SiteSqlStatementKind;
|
|
62
|
+
/** Parameter names in bind order (a name used twice appears twice). */
|
|
63
|
+
params: string[];
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export type SiteSqlCompileResult =
|
|
67
|
+
| {
|
|
68
|
+
ok: true;
|
|
69
|
+
statements: SiteSqlStatement[];
|
|
70
|
+
/** Distinct parameter names in first-use order. */
|
|
71
|
+
paramNames: string[];
|
|
72
|
+
}
|
|
73
|
+
| { ok: false; problem: SiteSqlProblem; message: string };
|
|
74
|
+
|
|
75
|
+
type ProfileRules = {
|
|
76
|
+
maxLength: number;
|
|
77
|
+
maxStatements: number;
|
|
78
|
+
kinds: readonly SiteSqlStatementKind[];
|
|
79
|
+
pragma: boolean;
|
|
80
|
+
explain: boolean;
|
|
81
|
+
recursive: boolean;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/** Longest SQL text per profile, in UTF-16 code units. */
|
|
85
|
+
export const SITE_SQL_MAX_NAMED_LENGTH = 16 * 1024;
|
|
86
|
+
export const SITE_SQL_MAX_AGENT_LENGTH = 64 * 1024;
|
|
87
|
+
export const SITE_SQL_MAX_SCRIPT_LENGTH = 2 * 1024 * 1024;
|
|
88
|
+
export const SITE_SQL_MAX_SCRIPT_STATEMENTS = 2_000;
|
|
89
|
+
export const SITE_SQL_MAX_PARAMETERS = 100;
|
|
90
|
+
|
|
91
|
+
const PROFILE_RULES: Readonly<Record<SiteSqlProfile, ProfileRules>> = Object.freeze({
|
|
92
|
+
named: {
|
|
93
|
+
maxLength: SITE_SQL_MAX_NAMED_LENGTH,
|
|
94
|
+
maxStatements: 1,
|
|
95
|
+
kinds: ["read", "write"],
|
|
96
|
+
pragma: false,
|
|
97
|
+
explain: false,
|
|
98
|
+
recursive: false,
|
|
99
|
+
},
|
|
100
|
+
agent: {
|
|
101
|
+
maxLength: SITE_SQL_MAX_AGENT_LENGTH,
|
|
102
|
+
maxStatements: 1,
|
|
103
|
+
kinds: ["read", "write"],
|
|
104
|
+
pragma: true,
|
|
105
|
+
explain: true,
|
|
106
|
+
recursive: true,
|
|
107
|
+
},
|
|
108
|
+
script: {
|
|
109
|
+
maxLength: SITE_SQL_MAX_SCRIPT_LENGTH,
|
|
110
|
+
maxStatements: SITE_SQL_MAX_SCRIPT_STATEMENTS,
|
|
111
|
+
kinds: ["read", "write", "schema"],
|
|
112
|
+
pragma: false,
|
|
113
|
+
explain: false,
|
|
114
|
+
recursive: true,
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
/** PRAGMAs that only read the schema. Anything else (and any `=` form) is refused. */
|
|
119
|
+
export const SITE_SQL_READ_PRAGMAS: readonly string[] = Object.freeze([
|
|
120
|
+
"table_info",
|
|
121
|
+
"table_xinfo",
|
|
122
|
+
"table_list",
|
|
123
|
+
"index_list",
|
|
124
|
+
"index_info",
|
|
125
|
+
"index_xinfo",
|
|
126
|
+
"foreign_key_list",
|
|
127
|
+
]);
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Identifier prefixes Site SQL may not name: Cloudflare's own tables
|
|
131
|
+
* (`_cf_`) and names kept for Opengeni (`_og_`). Quoted identifiers count,
|
|
132
|
+
* and so do string literals that start with one (SQLite accepts any string
|
|
133
|
+
* as a table name, as in `FROM '_cf_KV'`), so such a value must be passed as
|
|
134
|
+
* a parameter.
|
|
135
|
+
*/
|
|
136
|
+
export const SITE_SQL_RESERVED_PREFIXES: readonly string[] = Object.freeze(["_cf_", "_og_"]);
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Parameters the API binds from the verified viewer; a Site may never supply
|
|
140
|
+
* them. `:viewer` is the viewer's stable per-Site principal (`user:<id>`,
|
|
141
|
+
* `guest:<visitor>`, ...), `:viewer_kind` its principal kind (`user`,
|
|
142
|
+
* `guest`, ...), `:viewer_signed_in` 1 or 0, `:viewer_access` how the viewer
|
|
143
|
+
* reaches the Site (`member`, `invited`, `user` or `guest`; NULL for callers
|
|
144
|
+
* with no person behind them) and `:viewer_email` the viewer's verified email
|
|
145
|
+
* address in lowercase (NULL for guests and people without one).
|
|
146
|
+
*/
|
|
147
|
+
export const SITE_SQL_PRINCIPAL_PARAMS: readonly string[] = Object.freeze([
|
|
148
|
+
"viewer",
|
|
149
|
+
"viewer_kind",
|
|
150
|
+
"viewer_signed_in",
|
|
151
|
+
"viewer_access",
|
|
152
|
+
"viewer_email",
|
|
153
|
+
]);
|
|
154
|
+
|
|
155
|
+
/** `viewer` and every `viewer_*` name are reserved for the API. */
|
|
156
|
+
export function isSiteSqlPrincipalParam(name: string): boolean {
|
|
157
|
+
return name === "viewer" || name.startsWith("viewer_");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const PARAM_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,63}$/u;
|
|
161
|
+
|
|
162
|
+
type Token = {
|
|
163
|
+
kind:
|
|
164
|
+
| "word"
|
|
165
|
+
| "quoted"
|
|
166
|
+
| "string"
|
|
167
|
+
| "blob"
|
|
168
|
+
| "number"
|
|
169
|
+
| "param"
|
|
170
|
+
| "semi"
|
|
171
|
+
| "lparen"
|
|
172
|
+
| "rparen"
|
|
173
|
+
| "op"
|
|
174
|
+
| "space";
|
|
175
|
+
text: string;
|
|
176
|
+
/** Uppercase word, or the unquoted content of quoted identifiers and strings. */
|
|
177
|
+
value: string;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
class SiteSqlSyntaxError extends Error {}
|
|
181
|
+
|
|
182
|
+
const WORD_START = /[A-Za-z_\u0080-\uffff]/u;
|
|
183
|
+
const WORD_PART = /[A-Za-z0-9_$\u0080-\uffff]/u;
|
|
184
|
+
const NUMBER = /^(?:0[xX][0-9A-Fa-f]+|(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?)/u;
|
|
185
|
+
|
|
186
|
+
function readQuoted(sql: string, start: number, close: string): number {
|
|
187
|
+
let index = start + 1;
|
|
188
|
+
while (index < sql.length) {
|
|
189
|
+
if (sql[index] === close) {
|
|
190
|
+
if (close !== "]" && sql[index + 1] === close) {
|
|
191
|
+
index += 2;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
return index + 1;
|
|
195
|
+
}
|
|
196
|
+
index += 1;
|
|
197
|
+
}
|
|
198
|
+
throw new SiteSqlSyntaxError("Unterminated quote");
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function unquote(text: string): string {
|
|
202
|
+
const open = text[0]!;
|
|
203
|
+
const inner = text.slice(1, -1);
|
|
204
|
+
if (open === "[") return inner;
|
|
205
|
+
return inner.split(`${open}${open}`).join(open);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** SQLite-aware tokenizer: strings, quoted identifiers, blobs, comments and parameters. */
|
|
209
|
+
function tokenize(sql: string): Token[] {
|
|
210
|
+
const tokens: Token[] = [];
|
|
211
|
+
let index = 0;
|
|
212
|
+
const push = (kind: Token["kind"], end: number, value = "") => {
|
|
213
|
+
tokens.push({ kind, text: sql.slice(index, end), value });
|
|
214
|
+
index = end;
|
|
215
|
+
};
|
|
216
|
+
while (index < sql.length) {
|
|
217
|
+
const char = sql[index]!;
|
|
218
|
+
const next = sql[index + 1];
|
|
219
|
+
if (/\s/u.test(char)) {
|
|
220
|
+
let end = index + 1;
|
|
221
|
+
while (end < sql.length && /\s/u.test(sql[end]!)) end += 1;
|
|
222
|
+
push("space", end);
|
|
223
|
+
} else if (char === "-" && next === "-") {
|
|
224
|
+
const newline = sql.indexOf("\n", index);
|
|
225
|
+
// Comments become whitespace in the compiled statement.
|
|
226
|
+
tokens.push({ kind: "space", text: " ", value: "" });
|
|
227
|
+
index = newline < 0 ? sql.length : newline + 1;
|
|
228
|
+
} else if (char === "/" && next === "*") {
|
|
229
|
+
const close = sql.indexOf("*/", index + 2);
|
|
230
|
+
if (close < 0) throw new SiteSqlSyntaxError("Unterminated comment");
|
|
231
|
+
tokens.push({ kind: "space", text: " ", value: "" });
|
|
232
|
+
index = close + 2;
|
|
233
|
+
} else if (char === "'") {
|
|
234
|
+
const end = readQuoted(sql, index, "'");
|
|
235
|
+
push("string", end, unquote(sql.slice(index, end)));
|
|
236
|
+
} else if (char === '"' || char === "`") {
|
|
237
|
+
const end = readQuoted(sql, index, char);
|
|
238
|
+
push("quoted", end, unquote(sql.slice(index, end)));
|
|
239
|
+
} else if (char === "[") {
|
|
240
|
+
const end = readQuoted(sql, index, "]");
|
|
241
|
+
push("quoted", end, unquote(sql.slice(index, end)));
|
|
242
|
+
} else if ((char === "x" || char === "X") && next === "'") {
|
|
243
|
+
push("blob", readQuoted(sql, index + 1, "'"));
|
|
244
|
+
} else if (/\d/u.test(char) || (char === "." && /\d/u.test(next ?? ""))) {
|
|
245
|
+
const match = NUMBER.exec(sql.slice(index, index + 512));
|
|
246
|
+
push("number", index + (match ? match[0].length : 1));
|
|
247
|
+
} else if (WORD_START.test(char)) {
|
|
248
|
+
let end = index + 1;
|
|
249
|
+
while (end < sql.length && WORD_PART.test(sql[end]!)) end += 1;
|
|
250
|
+
push("word", end, sql.slice(index, end).toUpperCase());
|
|
251
|
+
} else if (
|
|
252
|
+
char === "?" ||
|
|
253
|
+
char === ":" ||
|
|
254
|
+
char === "@" ||
|
|
255
|
+
char === "$" ||
|
|
256
|
+
// SQLite reads `#name` as a parameter too.
|
|
257
|
+
(char === "#" && WORD_START.test(next ?? ""))
|
|
258
|
+
) {
|
|
259
|
+
let end = index + 1;
|
|
260
|
+
if (char === "?") while (end < sql.length && /\d/u.test(sql[end]!)) end += 1;
|
|
261
|
+
else while (end < sql.length && WORD_PART.test(sql[end]!)) end += 1;
|
|
262
|
+
if (char === ":" && end === index + 1) push("op", end);
|
|
263
|
+
else push("param", end, sql.slice(index, end));
|
|
264
|
+
} else if (char === ";") {
|
|
265
|
+
push("semi", index + 1);
|
|
266
|
+
} else if (char === "(") {
|
|
267
|
+
push("lparen", index + 1);
|
|
268
|
+
} else if (char === ")") {
|
|
269
|
+
push("rparen", index + 1);
|
|
270
|
+
} else {
|
|
271
|
+
push("op", index + 1);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return tokens;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function significant(tokens: Token[]): Token[] {
|
|
278
|
+
return tokens.filter((token) => token.kind !== "space");
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Split on top-level semicolons; a CREATE TRIGGER body (BEGIN ... END) keeps
|
|
283
|
+
* its own. Only the first BEGIN outside a CASE opens the body; inside it CASE
|
|
284
|
+
* and END nest, an unquoted BEGIN is refused (quote a column named begin),
|
|
285
|
+
* and the END that closes the body must be followed by `;` or the end of
|
|
286
|
+
* the SQL. That way a statement boundary is never in doubt: anything SQLite
|
|
287
|
+
* could read differently is refused rather than guessed.
|
|
288
|
+
*/
|
|
289
|
+
function splitStatements(tokens: Token[]): Token[][] {
|
|
290
|
+
const statements: Token[][] = [];
|
|
291
|
+
let current: Token[] = [];
|
|
292
|
+
let words: string[] = [];
|
|
293
|
+
let phase: "head" | "body" | "after" = "head";
|
|
294
|
+
let caseDepth = 0;
|
|
295
|
+
let depth = 0;
|
|
296
|
+
const isTrigger = () => {
|
|
297
|
+
const [first, second, third] = words;
|
|
298
|
+
return (
|
|
299
|
+
first === "CREATE" &&
|
|
300
|
+
(second === "TRIGGER" ||
|
|
301
|
+
((second === "TEMP" || second === "TEMPORARY") && third === "TRIGGER"))
|
|
302
|
+
);
|
|
303
|
+
};
|
|
304
|
+
for (const token of tokens) {
|
|
305
|
+
if (token.kind === "semi" && phase !== "body") {
|
|
306
|
+
statements.push(current);
|
|
307
|
+
current = [];
|
|
308
|
+
words = [];
|
|
309
|
+
phase = "head";
|
|
310
|
+
caseDepth = 0;
|
|
311
|
+
depth = 0;
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if (phase === "after" && token.kind !== "space")
|
|
315
|
+
throw new SiteSqlSyntaxError(
|
|
316
|
+
"A trigger's END must end the statement; quote identifiers named begin or end",
|
|
317
|
+
);
|
|
318
|
+
current.push(token);
|
|
319
|
+
if (token.kind !== "word") continue;
|
|
320
|
+
if (words.length < 3) words.push(token.value);
|
|
321
|
+
if (!isTrigger()) continue;
|
|
322
|
+
if (phase === "head") {
|
|
323
|
+
if (token.value === "CASE") caseDepth += 1;
|
|
324
|
+
else if (token.value === "END" && caseDepth > 0) caseDepth -= 1;
|
|
325
|
+
else if (token.value === "BEGIN" && caseDepth === 0) {
|
|
326
|
+
phase = "body";
|
|
327
|
+
depth = 1;
|
|
328
|
+
}
|
|
329
|
+
} else if (phase === "body") {
|
|
330
|
+
if (token.value === "BEGIN")
|
|
331
|
+
throw new SiteSqlSyntaxError(
|
|
332
|
+
"A trigger body cannot contain BEGIN; quote identifiers named begin or end",
|
|
333
|
+
);
|
|
334
|
+
if (token.value === "CASE") depth += 1;
|
|
335
|
+
else if (token.value === "END") {
|
|
336
|
+
depth -= 1;
|
|
337
|
+
if (depth === 0) phase = "after";
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
if (phase === "body") throw new SiteSqlSyntaxError("A trigger body is missing its END");
|
|
342
|
+
statements.push(current);
|
|
343
|
+
return statements.filter((statement) => significant(statement).length > 0);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const DML = new Set(["INSERT", "UPDATE", "DELETE", "REPLACE"]);
|
|
347
|
+
const COMPOUND = new Set(["UNION", "INTERSECT", "EXCEPT"]);
|
|
348
|
+
|
|
349
|
+
type Refusal = { problem: SiteSqlProblem; message: string };
|
|
350
|
+
|
|
351
|
+
type Cte = { name: string; body: Token[] };
|
|
352
|
+
|
|
353
|
+
function isName(token: Token | undefined): token is Token {
|
|
354
|
+
return token?.kind === "word" || token?.kind === "quoted" || token?.kind === "string";
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/** A name as SQLite compares it (ASCII case-insensitive; Unicode folding only refuses more). */
|
|
358
|
+
function nameKey(token: Token): string {
|
|
359
|
+
return (token.kind === "word" ? token.text : token.value).toUpperCase();
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/** Index of the `)` matching the `(` at `open`, or -1. */
|
|
363
|
+
function closingParen(sig: Token[], open: number): number {
|
|
364
|
+
let depth = 0;
|
|
365
|
+
for (let index = open; index < sig.length; index += 1) {
|
|
366
|
+
if (sig[index]!.kind === "lparen") depth += 1;
|
|
367
|
+
else if (sig[index]!.kind === "rparen") {
|
|
368
|
+
depth -= 1;
|
|
369
|
+
if (depth === 0) return index;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return -1;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* The common table expressions after the WITH at `at`:
|
|
377
|
+
* `WITH [RECURSIVE] name [(columns)] AS [NOT] [MATERIALIZED] (body), ...`,
|
|
378
|
+
* and the index of the statement keyword that follows them.
|
|
379
|
+
*/
|
|
380
|
+
function parseCtes(sig: Token[], at: number): { ctes: Cte[]; main: number } | Refusal {
|
|
381
|
+
const malformed: Refusal = { problem: "syntax", message: "Malformed WITH clause" };
|
|
382
|
+
const ctes: Cte[] = [];
|
|
383
|
+
let index = at + 1;
|
|
384
|
+
if (sig[index]?.kind === "word" && sig[index]!.value === "RECURSIVE") index += 1;
|
|
385
|
+
for (;;) {
|
|
386
|
+
const name = sig[index];
|
|
387
|
+
if (!isName(name)) return malformed;
|
|
388
|
+
index += 1;
|
|
389
|
+
if (sig[index]?.kind === "lparen") {
|
|
390
|
+
const close = closingParen(sig, index);
|
|
391
|
+
if (close < 0) return malformed;
|
|
392
|
+
index = close + 1;
|
|
393
|
+
}
|
|
394
|
+
if (sig[index]?.value !== "AS" || sig[index]?.kind !== "word") return malformed;
|
|
395
|
+
index += 1;
|
|
396
|
+
if (sig[index]?.kind === "word" && sig[index]!.value === "NOT") index += 1;
|
|
397
|
+
if (sig[index]?.kind === "word" && sig[index]!.value === "MATERIALIZED") index += 1;
|
|
398
|
+
if (sig[index]?.kind !== "lparen") return malformed;
|
|
399
|
+
const close = closingParen(sig, index);
|
|
400
|
+
if (close < 0) return malformed;
|
|
401
|
+
ctes.push({ name: nameKey(name), body: sig.slice(index + 1, close) });
|
|
402
|
+
index = close + 1;
|
|
403
|
+
if (sig[index]?.kind === "op" && sig[index]!.text === ",") {
|
|
404
|
+
index += 1;
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
return { ctes, main: index };
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Named queries may not recurse: besides the RECURSIVE keyword, refuse any
|
|
413
|
+
* CTE (at any nesting level) whose body names the CTE itself or combines
|
|
414
|
+
* selects, since SQLite runs `name AS (... UNION ALL ... FROM name)`
|
|
415
|
+
* recursively without the keyword.
|
|
416
|
+
*/
|
|
417
|
+
function refuseRecursion(sig: Token[]): Refusal | null {
|
|
418
|
+
for (let index = 0; index < sig.length; index += 1) {
|
|
419
|
+
if (sig[index]!.kind !== "word" || sig[index]!.value !== "WITH") continue;
|
|
420
|
+
const parsed = parseCtes(sig, index);
|
|
421
|
+
if ("problem" in parsed) return parsed;
|
|
422
|
+
for (const cte of parsed.ctes)
|
|
423
|
+
if (
|
|
424
|
+
cte.body.some(
|
|
425
|
+
(token) =>
|
|
426
|
+
(token.kind === "word" && COMPOUND.has(token.value)) ||
|
|
427
|
+
(isName(token) && nameKey(token) === cte.name),
|
|
428
|
+
)
|
|
429
|
+
)
|
|
430
|
+
return {
|
|
431
|
+
problem: "recursive",
|
|
432
|
+
message:
|
|
433
|
+
"Named queries cannot recurse: a WITH table may not refer to itself or use UNION, INTERSECT or EXCEPT",
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
return null;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/** The statement kind, or why it is refused. `at` indexes the first significant token. */
|
|
440
|
+
function classify(
|
|
441
|
+
sig: Token[],
|
|
442
|
+
rules: ProfileRules,
|
|
443
|
+
at = 0,
|
|
444
|
+
): { kind: SiteSqlStatementKind } | Refusal {
|
|
445
|
+
const first = sig[at];
|
|
446
|
+
if (!first || first.kind !== "word")
|
|
447
|
+
return { problem: "statement_not_allowed", message: "A statement must start with a keyword" };
|
|
448
|
+
switch (first.value) {
|
|
449
|
+
case "SELECT":
|
|
450
|
+
case "VALUES":
|
|
451
|
+
return { kind: "read" };
|
|
452
|
+
case "INSERT":
|
|
453
|
+
case "REPLACE":
|
|
454
|
+
case "UPDATE":
|
|
455
|
+
case "DELETE":
|
|
456
|
+
return { kind: "write" };
|
|
457
|
+
case "WITH": {
|
|
458
|
+
// Skip the `name AS (...)` definitions, then classify the statement itself.
|
|
459
|
+
const parsed = parseCtes(sig, at);
|
|
460
|
+
if ("problem" in parsed) return parsed;
|
|
461
|
+
const main = sig[parsed.main];
|
|
462
|
+
if (main?.kind === "word") {
|
|
463
|
+
if (main.value === "SELECT" || main.value === "VALUES") return { kind: "read" };
|
|
464
|
+
if (DML.has(main.value)) return { kind: "write" };
|
|
465
|
+
}
|
|
466
|
+
return {
|
|
467
|
+
problem: "syntax",
|
|
468
|
+
message: "A WITH clause must end in SELECT, VALUES, INSERT, REPLACE, UPDATE or DELETE",
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
case "CREATE":
|
|
472
|
+
case "DROP":
|
|
473
|
+
case "ALTER":
|
|
474
|
+
case "ANALYZE":
|
|
475
|
+
case "REINDEX":
|
|
476
|
+
return { kind: "schema" };
|
|
477
|
+
case "EXPLAIN": {
|
|
478
|
+
if (!rules.explain)
|
|
479
|
+
return { problem: "statement_not_allowed", message: "EXPLAIN is not allowed here" };
|
|
480
|
+
let inner = at + 1;
|
|
481
|
+
if (sig[inner]?.value === "QUERY" && sig[inner + 1]?.value === "PLAN") inner += 2;
|
|
482
|
+
const explained = classify(sig, rules, inner);
|
|
483
|
+
return "problem" in explained ? explained : { kind: "read" };
|
|
484
|
+
}
|
|
485
|
+
case "PRAGMA": {
|
|
486
|
+
if (!rules.pragma) return { problem: "pragma", message: "PRAGMA is not allowed here" };
|
|
487
|
+
const name = sig[at + 1];
|
|
488
|
+
const pragma = name?.kind === "word" || name?.kind === "quoted" ? name.value : "";
|
|
489
|
+
if (
|
|
490
|
+
!SITE_SQL_READ_PRAGMAS.includes(pragma.toLowerCase()) ||
|
|
491
|
+
sig.some((token) => token.kind === "op" && (token.text === "=" || token.text === "."))
|
|
492
|
+
)
|
|
493
|
+
return {
|
|
494
|
+
problem: "pragma",
|
|
495
|
+
message: `Only these read-only PRAGMAs are allowed: ${SITE_SQL_READ_PRAGMAS.join(", ")}`,
|
|
496
|
+
};
|
|
497
|
+
return { kind: "read" };
|
|
498
|
+
}
|
|
499
|
+
case "ATTACH":
|
|
500
|
+
case "DETACH":
|
|
501
|
+
return { problem: "attach", message: "ATTACH and DETACH are not allowed" };
|
|
502
|
+
case "VACUUM":
|
|
503
|
+
return { problem: "vacuum", message: "VACUUM is not allowed" };
|
|
504
|
+
case "BEGIN":
|
|
505
|
+
case "COMMIT":
|
|
506
|
+
case "END":
|
|
507
|
+
case "ROLLBACK":
|
|
508
|
+
case "SAVEPOINT":
|
|
509
|
+
case "RELEASE":
|
|
510
|
+
return {
|
|
511
|
+
problem: "transaction",
|
|
512
|
+
message: "Transaction statements are not allowed; each call already runs atomically",
|
|
513
|
+
};
|
|
514
|
+
default:
|
|
515
|
+
return {
|
|
516
|
+
problem: "statement_not_allowed",
|
|
517
|
+
message: `${first.value} statements are not allowed`,
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const KIND_LABEL: Record<SiteSqlStatementKind, string> = {
|
|
523
|
+
read: "read",
|
|
524
|
+
write: "write",
|
|
525
|
+
schema: "schema change",
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Check and compile Site SQL. Refuses ATTACH/DETACH, VACUUM, transaction
|
|
530
|
+
* control, PRAGMA writes, `pragma_*` functions outside the schema readers,
|
|
531
|
+
* load_extension, reserved table names, positional or `@`/`$`/`#`
|
|
532
|
+
* parameters and, per profile, more than one statement, schema changes and
|
|
533
|
+
* recursive CTEs. Parameters are `:name` only and are rewritten to `?` with
|
|
534
|
+
* their bind order, so values are always bound, never spliced.
|
|
535
|
+
*/
|
|
536
|
+
export function compileSiteSql(sql: string, profile: SiteSqlProfile): SiteSqlCompileResult {
|
|
537
|
+
const rules = PROFILE_RULES[profile];
|
|
538
|
+
const refuse = (problem: SiteSqlProblem, message: string): SiteSqlCompileResult => ({
|
|
539
|
+
ok: false,
|
|
540
|
+
problem,
|
|
541
|
+
message,
|
|
542
|
+
});
|
|
543
|
+
if (typeof sql !== "string") return refuse("empty", "Give a SQL statement");
|
|
544
|
+
if (sql.length > rules.maxLength)
|
|
545
|
+
return refuse("too_long", `SQL is limited to ${rules.maxLength} characters here`);
|
|
546
|
+
let statements: Token[][];
|
|
547
|
+
try {
|
|
548
|
+
statements = splitStatements(tokenize(sql));
|
|
549
|
+
} catch (error) {
|
|
550
|
+
return refuse("syntax", error instanceof Error ? error.message : "Invalid SQL");
|
|
551
|
+
}
|
|
552
|
+
if (statements.length === 0) return refuse("empty", "Give a SQL statement");
|
|
553
|
+
if (statements.length > rules.maxStatements)
|
|
554
|
+
return rules.maxStatements === 1
|
|
555
|
+
? refuse("multiple_statements", "Give exactly one statement")
|
|
556
|
+
: refuse("too_many_statements", `At most ${rules.maxStatements} statements`);
|
|
557
|
+
const compiled: SiteSqlStatement[] = [];
|
|
558
|
+
const paramNames: string[] = [];
|
|
559
|
+
for (const statement of statements) {
|
|
560
|
+
const sig = significant(statement);
|
|
561
|
+
for (const token of sig) {
|
|
562
|
+
if (token.kind === "string") {
|
|
563
|
+
// SQLite can read any string literal as a table name.
|
|
564
|
+
const value = token.value.toLowerCase();
|
|
565
|
+
if (SITE_SQL_RESERVED_PREFIXES.some((prefix) => value.startsWith(prefix)))
|
|
566
|
+
return refuse(
|
|
567
|
+
"reserved_name",
|
|
568
|
+
`Names starting with ${SITE_SQL_RESERVED_PREFIXES.join(" or ")} are reserved (pass such a value as a parameter)`,
|
|
569
|
+
);
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
if (token.kind !== "word" && token.kind !== "quoted") continue;
|
|
573
|
+
const name = (token.kind === "word" ? token.text : token.value).toLowerCase();
|
|
574
|
+
if (name === "load_extension")
|
|
575
|
+
return refuse("load_extension", "load_extension is not allowed");
|
|
576
|
+
if (SITE_SQL_RESERVED_PREFIXES.some((prefix) => name.startsWith(prefix)))
|
|
577
|
+
return refuse(
|
|
578
|
+
"reserved_name",
|
|
579
|
+
`Names starting with ${SITE_SQL_RESERVED_PREFIXES.join(" or ")} are reserved`,
|
|
580
|
+
);
|
|
581
|
+
// Table-valued PRAGMA functions: only the schema readers.
|
|
582
|
+
if (name.startsWith("pragma_") && !SITE_SQL_READ_PRAGMAS.includes(name.slice(7)))
|
|
583
|
+
return refuse(
|
|
584
|
+
"pragma",
|
|
585
|
+
`Only these pragma_ functions are allowed: ${SITE_SQL_READ_PRAGMAS.map((pragma) => `pragma_${pragma}`).join(", ")}`,
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
const classified = classify(sig, rules);
|
|
589
|
+
if ("problem" in classified) return refuse(classified.problem, classified.message);
|
|
590
|
+
if (!rules.kinds.includes(classified.kind)) {
|
|
591
|
+
if (profile === "agent" && classified.kind === "schema")
|
|
592
|
+
return refuse(
|
|
593
|
+
"statement_not_allowed",
|
|
594
|
+
"Change the schema with a migration (site_sql_migrate) so the database can be rebuilt",
|
|
595
|
+
);
|
|
596
|
+
return refuse(
|
|
597
|
+
"statement_not_allowed",
|
|
598
|
+
`A ${profile === "named" ? "named query" : "statement"} cannot be a ${KIND_LABEL[classified.kind]}`,
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
if (!rules.recursive) {
|
|
602
|
+
if (sig.some((token) => token.kind === "word" && token.value === "RECURSIVE"))
|
|
603
|
+
return refuse(
|
|
604
|
+
"recursive",
|
|
605
|
+
"Recursive queries are not allowed in named queries (quote an identifier named recursive)",
|
|
606
|
+
);
|
|
607
|
+
const recursion = refuseRecursion(sig);
|
|
608
|
+
if (recursion) return refuse(recursion.problem, recursion.message);
|
|
609
|
+
}
|
|
610
|
+
const params: string[] = [];
|
|
611
|
+
let text = "";
|
|
612
|
+
for (const token of statement) {
|
|
613
|
+
if (token.kind !== "param") {
|
|
614
|
+
text += token.text;
|
|
615
|
+
continue;
|
|
616
|
+
}
|
|
617
|
+
if (token.text[0] !== ":")
|
|
618
|
+
return refuse(
|
|
619
|
+
"parameter_style",
|
|
620
|
+
"Use named parameters like :name (not ?, @name, $name or #name)",
|
|
621
|
+
);
|
|
622
|
+
const name = token.text.slice(1);
|
|
623
|
+
if (!PARAM_NAME.test(name))
|
|
624
|
+
return refuse(
|
|
625
|
+
"parameter_name",
|
|
626
|
+
"Parameter names are a letter or _, then letters, digits or _ (up to 64)",
|
|
627
|
+
);
|
|
628
|
+
params.push(name);
|
|
629
|
+
if (!paramNames.includes(name)) paramNames.push(name);
|
|
630
|
+
text += "?";
|
|
631
|
+
}
|
|
632
|
+
if (params.length > SITE_SQL_MAX_PARAMETERS)
|
|
633
|
+
return refuse("too_many_parameters", `At most ${SITE_SQL_MAX_PARAMETERS} parameters`);
|
|
634
|
+
compiled.push({ sql: text.trim(), kind: classified.kind, params });
|
|
635
|
+
}
|
|
636
|
+
if (paramNames.length > SITE_SQL_MAX_PARAMETERS)
|
|
637
|
+
return refuse("too_many_parameters", `At most ${SITE_SQL_MAX_PARAMETERS} parameters`);
|
|
638
|
+
return { ok: true, statements: compiled, paramNames };
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/** Bind values for one compiled statement; a missing name binds null. Booleans become 1 or 0. */
|
|
642
|
+
export function siteSqlBindings(
|
|
643
|
+
statement: SiteSqlStatement,
|
|
644
|
+
params: Readonly<Record<string, SiteSqlValue | undefined>>,
|
|
645
|
+
): Array<string | number | null> {
|
|
646
|
+
return statement.params.map((name) => {
|
|
647
|
+
const value = Object.hasOwn(params, name) ? params[name] : null;
|
|
648
|
+
if (typeof value === "boolean") return value ? 1 : 0;
|
|
649
|
+
return value ?? null;
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/** A SQLite identifier quoted for splicing (table and column names only, never values). */
|
|
654
|
+
export function quoteSiteSqlIdentifier(name: string): string {
|
|
655
|
+
return `"${name.replaceAll('"', '""')}"`;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/** Table and column names Site SQL tools accept as arguments. */
|
|
659
|
+
export const SITE_SQL_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/u;
|
|
660
|
+
|
|
661
|
+
export function isSiteSqlTableName(name: string): boolean {
|
|
662
|
+
const lower = name.toLowerCase();
|
|
663
|
+
return (
|
|
664
|
+
SITE_SQL_IDENTIFIER.test(name) &&
|
|
665
|
+
!lower.startsWith("sqlite_") &&
|
|
666
|
+
!SITE_SQL_RESERVED_PREFIXES.some((prefix) => lower.startsWith(prefix))
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// ---------------------------------------------------------------------------
|
|
671
|
+
// Limits
|
|
672
|
+
// ---------------------------------------------------------------------------
|
|
673
|
+
|
|
674
|
+
/** Hard ceilings that hold whatever the profile or settings say. */
|
|
675
|
+
export const SITE_SQL_HARD_LIMITS = Object.freeze({
|
|
676
|
+
maxRowsPerCall: 5_000,
|
|
677
|
+
maxResponseBytes: 4 * 1024 * 1024,
|
|
678
|
+
storageBytesPerSite: 5 * 1024 * 1024 * 1024,
|
|
679
|
+
exportBytesPerCall: 4 * 1024 * 1024,
|
|
680
|
+
importRowsPerCall: 5_000,
|
|
681
|
+
/** UTF-8 bytes of SQL all of a Site's migrations hold together, so a rebuild fits in bounded calls. */
|
|
682
|
+
migrationBytesPerSite: 6 * 1024 * 1024,
|
|
683
|
+
/** Bytes one export call returns at most; follow nextCursor for the rest. */
|
|
684
|
+
exportBytesPerResponse: 16 * 1024 * 1024,
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
/** Response header of the HTTP export: the cursor of the next piece, when there is one. */
|
|
688
|
+
export const SITE_SQL_EXPORT_NEXT_CURSOR_HEADER = "X-OpenGeni-Export-Next-Cursor";
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* Effective Site SQL limits for one Site. Null means no Opengeni cap (the
|
|
692
|
+
* hard limits still hold).
|
|
693
|
+
*/
|
|
694
|
+
export type SiteSqlLimits = {
|
|
695
|
+
storageBytesPerSite: number;
|
|
696
|
+
rowsReadPerSitePerMonth: number | null;
|
|
697
|
+
rowsWrittenPerSitePerMonth: number | null;
|
|
698
|
+
callsPerMinutePerViewer: number | null;
|
|
699
|
+
callsPerMinutePerSite: number | null;
|
|
700
|
+
maxRowsPerCall: number;
|
|
701
|
+
maxResponseBytes: number;
|
|
702
|
+
};
|
|
703
|
+
|
|
704
|
+
// ---------------------------------------------------------------------------
|
|
705
|
+
// Definitions and requests
|
|
706
|
+
// ---------------------------------------------------------------------------
|
|
707
|
+
|
|
708
|
+
export const SITE_SQL_NAME = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/u;
|
|
709
|
+
|
|
710
|
+
const SqlName = primitives.string
|
|
711
|
+
.regex(SITE_SQL_NAME)
|
|
712
|
+
.describe("A letter, then letters, digits, - or _ (up to 64).");
|
|
713
|
+
|
|
714
|
+
export const SiteSqlValueSchema = /* @__PURE__ */ defineSchema(() =>
|
|
715
|
+
z.union([primitives.string.max(64 * 1024), z.number(), primitives.boolean, z.null()]),
|
|
716
|
+
);
|
|
717
|
+
|
|
718
|
+
export const SiteSqlParams = /* @__PURE__ */ defineSchema(() =>
|
|
719
|
+
z
|
|
720
|
+
.record(primitives.string.regex(PARAM_NAME), SiteSqlValueSchema)
|
|
721
|
+
.refine((value) => Object.keys(value).length <= SITE_SQL_MAX_PARAMETERS, {
|
|
722
|
+
message: `At most ${SITE_SQL_MAX_PARAMETERS} parameters`,
|
|
723
|
+
})
|
|
724
|
+
.describe("Parameter values by name (without the colon): strings, numbers, booleans or null."),
|
|
725
|
+
);
|
|
726
|
+
export type SiteSqlParams = z.infer<typeof SiteSqlParams>;
|
|
727
|
+
|
|
728
|
+
/**
|
|
729
|
+
* Who may run a named query from a Site page:
|
|
730
|
+
* - everyone: every viewer, including anonymous guests of public Sites;
|
|
731
|
+
* - signed_in: viewers signed in as a person (workspace members, invited
|
|
732
|
+
* people and, on public Sites, any signed-in user). Callers with no person
|
|
733
|
+
* behind them (service keys, agents and schedules acting on their own) are
|
|
734
|
+
* refused unless they manage the Site;
|
|
735
|
+
* - managers: only the Site's managers, through agent tools and the console's
|
|
736
|
+
* Data view; Site pages cannot run it.
|
|
737
|
+
*/
|
|
738
|
+
export const SiteSqlQueryAudience = /* @__PURE__ */ defineSchema(() =>
|
|
739
|
+
z.enum(["everyone", "signed_in", "managers"]),
|
|
740
|
+
);
|
|
741
|
+
export type SiteSqlQueryAudience = z.infer<typeof SiteSqlQueryAudience>;
|
|
742
|
+
|
|
743
|
+
/** site_sql_define_query and PUT .../sql/queries/:name. */
|
|
744
|
+
export const SiteSqlQueryDefinitionRequest = /* @__PURE__ */ defineSchema(() =>
|
|
745
|
+
z.object({
|
|
746
|
+
sql: primitives.nonEmptyString
|
|
747
|
+
.max(SITE_SQL_MAX_NAMED_LENGTH)
|
|
748
|
+
.describe(
|
|
749
|
+
"Exactly one SELECT, INSERT, UPDATE, DELETE or REPLACE statement (WITH allowed, not recursive). Use :name parameters; :viewer, :viewer_kind, :viewer_signed_in, :viewer_access (member, invited, user or guest) and :viewer_email (verified, lowercase, or NULL) are bound by Opengeni from the signed-in viewer.",
|
|
750
|
+
),
|
|
751
|
+
audience: SiteSqlQueryAudience.optional().describe(
|
|
752
|
+
"everyone (including anonymous guests of public Sites), signed_in (default: viewers signed in as a person) or managers.",
|
|
753
|
+
),
|
|
754
|
+
description: z.string().max(2000).optional().describe("What the query is for."),
|
|
755
|
+
maxRows: z
|
|
756
|
+
.number()
|
|
757
|
+
.int()
|
|
758
|
+
.min(1)
|
|
759
|
+
.max(SITE_SQL_HARD_LIMITS.maxRowsPerCall)
|
|
760
|
+
.optional()
|
|
761
|
+
.describe("Most rows one call returns (capped by the Site's limits)."),
|
|
762
|
+
}),
|
|
763
|
+
);
|
|
764
|
+
export type SiteSqlQueryDefinitionRequest = z.infer<typeof SiteSqlQueryDefinitionRequest>;
|
|
765
|
+
|
|
766
|
+
/** POST .../published-artifacts/current/sql/queries/:name (Site pages, `site.sql.run`). */
|
|
767
|
+
export const SiteSqlRunRequest = /* @__PURE__ */ defineSchema(() =>
|
|
768
|
+
z.object({ params: SiteSqlParams.optional() }),
|
|
769
|
+
);
|
|
770
|
+
export type SiteSqlRunRequest = z.infer<typeof SiteSqlRunRequest>;
|
|
771
|
+
|
|
772
|
+
/** POST .../sql/query and .../sql/execute: one ad hoc statement from a manager. */
|
|
773
|
+
export const SiteSqlStatementRequest = /* @__PURE__ */ defineSchema(() =>
|
|
774
|
+
z.object({
|
|
775
|
+
sql: primitives.nonEmptyString
|
|
776
|
+
.max(SITE_SQL_MAX_AGENT_LENGTH)
|
|
777
|
+
.describe("Exactly one SQLite statement; use :name parameters for values."),
|
|
778
|
+
params: SiteSqlParams.optional(),
|
|
779
|
+
maxRows: z
|
|
780
|
+
.number()
|
|
781
|
+
.int()
|
|
782
|
+
.min(1)
|
|
783
|
+
.max(SITE_SQL_HARD_LIMITS.maxRowsPerCall)
|
|
784
|
+
.optional()
|
|
785
|
+
.describe("Most rows to return (capped by the Site's limits)."),
|
|
786
|
+
}),
|
|
787
|
+
);
|
|
788
|
+
export type SiteSqlStatementRequest = z.infer<typeof SiteSqlStatementRequest>;
|
|
789
|
+
|
|
790
|
+
/** POST .../sql/reset. */
|
|
791
|
+
export const SiteSqlResetRequest = /* @__PURE__ */ defineSchema(() =>
|
|
792
|
+
z.object({
|
|
793
|
+
confirm: z.literal(true).describe("Must be true."),
|
|
794
|
+
schema: z
|
|
795
|
+
.enum(["keep", "drop"])
|
|
796
|
+
.optional()
|
|
797
|
+
.describe(
|
|
798
|
+
"keep (default): delete every row and rebuild the tables from the migrations. drop: delete the database and forget every migration and named query.",
|
|
799
|
+
),
|
|
800
|
+
}),
|
|
801
|
+
);
|
|
802
|
+
export type SiteSqlResetRequest = z.infer<typeof SiteSqlResetRequest>;
|
|
803
|
+
|
|
804
|
+
export const SiteSqlMigrationRequest = /* @__PURE__ */ defineSchema(() =>
|
|
805
|
+
z.object({
|
|
806
|
+
name: SqlName.describe("A unique migration name, such as create_votes."),
|
|
807
|
+
sql: primitives.nonEmptyString
|
|
808
|
+
.max(SITE_SQL_MAX_SCRIPT_LENGTH)
|
|
809
|
+
.describe("One or more statements (CREATE TABLE, CREATE INDEX, ALTER TABLE, INSERT ...)."),
|
|
810
|
+
}),
|
|
811
|
+
);
|
|
812
|
+
export type SiteSqlMigrationRequest = z.infer<typeof SiteSqlMigrationRequest>;
|
|
813
|
+
|
|
814
|
+
export const SiteSqlExportFormat = /* @__PURE__ */ defineSchema(() =>
|
|
815
|
+
z
|
|
816
|
+
.enum(["sql", "csv", "json"])
|
|
817
|
+
.describe("sql: schema and INSERT statements. csv: one table. json: rows per table."),
|
|
818
|
+
);
|
|
819
|
+
export type SiteSqlExportFormat = z.infer<typeof SiteSqlExportFormat>;
|
|
820
|
+
|
|
821
|
+
export const SiteSqlImportRequest = /* @__PURE__ */ defineSchema(() =>
|
|
822
|
+
z
|
|
823
|
+
.object({
|
|
824
|
+
format: SiteSqlExportFormat,
|
|
825
|
+
sql: primitives.string
|
|
826
|
+
.max(SITE_SQL_MAX_SCRIPT_LENGTH)
|
|
827
|
+
.optional()
|
|
828
|
+
.describe("sql format: statements to run together (schema and data)."),
|
|
829
|
+
table: primitives.string
|
|
830
|
+
.regex(SITE_SQL_IDENTIFIER)
|
|
831
|
+
.optional()
|
|
832
|
+
.describe("csv and json formats: the existing table to insert into."),
|
|
833
|
+
csv: primitives.string
|
|
834
|
+
.max(SITE_SQL_MAX_SCRIPT_LENGTH)
|
|
835
|
+
.optional()
|
|
836
|
+
.describe("csv format: a header row of column names, then one row per line."),
|
|
837
|
+
rows: z
|
|
838
|
+
.array(z.record(primitives.string, SiteSqlValueSchema))
|
|
839
|
+
.max(SITE_SQL_HARD_LIMITS.importRowsPerCall)
|
|
840
|
+
.optional()
|
|
841
|
+
.describe("json format: objects keyed by column name."),
|
|
842
|
+
onConflict: z
|
|
843
|
+
.enum(["fail", "replace", "ignore"])
|
|
844
|
+
.optional()
|
|
845
|
+
.describe("csv and json formats: what to do with a row that hits a unique key."),
|
|
846
|
+
})
|
|
847
|
+
.refine(
|
|
848
|
+
(value) =>
|
|
849
|
+
value.format === "sql"
|
|
850
|
+
? value.sql !== undefined
|
|
851
|
+
: value.table !== undefined &&
|
|
852
|
+
(value.format === "csv" ? value.csv !== undefined : value.rows !== undefined),
|
|
853
|
+
{ message: "Give sql for sql, or table plus csv or rows" },
|
|
854
|
+
),
|
|
855
|
+
);
|
|
856
|
+
export type SiteSqlImportRequest = z.infer<typeof SiteSqlImportRequest>;
|
|
857
|
+
|
|
858
|
+
/** PUT .../sql/settings: the owner turns Site SQL on or off for one Site. */
|
|
859
|
+
export const SiteSqlSettingsRequest = /* @__PURE__ */ defineSchema(() =>
|
|
860
|
+
z.object({ enabled: primitives.boolean }),
|
|
861
|
+
);
|
|
862
|
+
export type SiteSqlSettingsRequest = z.infer<typeof SiteSqlSettingsRequest>;
|
|
863
|
+
|
|
864
|
+
export const SiteSqlErrorCode = /* @__PURE__ */ defineSchema(() =>
|
|
865
|
+
z.enum([
|
|
866
|
+
"validation",
|
|
867
|
+
"not_found",
|
|
868
|
+
"forbidden",
|
|
869
|
+
"conflict",
|
|
870
|
+
"quota_exceeded",
|
|
871
|
+
"rate_limited",
|
|
872
|
+
"sql_error",
|
|
873
|
+
"unavailable",
|
|
874
|
+
"disabled",
|
|
875
|
+
]),
|
|
876
|
+
);
|
|
877
|
+
export type SiteSqlErrorCode = z.infer<typeof SiteSqlErrorCode>;
|
|
878
|
+
|
|
879
|
+
export const SITE_SQL_ERROR_STATUS: Readonly<Record<SiteSqlErrorCode, number>> = Object.freeze({
|
|
880
|
+
validation: 422,
|
|
881
|
+
not_found: 404,
|
|
882
|
+
forbidden: 403,
|
|
883
|
+
conflict: 409,
|
|
884
|
+
quota_exceeded: 413,
|
|
885
|
+
rate_limited: 429,
|
|
886
|
+
sql_error: 422,
|
|
887
|
+
unavailable: 503,
|
|
888
|
+
disabled: 409,
|
|
889
|
+
});
|
|
890
|
+
|
|
891
|
+
/** A row as a Site sees it. Blobs come back as `{ "$base64": "..." }`. */
|
|
892
|
+
export type SiteSqlRow = Record<string, unknown>;
|
|
893
|
+
|
|
894
|
+
export type SiteSqlRunResponse = {
|
|
895
|
+
columns: string[];
|
|
896
|
+
rows: SiteSqlRow[];
|
|
897
|
+
/** True when maxRows or the response size cut the result short. */
|
|
898
|
+
truncated: boolean;
|
|
899
|
+
rowsRead: number;
|
|
900
|
+
rowsWritten: number;
|
|
901
|
+
};
|
|
902
|
+
|
|
903
|
+
export type SiteSqlQueryResponse = {
|
|
904
|
+
name: string;
|
|
905
|
+
sql: string;
|
|
906
|
+
mode: "read" | "write";
|
|
907
|
+
params: string[];
|
|
908
|
+
audience: SiteSqlQueryAudience;
|
|
909
|
+
description: string | null;
|
|
910
|
+
maxRows: number | null;
|
|
911
|
+
createdAt: string;
|
|
912
|
+
updatedAt: string;
|
|
913
|
+
};
|
|
914
|
+
|
|
915
|
+
export type SiteSqlMigrationResponse = {
|
|
916
|
+
ordinal: number;
|
|
917
|
+
name: string;
|
|
918
|
+
sql: string;
|
|
919
|
+
checksum: string;
|
|
920
|
+
createdAt: string;
|
|
921
|
+
};
|
|
922
|
+
|
|
923
|
+
export type SiteSqlUsageResponse = {
|
|
924
|
+
storageBytes: number;
|
|
925
|
+
rowsReadThisMonth: number;
|
|
926
|
+
rowsWrittenThisMonth: number;
|
|
927
|
+
};
|
|
928
|
+
|
|
929
|
+
export type SiteSqlDescribeResponse = {
|
|
930
|
+
siteId: string;
|
|
931
|
+
/** Site SQL is configured on this deployment. */
|
|
932
|
+
available: boolean;
|
|
933
|
+
/** The owner turned Site SQL on for this Site. */
|
|
934
|
+
enabled: boolean;
|
|
935
|
+
migrations: SiteSqlMigrationResponse[];
|
|
936
|
+
queries: SiteSqlQueryResponse[];
|
|
937
|
+
usage: SiteSqlUsageResponse;
|
|
938
|
+
limits: SiteSqlLimits;
|
|
939
|
+
};
|
|
940
|
+
|
|
941
|
+
export type SiteSqlMigrateResponse = {
|
|
942
|
+
migration: SiteSqlMigrationResponse;
|
|
943
|
+
/** Migrations the database applied now (earlier ones it was missing, then this one). */
|
|
944
|
+
applied: string[];
|
|
945
|
+
};
|
|
946
|
+
|
|
947
|
+
export type SiteSqlImportResponse = {
|
|
948
|
+
/** Rows inserted (csv, json) or data statements run (sql). */
|
|
949
|
+
imported: number;
|
|
950
|
+
/** The migration a sql import recorded for its schema statements (before the data), if any. */
|
|
951
|
+
migration: string | null;
|
|
952
|
+
/**
|
|
953
|
+
* Every migration a sql import recorded, in order: its schema statements
|
|
954
|
+
* before the data, then its triggers after the data (so they do not fire
|
|
955
|
+
* on imported rows).
|
|
956
|
+
*/
|
|
957
|
+
migrations: string[];
|
|
958
|
+
rowsWritten: number;
|
|
959
|
+
};
|
|
960
|
+
|
|
961
|
+
export type SiteSqlResetResponse = { applied: string[]; droppedSchema: boolean };
|
|
962
|
+
|
|
963
|
+
// ---------------------------------------------------------------------------
|
|
964
|
+
// API -> gateway internal protocol
|
|
965
|
+
// ---------------------------------------------------------------------------
|
|
966
|
+
|
|
967
|
+
/**
|
|
968
|
+
* The gateway route the API calls for a Site's Durable Object:
|
|
969
|
+
* `POST <gateway internal origin>/_og/internal/site-db/<siteId>/<operation>`,
|
|
970
|
+
* signed like gateway -> API requests (`siteGatewayCanonicalString` over the
|
|
971
|
+
* internal path and the gateway's host, empty session, visitor, IP and human
|
|
972
|
+
* fields, the shared gateway secret, 30 s skew). The path prefix keeps these
|
|
973
|
+
* signatures distinct from any site-runtime request.
|
|
974
|
+
*/
|
|
975
|
+
export const SITE_DB_INTERNAL_PREFIX = "/_og/internal/site-db";
|
|
976
|
+
|
|
977
|
+
export const SITE_DB_OPERATIONS = Object.freeze([
|
|
978
|
+
"run",
|
|
979
|
+
"script",
|
|
980
|
+
"migrate",
|
|
981
|
+
"insert",
|
|
982
|
+
"export",
|
|
983
|
+
"inspect",
|
|
984
|
+
"reset",
|
|
985
|
+
] as const);
|
|
986
|
+
export type SiteDbOperation = (typeof SITE_DB_OPERATIONS)[number];
|
|
987
|
+
|
|
988
|
+
export function siteDbInternalPath(siteId: string, operation: SiteDbOperation): string {
|
|
989
|
+
return `${SITE_DB_INTERNAL_PREFIX}/${encodeURIComponent(siteId)}/${operation}`;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
/** Every internal request names a fresh id; the Durable Object refuses a replay within the skew window. */
|
|
993
|
+
type SiteDbBase = { requestId: string };
|
|
994
|
+
|
|
995
|
+
export type SiteDbRunRequest = SiteDbBase & {
|
|
996
|
+
sql: string;
|
|
997
|
+
profile: "named" | "agent";
|
|
998
|
+
params: Record<string, SiteSqlValue>;
|
|
999
|
+
/** The mode a named query was defined with; a read that writes is rolled back. */
|
|
1000
|
+
expect?: "read" | "write";
|
|
1001
|
+
maxRows: number;
|
|
1002
|
+
maxResponseBytes: number;
|
|
1003
|
+
maxDatabaseBytes: number;
|
|
1004
|
+
};
|
|
1005
|
+
|
|
1006
|
+
export type SiteDbScriptRequest = SiteDbBase & { sql: string; maxDatabaseBytes: number };
|
|
1007
|
+
|
|
1008
|
+
export type SiteDbMigrateRequest = SiteDbBase & {
|
|
1009
|
+
/**
|
|
1010
|
+
* Migrations to apply in order; ones already applied (same checksum) are
|
|
1011
|
+
* skipped. `position` is the migration's ordinal in Postgres, so the
|
|
1012
|
+
* database keeps the order when the API sends only the missing ones.
|
|
1013
|
+
* `checksum` is the lowercase hex SHA-256 of `sql` (UTF-8); the Durable
|
|
1014
|
+
* Object recomputes it and refuses a mismatch.
|
|
1015
|
+
*/
|
|
1016
|
+
migrations: Array<{ name: string; sql: string; checksum: string; position?: number }>;
|
|
1017
|
+
/**
|
|
1018
|
+
* Delete everything first, then apply these migrations (the first call of
|
|
1019
|
+
* a rebuild from Postgres; later calls send the rest without it).
|
|
1020
|
+
*/
|
|
1021
|
+
rebuild?: boolean;
|
|
1022
|
+
maxDatabaseBytes: number;
|
|
1023
|
+
};
|
|
1024
|
+
|
|
1025
|
+
/** Inspect; `migrationsOnly` skips the tables (and their row counts). */
|
|
1026
|
+
export type SiteDbInspectRequest = SiteDbBase & { migrationsOnly?: boolean };
|
|
1027
|
+
|
|
1028
|
+
export type SiteDbInsertRequest = SiteDbBase & {
|
|
1029
|
+
table: string;
|
|
1030
|
+
columns: string[];
|
|
1031
|
+
rows: SiteSqlValue[][];
|
|
1032
|
+
onConflict: "fail" | "replace" | "ignore";
|
|
1033
|
+
maxDatabaseBytes: number;
|
|
1034
|
+
};
|
|
1035
|
+
|
|
1036
|
+
export type SiteDbExportRequest = SiteDbBase & {
|
|
1037
|
+
/** Tables to export; all user tables when omitted. */
|
|
1038
|
+
tables?: string[];
|
|
1039
|
+
/** nextCursor from the previous page. */
|
|
1040
|
+
cursor?: string | null;
|
|
1041
|
+
maxRows: number;
|
|
1042
|
+
maxBytes: number;
|
|
1043
|
+
};
|
|
1044
|
+
|
|
1045
|
+
export type SiteDbMeter = {
|
|
1046
|
+
rowsRead: number;
|
|
1047
|
+
rowsWritten: number;
|
|
1048
|
+
/** Database size after the operation. */
|
|
1049
|
+
databaseBytes: number;
|
|
1050
|
+
};
|
|
1051
|
+
|
|
1052
|
+
export type SiteDbSchemaEntry = {
|
|
1053
|
+
type: "table" | "index" | "view" | "trigger";
|
|
1054
|
+
name: string;
|
|
1055
|
+
table: string;
|
|
1056
|
+
sql: string | null;
|
|
1057
|
+
};
|
|
1058
|
+
|
|
1059
|
+
export type SiteDbRunResponse = SiteDbMeter & {
|
|
1060
|
+
columns: string[];
|
|
1061
|
+
rows: SiteSqlRow[];
|
|
1062
|
+
truncated: boolean;
|
|
1063
|
+
};
|
|
1064
|
+
|
|
1065
|
+
export type SiteDbMigrateResponse = SiteDbMeter & { applied: string[] };
|
|
1066
|
+
|
|
1067
|
+
export type SiteDbExportResponse = SiteDbMeter & {
|
|
1068
|
+
/** The whole database schema, on every page. */
|
|
1069
|
+
schema: SiteDbSchemaEntry[];
|
|
1070
|
+
/** Row values in column order; blobs as `{ "$base64": "..." }`. */
|
|
1071
|
+
tables: Array<{ name: string; columns: string[]; rows: unknown[][] }>;
|
|
1072
|
+
nextCursor: string | null;
|
|
1073
|
+
};
|
|
1074
|
+
|
|
1075
|
+
export type SiteDbInspectResponse = SiteDbMeter & {
|
|
1076
|
+
tables: Array<{
|
|
1077
|
+
name: string;
|
|
1078
|
+
type: "table" | "view";
|
|
1079
|
+
sql: string | null;
|
|
1080
|
+
columns: Array<{
|
|
1081
|
+
name: string;
|
|
1082
|
+
type: string;
|
|
1083
|
+
notNull: boolean;
|
|
1084
|
+
primaryKey: boolean;
|
|
1085
|
+
defaultValue: string | null;
|
|
1086
|
+
}>;
|
|
1087
|
+
/** Rows, counted up to 100,000 (rowCountCapped says the count stopped there). */
|
|
1088
|
+
rowCount: number;
|
|
1089
|
+
rowCountCapped: boolean;
|
|
1090
|
+
}>;
|
|
1091
|
+
indexes: SiteDbSchemaEntry[];
|
|
1092
|
+
migrations: Array<{ name: string; checksum: string; appliedAt: string }>;
|
|
1093
|
+
};
|
|
1094
|
+
|
|
1095
|
+
/** GET .../sql/inspect: the database as the Durable Object sees it. */
|
|
1096
|
+
export type SiteSqlInspectResponse = Omit<SiteDbInspectResponse, "rowsRead" | "rowsWritten"> & {
|
|
1097
|
+
/** Migrations Postgres has that the database has not applied (migrate or reset to apply). */
|
|
1098
|
+
pendingMigrations: string[];
|
|
1099
|
+
};
|
|
1100
|
+
|
|
1101
|
+
export type SiteDbErrorCode =
|
|
1102
|
+
| "validation"
|
|
1103
|
+
| "sql_error"
|
|
1104
|
+
| "quota_exceeded"
|
|
1105
|
+
| "conflict"
|
|
1106
|
+
| "replayed"
|
|
1107
|
+
| "too_large"
|
|
1108
|
+
| "unauthorized"
|
|
1109
|
+
| "not_found"
|
|
1110
|
+
/** SQLite or Durable Object failures that are not the caller's (busy, I/O, corruption); 500. */
|
|
1111
|
+
| "internal";
|
|
1112
|
+
|
|
1113
|
+
export type SiteDbErrorBody = { error: { code: SiteDbErrorCode; message: string } };
|
|
1114
|
+
|
|
1115
|
+
/** Encode a SQLite blob for JSON. */
|
|
1116
|
+
export function siteSqlBlobValue(bytes: Uint8Array): { $base64: string } {
|
|
1117
|
+
let binary = "";
|
|
1118
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
1119
|
+
return { $base64: btoa(binary) };
|
|
1120
|
+
}
|