@anchrd/intel 0.59.0
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/README.md +496 -0
- package/bin/intel.mjs +134 -0
- package/dist/adapters/cloudflare/cloudflare-flow-workflow.d.ts +42 -0
- package/dist/adapters/cloudflare/cloudflare-flow-workflow.js +89 -0
- package/dist/adapters/cloudflare/cloudflare.d.ts +29 -0
- package/dist/adapters/cloudflare/cloudflare.js +416 -0
- package/dist/adapters/cloudflare/cloudflare.types.d.ts +31 -0
- package/dist/adapters/cloudflare/cloudflare.types.js +1 -0
- package/dist/adapters/content/content.d.ts +3 -0
- package/dist/adapters/content/content.js +128 -0
- package/dist/adapters/content/content.types.d.ts +38 -0
- package/dist/adapters/content/content.types.js +1 -0
- package/dist/adapters/db/db-audit.d.ts +62 -0
- package/dist/adapters/db/db-audit.js +150 -0
- package/dist/adapters/db/db-boards.d.ts +6 -0
- package/dist/adapters/db/db-boards.js +324 -0
- package/dist/adapters/db/db-feed.d.ts +30 -0
- package/dist/adapters/db/db-feed.js +276 -0
- package/dist/adapters/db/db-flows.d.ts +42 -0
- package/dist/adapters/db/db-flows.js +1311 -0
- package/dist/adapters/db/db-grants.d.ts +133 -0
- package/dist/adapters/db/db-grants.js +269 -0
- package/dist/adapters/db/db-indexing.d.ts +3 -0
- package/dist/adapters/db/db-indexing.js +200 -0
- package/dist/adapters/db/db-oauth.d.ts +3 -0
- package/dist/adapters/db/db-oauth.js +26 -0
- package/dist/adapters/db/db-prompts.d.ts +46 -0
- package/dist/adapters/db/db-prompts.js +144 -0
- package/dist/adapters/db/db.d.ts +6 -0
- package/dist/adapters/db/db.js +1687 -0
- package/dist/adapters/db/db.types.d.ts +16 -0
- package/dist/adapters/db/db.types.js +1 -0
- package/dist/adapters/document-converter/document-converter.d.ts +2 -0
- package/dist/adapters/document-converter/document-converter.js +26 -0
- package/dist/adapters/document-converter/document-converter.types.d.ts +17 -0
- package/dist/adapters/document-converter/document-converter.types.js +1 -0
- package/dist/adapters/flow-runtime/flow-runtime.d.ts +3 -0
- package/dist/adapters/flow-runtime/flow-runtime.js +22 -0
- package/dist/adapters/flow-runtime/flow-runtime.types.d.ts +19 -0
- package/dist/adapters/flow-runtime/flow-runtime.types.js +1 -0
- package/dist/adapters/index-queue/index-queue.d.ts +10 -0
- package/dist/adapters/index-queue/index-queue.js +12 -0
- package/dist/adapters/index-queue/index-queue.types.d.ts +11 -0
- package/dist/adapters/index-queue/index-queue.types.js +1 -0
- package/dist/adapters/json-schema/json-schema.d.ts +2 -0
- package/dist/adapters/json-schema/json-schema.js +77 -0
- package/dist/adapters/openid/openid.d.ts +5 -0
- package/dist/adapters/openid/openid.js +301 -0
- package/dist/adapters/portal-tokens/portal-tokens.d.ts +8 -0
- package/dist/adapters/portal-tokens/portal-tokens.js +79 -0
- package/dist/adapters/remote-tools/remote-tools.d.ts +5 -0
- package/dist/adapters/remote-tools/remote-tools.js +129 -0
- package/dist/adapters/semantic-index/semantic-index.d.ts +2 -0
- package/dist/adapters/semantic-index/semantic-index.js +139 -0
- package/dist/adapters/semantic-index/semantic-index.types.d.ts +56 -0
- package/dist/adapters/semantic-index/semantic-index.types.js +1 -0
- package/dist/adapters/session-cookie/session-cookie.d.ts +5 -0
- package/dist/adapters/session-cookie/session-cookie.js +87 -0
- package/dist/adapters/tool-source-policy/tool-source-policy.d.ts +1 -0
- package/dist/adapters/tool-source-policy/tool-source-policy.js +22 -0
- package/dist/audit/audit.d.ts +2 -0
- package/dist/audit/audit.js +87 -0
- package/dist/audit/audit.types.d.ts +23 -0
- package/dist/audit/audit.types.js +1 -0
- package/dist/auth/auth.d.ts +2 -0
- package/dist/auth/auth.js +284 -0
- package/dist/auth/auth.types.d.ts +108 -0
- package/dist/auth/auth.types.js +1 -0
- package/dist/boards/boards.d.ts +44 -0
- package/dist/boards/boards.js +447 -0
- package/dist/boards/boards.types.d.ts +117 -0
- package/dist/boards/boards.types.js +1 -0
- package/dist/build/build.d.ts +2 -0
- package/dist/build/build.js +192 -0
- package/dist/build/build.types.d.ts +17 -0
- package/dist/build/build.types.js +1 -0
- package/dist/bundle/bundle.d.ts +4 -0
- package/dist/bundle/bundle.js +1194 -0
- package/dist/bundle/bundle.types.d.ts +33 -0
- package/dist/bundle/bundle.types.js +1 -0
- package/dist/cli/cli.d.ts +4 -0
- package/dist/cli/cli.js +262 -0
- package/dist/cli/cli.types.d.ts +14 -0
- package/dist/cli/cli.types.js +1 -0
- package/dist/contract/audit.d.ts +50 -0
- package/dist/contract/audit.js +72 -0
- package/dist/contract/board.d.ts +150 -0
- package/dist/contract/board.js +262 -0
- package/dist/contract/bundle.d.ts +147 -0
- package/dist/contract/bundle.js +139 -0
- package/dist/contract/contract.d.ts +112 -0
- package/dist/contract/contract.js +143 -0
- package/dist/contract/feed.d.ts +126 -0
- package/dist/contract/feed.js +149 -0
- package/dist/contract/flow-run.d.ts +346 -0
- package/dist/contract/flow-run.js +194 -0
- package/dist/contract/flow.d.ts +1057 -0
- package/dist/contract/flow.js +552 -0
- package/dist/contract/node.d.ts +551 -0
- package/dist/contract/node.js +456 -0
- package/dist/contract/share.d.ts +216 -0
- package/dist/contract/share.js +134 -0
- package/dist/contract/table.d.ts +171 -0
- package/dist/contract/table.js +117 -0
- package/dist/contract/tool.d.ts +122 -0
- package/dist/contract/tool.js +172 -0
- package/dist/feed/feed.d.ts +2 -0
- package/dist/feed/feed.js +77 -0
- package/dist/feed/feed.types.d.ts +23 -0
- package/dist/feed/feed.types.js +1 -0
- package/dist/flows/flows.d.ts +41 -0
- package/dist/flows/flows.js +2216 -0
- package/dist/flows/flows.types.d.ts +339 -0
- package/dist/flows/flows.types.js +1 -0
- package/dist/http/http.d.ts +9 -0
- package/dist/http/http.js +874 -0
- package/dist/http/http.types.d.ts +22 -0
- package/dist/http/http.types.js +1 -0
- package/dist/indexing/indexing.d.ts +6 -0
- package/dist/indexing/indexing.js +239 -0
- package/dist/indexing/indexing.types.d.ts +11 -0
- package/dist/indexing/indexing.types.js +1 -0
- package/dist/intel/intel.d.ts +3 -0
- package/dist/intel/intel.js +194 -0
- package/dist/intel/intel.types.d.ts +24 -0
- package/dist/intel/intel.types.js +1 -0
- package/dist/mcp/mcp.d.ts +2 -0
- package/dist/mcp/mcp.js +1292 -0
- package/dist/mcp/mcp.types.d.ts +25 -0
- package/dist/mcp/mcp.types.js +1 -0
- package/dist/nodes/document-links/document-links.d.ts +20 -0
- package/dist/nodes/document-links/document-links.js +57 -0
- package/dist/nodes/nodes.d.ts +2 -0
- package/dist/nodes/nodes.js +1667 -0
- package/dist/nodes/nodes.types.d.ts +511 -0
- package/dist/nodes/nodes.types.js +1 -0
- package/dist/prepare/prepare.d.ts +4 -0
- package/dist/prepare/prepare.js +16 -0
- package/dist/prepare/prepare.types.d.ts +9 -0
- package/dist/prepare/prepare.types.js +1 -0
- package/dist/prompts/prompts.d.ts +2 -0
- package/dist/prompts/prompts.js +65 -0
- package/dist/prompts/prompts.types.d.ts +71 -0
- package/dist/prompts/prompts.types.js +1 -0
- package/dist/shared/callers/callers.d.ts +28 -0
- package/dist/shared/callers/callers.js +30 -0
- package/dist/shared/csv/csv.d.ts +13 -0
- package/dist/shared/csv/csv.js +85 -0
- package/dist/shared/document-text/document-text.d.ts +21 -0
- package/dist/shared/document-text/document-text.js +31 -0
- package/dist/shared/gate-authorization/gate-authorization.d.ts +5 -0
- package/dist/shared/gate-authorization/gate-authorization.js +26 -0
- package/dist/shared/grant-expiry/grant-expiry.d.ts +19 -0
- package/dist/shared/grant-expiry/grant-expiry.js +26 -0
- package/dist/shared/intel-error/intel-error.d.ts +22 -0
- package/dist/shared/intel-error/intel-error.js +27 -0
- package/dist/shared/plain-title/plain-title.d.ts +16 -0
- package/dist/shared/plain-title/plain-title.js +45 -0
- package/dist/shared/problem-details/problem-details.d.ts +30 -0
- package/dist/shared/problem-details/problem-details.js +18 -0
- package/dist/shared/report-unexpected-error/report-unexpected-error.d.ts +1 -0
- package/dist/shared/report-unexpected-error/report-unexpected-error.js +37 -0
- package/dist/shared/safe-return-path/safe-return-path.d.ts +2 -0
- package/dist/shared/safe-return-path/safe-return-path.js +11 -0
- package/dist/shared/sha256/sha256.d.ts +2 -0
- package/dist/shared/sha256/sha256.js +14 -0
- package/dist/tools/tool-servers/tool-servers.d.ts +46 -0
- package/dist/tools/tool-servers/tool-servers.js +177 -0
- package/dist/tools/tools.d.ts +2 -0
- package/dist/tools/tools.js +207 -0
- package/dist/tools/tools.types.d.ts +56 -0
- package/dist/tools/tools.types.js +1 -0
- package/examples/branding/favicon.svg +5 -0
- package/examples/branding/logo.svg +5 -0
- package/examples/branding/theme.css +16 -0
- package/examples/dev.vars.example +7 -0
- package/examples/intel.json +9 -0
- package/migrations/0000_intel_foundation.sql +203 -0
- package/migrations/0001_portal_native_tools.sql +21 -0
- package/migrations/0002_flows_in_the_knowledge_tree.sql +34 -0
- package/migrations/0003_folder_permissions.sql +211 -0
- package/migrations/0004_subflow_runs.sql +14 -0
- package/migrations/0005_flow_node_cleanup.sql +28 -0
- package/migrations/0005_tables_in_the_knowledge_tree.sql +68 -0
- package/migrations/0006_links_are_written_in_the_text.sql +20 -0
- package/migrations/0007_no_node_waits.sql +69 -0
- package/migrations/0008_three_layers.sql +130 -0
- package/migrations/0009_no_context_policy.sql +48 -0
- package/migrations/0010_no_run_waits_a_year.sql +27 -0
- package/migrations/0011_one_name_for_the_tree.sql +53 -0
- package/migrations/0012_table_snapshots.sql +29 -0
- package/migrations/0017_a_vector_per_card.sql +38 -0
- package/migrations/0018_no_context_policy_at_last.sql +97 -0
- package/migrations/0019_one_name_for_the_grants.sql +52 -0
- package/migrations/0020_cascade_purge_replay.sql +11 -0
- package/migrations/0021_a_flow_carries_its_own_grant.sql +44 -0
- package/migrations/0022_a_cursor_over_the_journal.sql +14 -0
- package/migrations/0023_a_board_and_its_tasks.sql +116 -0
- package/migrations/0024_the_archive_is_a_column.sql +33 -0
- package/migrations/0025_archiving_takes_the_subtree.sql +29 -0
- package/migrations/0026_one_feed_over_two_kinds.sql +23 -0
- package/migrations/0027_the_runs_of_every_flow.sql +27 -0
- package/migrations/0028_a_prompt_name_over_two_kinds.sql +70 -0
- package/package.json +118 -0
- package/src/contract/CLAUDE.md +152 -0
- package/src/contract/README.md +102 -0
- package/src/contract/audit.ts +85 -0
- package/src/contract/board.ts +306 -0
- package/src/contract/bundle.ts +157 -0
- package/src/contract/contract.ts +161 -0
- package/src/contract/feed.ts +165 -0
- package/src/contract/flow-run.ts +241 -0
- package/src/contract/flow.ts +675 -0
- package/src/contract/node.ts +583 -0
- package/src/contract/share.ts +196 -0
- package/src/contract/table.ts +178 -0
- package/src/contract/tool.ts +202 -0
- package/ui/README.md +110 -0
- package/ui/components.json +21 -0
- package/ui/index.html +13 -0
- package/ui/package.json +11 -0
- package/ui/scripts/lint-tokens.mjs +187 -0
- package/ui/src/access-summary/access-summary.tsx +385 -0
- package/ui/src/app/action-slot/action-slot.tsx +27 -0
- package/ui/src/app/app-sidebar/app-sidebar.tsx +71 -0
- package/ui/src/app/app-tree/app-tree.tsx +1175 -0
- package/ui/src/app/app.tsx +87 -0
- package/ui/src/app/header-search/header-search.tsx +294 -0
- package/ui/src/app/reindex-dialog/reindex-dialog.tsx +79 -0
- package/ui/src/app/settings-dialog/settings-dialog.tsx +135 -0
- package/ui/src/app/sidebar-preferences/sidebar-preferences.ts +68 -0
- package/ui/src/app/sidebar-preferences/sidebar-preferences.types.ts +15 -0
- package/ui/src/app/sidebar-resize-handle/sidebar-resize-handle.tsx +86 -0
- package/ui/src/app/tree-expansion/tree-expansion.ts +107 -0
- package/ui/src/app/tree-expansion/tree-expansion.types.ts +31 -0
- package/ui/src/app/tree-move/tree-move.tsx +427 -0
- package/ui/src/app/tree-reveal/tree-reveal.ts +64 -0
- package/ui/src/app/tree-reveal/tree-reveal.types.ts +16 -0
- package/ui/src/app/user-footer/user-footer.tsx +188 -0
- package/ui/src/app/view-menu/view-menu.tsx +115 -0
- package/ui/src/app-root/app-root.tsx +109 -0
- package/ui/src/app-root/app-root.types.ts +27 -0
- package/ui/src/archive/archive.tsx +417 -0
- package/ui/src/attachment-detail/attachment-detail.ts +52 -0
- package/ui/src/attachment-viewer/attachment-viewer.tsx +166 -0
- package/ui/src/blocknote-view/blocknote-view.tsx +30 -0
- package/ui/src/board/board-assignee/board-assignee-picker.tsx +132 -0
- package/ui/src/board/board-assignee/board-assignee.ts +48 -0
- package/ui/src/board/board-assignee/board-assignee.tsx +75 -0
- package/ui/src/board/board-chip/board-chip.tsx +46 -0
- package/ui/src/board/board-crumbs/board-crumbs.ts +56 -0
- package/ui/src/board/board-crumbs/board-crumbs.tsx +111 -0
- package/ui/src/board/board-data/board-data.ts +195 -0
- package/ui/src/board/board-data/board-data.types.ts +38 -0
- package/ui/src/board/board-dates/board-dates.ts +40 -0
- package/ui/src/board/board-dates/board-dates.tsx +105 -0
- package/ui/src/board/board-kanban/board-kanban.ts +162 -0
- package/ui/src/board/board-kanban/board-kanban.tsx +680 -0
- package/ui/src/board/board-open-task/board-open-task.ts +32 -0
- package/ui/src/board/board-panel/board-panel.tsx +259 -0
- package/ui/src/board/board-settings/board-settings.tsx +226 -0
- package/ui/src/board/board-status/board-status.tsx +60 -0
- package/ui/src/board/board-stripes/board-stripes.ts +128 -0
- package/ui/src/board/board-table/board-table.ts +53 -0
- package/ui/src/board/board-table/board-table.tsx +610 -0
- package/ui/src/board/board-task/board-task.ts +108 -0
- package/ui/src/board/board-task/board-task.tsx +335 -0
- package/ui/src/board/board-title-row/board-title-row.tsx +66 -0
- package/ui/src/branding/branding.tsx +17 -0
- package/ui/src/branding/custom-logo.ts +1 -0
- package/ui/src/branding/favicon.default.svg +5 -0
- package/ui/src/branding/favicon.svg +5 -0
- package/ui/src/components/ui/avatar.tsx +39 -0
- package/ui/src/components/ui/button.tsx +64 -0
- package/ui/src/components/ui/collapsible.tsx +20 -0
- package/ui/src/components/ui/command.tsx +160 -0
- package/ui/src/components/ui/dialog.tsx +143 -0
- package/ui/src/components/ui/dropdown-menu.tsx +187 -0
- package/ui/src/components/ui/input.tsx +21 -0
- package/ui/src/components/ui/popover.tsx +41 -0
- package/ui/src/components/ui/select.tsx +163 -0
- package/ui/src/components/ui/separator.tsx +26 -0
- package/ui/src/components/ui/sheet.tsx +136 -0
- package/ui/src/components/ui/sidebar.tsx +693 -0
- package/ui/src/components/ui/skeleton.tsx +13 -0
- package/ui/src/components/ui/switch.tsx +25 -0
- package/ui/src/components/ui/table.tsx +82 -0
- package/ui/src/components/ui/tabs.tsx +52 -0
- package/ui/src/components/ui/tooltip.tsx +51 -0
- package/ui/src/data/intel-data-provider/intel-data-provider.ts +685 -0
- package/ui/src/data/intel-data-provider/intel-data-provider.types.ts +266 -0
- package/ui/src/data/query-client.ts +18 -0
- package/ui/src/data/request-refusal/refusal-notice.tsx +36 -0
- package/ui/src/data/request-refusal/request-refusal.ts +51 -0
- package/ui/src/data/sign-in/sign-in.ts +97 -0
- package/ui/src/data/sign-in/sign-in.types.ts +15 -0
- package/ui/src/document-link/document-link.tsx +114 -0
- package/ui/src/document-markdown/document-markdown.ts +195 -0
- package/ui/src/editor-schema/editor-schema.ts +23 -0
- package/ui/src/entry-picker/entry-picker.tsx +238 -0
- package/ui/src/feed/feed.tsx +344 -0
- package/ui/src/file-preview/file-preview-view.tsx +496 -0
- package/ui/src/file-preview/file-preview.tsx +28 -0
- package/ui/src/file-preview/pdf-file-preview.tsx +5 -0
- package/ui/src/file-preview/presentation-file-preview.tsx +21 -0
- package/ui/src/file-preview/spreadsheet-file-preview.tsx +5 -0
- package/ui/src/file-preview/word-file-preview.tsx +5 -0
- package/ui/src/flow-runs/flow-runs.tsx +238 -0
- package/ui/src/flows/flows.tsx +1646 -0
- package/ui/src/flows/node-icon/node-icon.ts +34 -0
- package/ui/src/flows/node-palette/node-palette.tsx +214 -0
- package/ui/src/flows/node-palette/node-palette.types.ts +15 -0
- package/ui/src/folder-contents/folder-contents.tsx +321 -0
- package/ui/src/folder-index/folder-index.ts +55 -0
- package/ui/src/frontmatter/frontmatter.tsx +193 -0
- package/ui/src/graph-pane/graph-pane.tsx +53 -0
- package/ui/src/hooks/use-mobile.ts +19 -0
- package/ui/src/i18n/custom.json +4 -0
- package/ui/src/i18n/de.json +609 -0
- package/ui/src/i18n/en.json +609 -0
- package/ui/src/i18n/es.json +609 -0
- package/ui/src/i18n/i18n-context.tsx +65 -0
- package/ui/src/i18n/i18n-languages/i18n-languages.ts +83 -0
- package/ui/src/i18n/i18n.ts +32 -0
- package/ui/src/i18n/i18n.types.ts +49 -0
- package/ui/src/kind-icon.ts +97 -0
- package/ui/src/lib/utils.ts +6 -0
- package/ui/src/main.tsx +68 -0
- package/ui/src/modal/modal.tsx +53 -0
- package/ui/src/node-details/node-details.tsx +545 -0
- package/ui/src/node-editor/node-editor.tsx +304 -0
- package/ui/src/node-graph/graph-notice.tsx +21 -0
- package/ui/src/node-graph/node-graph.ts +214 -0
- package/ui/src/node-graph/node-graph.tsx +295 -0
- package/ui/src/node-import/node-import.tsx +418 -0
- package/ui/src/node-save-error.ts +46 -0
- package/ui/src/node-source/node-source.tsx +128 -0
- package/ui/src/node-table/node-table.tsx +107 -0
- package/ui/src/nodes/nodes.tsx +240 -0
- package/ui/src/rename-resource/rename-resource.ts +80 -0
- package/ui/src/resolved-names/resolved-names.tsx +67 -0
- package/ui/src/resource-error.ts +33 -0
- package/ui/src/resource-menu/resource-menu.tsx +1156 -0
- package/ui/src/router/router-context.ts +6 -0
- package/ui/src/router/router.tsx +67 -0
- package/ui/src/router/router.types.ts +8 -0
- package/ui/src/router/selection-search.ts +88 -0
- package/ui/src/save-button/save-button.tsx +94 -0
- package/ui/src/section-hint/section-hint.tsx +40 -0
- package/ui/src/sign-in-refused/sign-in-refused.tsx +35 -0
- package/ui/src/styles.css +189 -0
- package/ui/src/table-columns/table-columns.tsx +379 -0
- package/ui/src/theme/custom.css +1 -0
- package/ui/src/theme/theme-context.tsx +89 -0
- package/ui/src/theme/theme.ts +70 -0
- package/ui/src/time/relative-time.tsx +41 -0
- package/ui/src/time/time-context.tsx +78 -0
- package/ui/src/time/time.ts +165 -0
- package/ui/src/title-row/title-row.tsx +185 -0
- package/ui/src/tools/tools.tsx +546 -0
- package/ui/src/user-name/user-name.ts +61 -0
- package/ui/src/verbatim/verbatim.tsx +68 -0
- package/ui/tsconfig.json +17 -0
- package/ui/vite.config.ts +129 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { IntelError } from "../../shared/intel-error/intel-error.js";
|
|
2
|
+
import { reportUnexpectedError } from "../../shared/report-unexpected-error/report-unexpected-error.js";
|
|
3
|
+
import { hexDigest } from "../../shared/sha256/sha256.js";
|
|
4
|
+
// ⚠️ Both are workerd globals rather than imports, and they are read through a guard because a
|
|
5
|
+
// missing one is a deployment fact, not a bug in the caller: without them this adapter can still
|
|
6
|
+
// serve every other method, and the one that needs them says so instead of throwing a `TypeError`
|
|
7
|
+
// out of a stream nobody is watching.
|
|
8
|
+
function streaming() {
|
|
9
|
+
const fixedLength = globalThis
|
|
10
|
+
.FixedLengthStream;
|
|
11
|
+
const digestStream = crypto
|
|
12
|
+
.DigestStream;
|
|
13
|
+
if (!fixedLength || !digestStream) {
|
|
14
|
+
throw new IntelError(500, "streaming_unavailable", "This runtime cannot store a file without holding it in memory");
|
|
15
|
+
}
|
|
16
|
+
return { fixedLength, digestStream };
|
|
17
|
+
}
|
|
18
|
+
// The two halves of one pass over the bytes, named so that the promise of each can be held and
|
|
19
|
+
// answered separately from the other.
|
|
20
|
+
function hashed(content, hashing) {
|
|
21
|
+
return content.pipeTo(hashing.writable);
|
|
22
|
+
}
|
|
23
|
+
function written(hashing, body) {
|
|
24
|
+
return hashing.readable.pipeTo(body.writable);
|
|
25
|
+
}
|
|
26
|
+
export function createContentStore(bucket) {
|
|
27
|
+
return {
|
|
28
|
+
async get(key) {
|
|
29
|
+
return (await (await bucket.get(key))?.text()) ?? null;
|
|
30
|
+
},
|
|
31
|
+
async getBytes(key) {
|
|
32
|
+
return (await (await bucket.get(key))?.arrayBuffer()) ?? null;
|
|
33
|
+
},
|
|
34
|
+
async getStream(key) {
|
|
35
|
+
return (await bucket.get(key))?.body ?? null;
|
|
36
|
+
},
|
|
37
|
+
async put(key, content, mediaType) {
|
|
38
|
+
await bucket.put(key, content, { httpMetadata: { contentType: mediaType } });
|
|
39
|
+
},
|
|
40
|
+
async putBytes(key, content, mediaType) {
|
|
41
|
+
await bucket.put(key, content, { httpMetadata: { contentType: mediaType } });
|
|
42
|
+
},
|
|
43
|
+
/**
|
|
44
|
+
* The file into R2 and the hash out of it, in one pass over the bytes (#821).
|
|
45
|
+
*
|
|
46
|
+
* ⚠️ The `put` is started BEFORE the pipe is awaited, and the order is the whole of it: R2 pulls
|
|
47
|
+
* from `readable` while this writes into `writable`. Awaiting the write first would fill a
|
|
48
|
+
* stream nobody reads, and a 50 MB body would sit in memory — which is the state this method
|
|
49
|
+
* exists to prevent.
|
|
50
|
+
*
|
|
51
|
+
* ⚠️ Every chunk goes to the digest before it is passed on, so the hash covers exactly the bytes
|
|
52
|
+
* that were stored. Hashing a copy taken anywhere else would be a statement about a different
|
|
53
|
+
* stream.
|
|
54
|
+
*/
|
|
55
|
+
async putStream(key, content, mediaType, size) {
|
|
56
|
+
const { fixedLength, digestStream } = streaming();
|
|
57
|
+
const digest = new digestStream("SHA-256");
|
|
58
|
+
const writer = digest.getWriter();
|
|
59
|
+
let counted = 0;
|
|
60
|
+
const hashing = new TransformStream({
|
|
61
|
+
async transform(chunk, controller) {
|
|
62
|
+
counted += chunk.byteLength;
|
|
63
|
+
await writer.write(chunk);
|
|
64
|
+
controller.enqueue(chunk);
|
|
65
|
+
},
|
|
66
|
+
async flush() {
|
|
67
|
+
await writer.close();
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
const body = new fixedLength(size);
|
|
71
|
+
const stored = bucket.put(key, body.readable, { httpMetadata: { contentType: mediaType } });
|
|
72
|
+
// ⚠️ **Nothing here is ever awaited on its own, and that is the whole shape of this method.**
|
|
73
|
+
// Three promises are in flight and each of them can be the one that fails: the source, the
|
|
74
|
+
// write into the store's body, and the store itself. Awaiting one of them first means waiting
|
|
75
|
+
// forever for the other two whenever THAT one is not the one that broke — a store which
|
|
76
|
+
// refuses before it reads leaves the pipe with no reader, and a source which dies leaves the
|
|
77
|
+
// store with no bytes. Both were measured as a 30 s hang rather than a refusal.
|
|
78
|
+
const pipes = Promise.all([hashed(content, hashing), written(hashing, body)]);
|
|
79
|
+
const outcome = await Promise.race([
|
|
80
|
+
pipes.then(() => ({ side: "piped" }), (cause) => ({ side: "body", cause })),
|
|
81
|
+
stored.then(() => ({ side: "stored" }), (cause) => ({ side: "store", cause })),
|
|
82
|
+
]);
|
|
83
|
+
// The digest stream is closed by the transform's `flush` and by nothing else, so a run that
|
|
84
|
+
// ends any other way has to end it here or its `digest` never settles. Every promise gets a
|
|
85
|
+
// catch of its own for the same reason: a failure already answered must not surface a second
|
|
86
|
+
// time as an unhandled rejection.
|
|
87
|
+
const giveUp = (cause) => {
|
|
88
|
+
void writer.abort(cause).catch(() => undefined);
|
|
89
|
+
void digest.digest.catch(() => undefined);
|
|
90
|
+
void stored.catch(() => undefined);
|
|
91
|
+
void pipes.catch(() => undefined);
|
|
92
|
+
};
|
|
93
|
+
if (outcome.side === "body") {
|
|
94
|
+
giveUp(outcome.cause);
|
|
95
|
+
throw new IntelError(400, "upload_incomplete", "The upload ended before the body did");
|
|
96
|
+
}
|
|
97
|
+
if (outcome.side === "store") {
|
|
98
|
+
giveUp(outcome.cause);
|
|
99
|
+
// The reason goes to the log, the answer names only whose side failed: a store can quote
|
|
100
|
+
// what a peer sent, and that is not something a response may carry.
|
|
101
|
+
reportUnexpectedError(outcome.cause);
|
|
102
|
+
throw new IntelError(502, "content_store_failed", "The file could not be stored");
|
|
103
|
+
}
|
|
104
|
+
// ⚠️ The length is compared before the store is asked how it went. A body that simply stops
|
|
105
|
+
// carries fewer bytes without failing a single write — the `FixedLengthStream` notices only
|
|
106
|
+
// when the store reads it — so without this the caller's short body and a failure inside the
|
|
107
|
+
// store would arrive as one rejection and be answered with one sentence. They are opposite
|
|
108
|
+
// failures: one is the caller's, the other is ours, and a reader sent hunting through their
|
|
109
|
+
// own connection for our outage is what that costs.
|
|
110
|
+
if (counted !== size) {
|
|
111
|
+
giveUp(new Error("short body"));
|
|
112
|
+
throw new IntelError(400, "upload_incomplete", `The body carried ${counted} bytes, and ${size} were announced`);
|
|
113
|
+
}
|
|
114
|
+
let object;
|
|
115
|
+
try {
|
|
116
|
+
object = await stored;
|
|
117
|
+
}
|
|
118
|
+
catch (cause) {
|
|
119
|
+
reportUnexpectedError(cause);
|
|
120
|
+
throw new IntelError(502, "content_store_failed", "The file could not be stored");
|
|
121
|
+
}
|
|
122
|
+
return { size: object.size, hash: hexDigest(await digest.digest) };
|
|
123
|
+
},
|
|
124
|
+
async delete(key) {
|
|
125
|
+
await bucket.delete(key);
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface R2ObjectBody {
|
|
2
|
+
body: ReadableStream<Uint8Array>;
|
|
3
|
+
text(): Promise<string>;
|
|
4
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
5
|
+
}
|
|
6
|
+
export interface R2PutResult {
|
|
7
|
+
size: number;
|
|
8
|
+
}
|
|
9
|
+
export interface R2Bucket {
|
|
10
|
+
get(key: string): Promise<R2ObjectBody | null>;
|
|
11
|
+
put(key: string, value: string | ArrayBuffer | ReadableStream<Uint8Array>, options?: {
|
|
12
|
+
httpMetadata?: {
|
|
13
|
+
contentType?: string;
|
|
14
|
+
};
|
|
15
|
+
}): Promise<R2PutResult>;
|
|
16
|
+
delete(key: string): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The two workerd streams this adapter needs, declared rather than imported (#821).
|
|
20
|
+
*
|
|
21
|
+
* ⚠️ `FixedLengthStream` is why a stream can reach R2 at all: a `put` needs to know how long the
|
|
22
|
+
* body is, and everything piped through a `TransformStream` has lost that. It carries the length
|
|
23
|
+
* with it and fails the write when the bytes disagree — the same guard, one layer lower.
|
|
24
|
+
*
|
|
25
|
+
* ⚠️ `crypto.DigestStream` is the hash of a file nobody holds. `crypto.subtle.digest` wants the
|
|
26
|
+
* whole buffer, which is the memory this path exists to avoid.
|
|
27
|
+
*/
|
|
28
|
+
export interface FixedLengthStreamConstructor {
|
|
29
|
+
new (length: number): {
|
|
30
|
+
readable: ReadableStream<Uint8Array>;
|
|
31
|
+
writable: WritableStream<Uint8Array>;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export interface DigestStreamConstructor {
|
|
35
|
+
new (algorithm: string): WritableStream<Uint8Array> & {
|
|
36
|
+
digest: Promise<ArrayBuffer>;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { AuditRepository } from "../../audit/audit.types.js";
|
|
2
|
+
import type { D1Database } from "./db.types.js";
|
|
3
|
+
/**
|
|
4
|
+
* The journal, cut down to the rows this actor may see, continued from a stable position.
|
|
5
|
+
*
|
|
6
|
+
* ⚠️ **The visibility check is a JOIN against `allowed`, evaluated on every call.** `allowed` is
|
|
7
|
+
* the same tree walk `nodes` reads through (`db-grants.ts`), so an event about a node is visible
|
|
8
|
+
* exactly when the node is — no second rule that could drift from the first. It runs per row and
|
|
9
|
+
* per request; nothing here is cached, so a grant revoked a second ago is gone from the next
|
|
10
|
+
* answer.
|
|
11
|
+
*
|
|
12
|
+
* ⚠️ It filters the EXISTENCE of the row, not only its contents. That is deliberate and it is
|
|
13
|
+
* the stricter reading: that something is moving inside a customer's folder is itself
|
|
14
|
+
* information, so an actor who may not read the node learns nothing at all — not an id, not a
|
|
15
|
+
* count, not a timestamp.
|
|
16
|
+
*
|
|
17
|
+
* ⚠️ **An event about a purged node is therefore unreachable, for everybody.** `allowed` walks
|
|
18
|
+
* `nodes`, and after `node.purge` that row is gone, so the join finds nothing. This is not an
|
|
19
|
+
* oversight and it cannot be fixed without giving up the check: at a deleted row there is no
|
|
20
|
+
* longer anything against which a claim to have been allowed to read it could be tested. Hiding
|
|
21
|
+
* is the safe direction. A consumer learns about creation, change, moves and archiving — never
|
|
22
|
+
* about final deletion (#620).
|
|
23
|
+
*
|
|
24
|
+
* ⚠️ **`ORDER BY` and the cursor comparison must name BOTH columns, in the same order.** Two
|
|
25
|
+
* events in the same millisecond are the normal case in a batch, not the exception; on
|
|
26
|
+
* `occurred_at` alone the second one is skipped in silence — no error, no log, just an event
|
|
27
|
+
* that never arrived. The tuple comparison below is written out by hand because SQLite has no
|
|
28
|
+
* row-value comparison here: "later timestamp, OR same timestamp and larger id".
|
|
29
|
+
*/
|
|
30
|
+
export declare const auditFeedQuery: string;
|
|
31
|
+
/**
|
|
32
|
+
* The same journal about FLOWS (#774).
|
|
33
|
+
*
|
|
34
|
+
* ⚠️ **A separate statement rather than a widened one, and that is the decision.** The three kinds
|
|
35
|
+
* do not differ in a value but in the QUESTION they ask: a node is visible when the node is in
|
|
36
|
+
* `allowed`, a flow when its folder is or it carries a grant of its own, a run one join further on.
|
|
37
|
+
* Written as one query with three branches, every caller would pay all three joins to read one
|
|
38
|
+
* kind — and `audit_list` names exactly one kind per call, so there is nothing to gain from it.
|
|
39
|
+
* The feed is the opposite case and rightly written the opposite way: it reads all three at once.
|
|
40
|
+
*
|
|
41
|
+
* ⚠️ **`INNER JOIN flows`, so an event about a purged flow is unreachable** — the same shape the
|
|
42
|
+
* node half has carried since #620, and for the same reason: at a deleted row there is nothing left
|
|
43
|
+
* against which a claim to have been allowed to read it could be tested. Hiding is the safe
|
|
44
|
+
* direction. The feed differs deliberately (it falls back on the purge's own metadata) because a
|
|
45
|
+
* card that says "deleted" is the whole point there; a journal consumer gets the deletion through
|
|
46
|
+
* the parent folder's events instead.
|
|
47
|
+
*/
|
|
48
|
+
export declare const auditFlowFeedQuery: string;
|
|
49
|
+
/**
|
|
50
|
+
* And about RUNS, which is the walk one join further: run → flow → folder (#774).
|
|
51
|
+
*
|
|
52
|
+
* ⚠️ The alias is `run_flow` and it is handed to `flowInSubtreeOver` explicitly. Until #767 that
|
|
53
|
+
* guard had `flow` hard-wired, and against a flow reached through a second join every branch but
|
|
54
|
+
* the folder one compared NULL — a flow somebody OWNS but that hangs at the top level answered "not
|
|
55
|
+
* visible" to its own owner. It fails in the safe direction, which is exactly why it needs a test
|
|
56
|
+
* that seeds a run of a flow at the ROOT rather than in a folder.
|
|
57
|
+
*/
|
|
58
|
+
export declare const auditRunFeedQuery: string;
|
|
59
|
+
export declare function createAuditRepository(deps: {
|
|
60
|
+
db: D1Database;
|
|
61
|
+
now(): Date;
|
|
62
|
+
}): AuditRepository;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { flowInSubtree, flowInSubtreeBindings, flowInSubtreeOver, subtreeBindings, subtreeCte, } from "./db-grants.js";
|
|
2
|
+
function mapEvent(row, resourceType) {
|
|
3
|
+
let metadata = {};
|
|
4
|
+
// The column is `NOT NULL DEFAULT '{}'`, but it is written by 11 call sites over
|
|
5
|
+
// `JSON.stringify` on whatever each one had at hand. A row that cannot be parsed, or that parses
|
|
6
|
+
// to something other than an object, must not take the whole page down with it: the reader would
|
|
7
|
+
// then be stuck at that cursor forever with no way past. An unreadable payload becomes `{}` and
|
|
8
|
+
// the event itself still arrives — the fact that something happened is the part a change feed
|
|
9
|
+
// cannot afford to lose.
|
|
10
|
+
try {
|
|
11
|
+
const parsed = JSON.parse(row.metadata_json);
|
|
12
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
13
|
+
metadata = parsed;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
metadata = {};
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
id: row.id,
|
|
21
|
+
actorId: row.actor_id,
|
|
22
|
+
action: row.action,
|
|
23
|
+
// ⚠️ Taken from the QUESTION, not from the row. Each statement names exactly one
|
|
24
|
+
// `resource_type` in its `WHERE`, so the answer cannot carry another — and reading the column
|
|
25
|
+
// back would add a second truth about the same thing (#774).
|
|
26
|
+
resourceType,
|
|
27
|
+
resourceId: row.resource_id,
|
|
28
|
+
metadata,
|
|
29
|
+
occurredAt: row.occurred_at,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The journal, cut down to the rows this actor may see, continued from a stable position.
|
|
34
|
+
*
|
|
35
|
+
* ⚠️ **The visibility check is a JOIN against `allowed`, evaluated on every call.** `allowed` is
|
|
36
|
+
* the same tree walk `nodes` reads through (`db-grants.ts`), so an event about a node is visible
|
|
37
|
+
* exactly when the node is — no second rule that could drift from the first. It runs per row and
|
|
38
|
+
* per request; nothing here is cached, so a grant revoked a second ago is gone from the next
|
|
39
|
+
* answer.
|
|
40
|
+
*
|
|
41
|
+
* ⚠️ It filters the EXISTENCE of the row, not only its contents. That is deliberate and it is
|
|
42
|
+
* the stricter reading: that something is moving inside a customer's folder is itself
|
|
43
|
+
* information, so an actor who may not read the node learns nothing at all — not an id, not a
|
|
44
|
+
* count, not a timestamp.
|
|
45
|
+
*
|
|
46
|
+
* ⚠️ **An event about a purged node is therefore unreachable, for everybody.** `allowed` walks
|
|
47
|
+
* `nodes`, and after `node.purge` that row is gone, so the join finds nothing. This is not an
|
|
48
|
+
* oversight and it cannot be fixed without giving up the check: at a deleted row there is no
|
|
49
|
+
* longer anything against which a claim to have been allowed to read it could be tested. Hiding
|
|
50
|
+
* is the safe direction. A consumer learns about creation, change, moves and archiving — never
|
|
51
|
+
* about final deletion (#620).
|
|
52
|
+
*
|
|
53
|
+
* ⚠️ **`ORDER BY` and the cursor comparison must name BOTH columns, in the same order.** Two
|
|
54
|
+
* events in the same millisecond are the normal case in a batch, not the exception; on
|
|
55
|
+
* `occurred_at` alone the second one is skipped in silence — no error, no log, just an event
|
|
56
|
+
* that never arrived. The tuple comparison below is written out by hand because SQLite has no
|
|
57
|
+
* row-value comparison here: "later timestamp, OR same timestamp and larger id".
|
|
58
|
+
*/
|
|
59
|
+
export const auditFeedQuery = `${subtreeCte}
|
|
60
|
+
SELECT e.id, e.actor_id, e.action, e.resource_id, e.metadata_json, e.occurred_at
|
|
61
|
+
FROM audit_events e
|
|
62
|
+
JOIN allowed ON allowed.id = e.resource_id
|
|
63
|
+
WHERE e.resource_type = 'node'
|
|
64
|
+
AND (
|
|
65
|
+
? IS NULL
|
|
66
|
+
OR e.occurred_at > ?
|
|
67
|
+
OR (e.occurred_at = ? AND e.id > ?)
|
|
68
|
+
)
|
|
69
|
+
ORDER BY e.occurred_at ASC, e.id ASC
|
|
70
|
+
LIMIT ?`;
|
|
71
|
+
/**
|
|
72
|
+
* The same journal about FLOWS (#774).
|
|
73
|
+
*
|
|
74
|
+
* ⚠️ **A separate statement rather than a widened one, and that is the decision.** The three kinds
|
|
75
|
+
* do not differ in a value but in the QUESTION they ask: a node is visible when the node is in
|
|
76
|
+
* `allowed`, a flow when its folder is or it carries a grant of its own, a run one join further on.
|
|
77
|
+
* Written as one query with three branches, every caller would pay all three joins to read one
|
|
78
|
+
* kind — and `audit_list` names exactly one kind per call, so there is nothing to gain from it.
|
|
79
|
+
* The feed is the opposite case and rightly written the opposite way: it reads all three at once.
|
|
80
|
+
*
|
|
81
|
+
* ⚠️ **`INNER JOIN flows`, so an event about a purged flow is unreachable** — the same shape the
|
|
82
|
+
* node half has carried since #620, and for the same reason: at a deleted row there is nothing left
|
|
83
|
+
* against which a claim to have been allowed to read it could be tested. Hiding is the safe
|
|
84
|
+
* direction. The feed differs deliberately (it falls back on the purge's own metadata) because a
|
|
85
|
+
* card that says "deleted" is the whole point there; a journal consumer gets the deletion through
|
|
86
|
+
* the parent folder's events instead.
|
|
87
|
+
*/
|
|
88
|
+
export const auditFlowFeedQuery = `${subtreeCte}
|
|
89
|
+
SELECT e.id, e.actor_id, e.action, e.resource_id, e.metadata_json, e.occurred_at
|
|
90
|
+
FROM audit_events e
|
|
91
|
+
JOIN flows flow ON flow.id = e.resource_id
|
|
92
|
+
WHERE e.resource_type = 'flow'
|
|
93
|
+
AND ${flowInSubtree}
|
|
94
|
+
AND (
|
|
95
|
+
? IS NULL
|
|
96
|
+
OR e.occurred_at > ?
|
|
97
|
+
OR (e.occurred_at = ? AND e.id > ?)
|
|
98
|
+
)
|
|
99
|
+
ORDER BY e.occurred_at ASC, e.id ASC
|
|
100
|
+
LIMIT ?`;
|
|
101
|
+
/**
|
|
102
|
+
* And about RUNS, which is the walk one join further: run → flow → folder (#774).
|
|
103
|
+
*
|
|
104
|
+
* ⚠️ The alias is `run_flow` and it is handed to `flowInSubtreeOver` explicitly. Until #767 that
|
|
105
|
+
* guard had `flow` hard-wired, and against a flow reached through a second join every branch but
|
|
106
|
+
* the folder one compared NULL — a flow somebody OWNS but that hangs at the top level answered "not
|
|
107
|
+
* visible" to its own owner. It fails in the safe direction, which is exactly why it needs a test
|
|
108
|
+
* that seeds a run of a flow at the ROOT rather than in a folder.
|
|
109
|
+
*/
|
|
110
|
+
export const auditRunFeedQuery = `${subtreeCte}
|
|
111
|
+
SELECT e.id, e.actor_id, e.action, e.resource_id, e.metadata_json, e.occurred_at
|
|
112
|
+
FROM audit_events e
|
|
113
|
+
JOIN flow_runs run ON run.id = e.resource_id
|
|
114
|
+
JOIN flows run_flow ON run_flow.id = run.flow_id
|
|
115
|
+
WHERE e.resource_type = 'flow-run'
|
|
116
|
+
AND ${flowInSubtreeOver("run_flow.parent_id", "run_flow")}
|
|
117
|
+
AND (
|
|
118
|
+
? IS NULL
|
|
119
|
+
OR e.occurred_at > ?
|
|
120
|
+
OR (e.occurred_at = ? AND e.id > ?)
|
|
121
|
+
)
|
|
122
|
+
ORDER BY e.occurred_at ASC, e.id ASC
|
|
123
|
+
LIMIT ?`;
|
|
124
|
+
export function createAuditRepository(deps) {
|
|
125
|
+
return {
|
|
126
|
+
async listEvents(actor, query) {
|
|
127
|
+
const after = query.after ?? null;
|
|
128
|
+
const now = deps.now().toISOString();
|
|
129
|
+
// ⚠️ One reading of the clock for the whole call. Both guards measure grant expiry against
|
|
130
|
+
// it, and two `now()` readings could straddle the moment a grant runs out.
|
|
131
|
+
const guard = query.resourceType === "node"
|
|
132
|
+
? { page: auditFeedQuery, bindings: subtreeBindings(actor, "read", now) }
|
|
133
|
+
: {
|
|
134
|
+
page: query.resourceType === "flow" ? auditFlowFeedQuery : auditRunFeedQuery,
|
|
135
|
+
// The flow walks need the CTE's bindings and the guard's, in that order — the guard
|
|
136
|
+
// is written into the statement after the CTE.
|
|
137
|
+
bindings: [
|
|
138
|
+
...subtreeBindings(actor, "read", now),
|
|
139
|
+
...flowInSubtreeBindings(actor, "read", now),
|
|
140
|
+
],
|
|
141
|
+
};
|
|
142
|
+
const result = await deps.db
|
|
143
|
+
.prepare(guard.page)
|
|
144
|
+
.bind(...guard.bindings, after === null ? null : after.occurredAt, after === null ? null : after.occurredAt, after === null ? null : after.occurredAt, after === null ? null : after.id, query.limit)
|
|
145
|
+
.all();
|
|
146
|
+
const rows = result.results ?? [];
|
|
147
|
+
return { events: rows.map((row) => mapEvent(row, query.resourceType)) };
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|