@fungi.computer/oyster 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +1134 -2
- package/dist/agents.d.ts +114 -0
- package/dist/agents.js +322 -0
- package/dist/app-builds.d.ts +210 -0
- package/dist/app-builds.js +499 -0
- package/dist/app-sandbox.d.ts +2 -0
- package/dist/app-sandbox.js +2 -0
- package/dist/app-sources.d.ts +31 -0
- package/dist/app-sources.js +370 -0
- package/dist/app-storage.d.ts +141 -0
- package/dist/app-storage.js +281 -0
- package/dist/apps/gate.d.ts +105 -0
- package/dist/apps/gate.js +273 -0
- package/dist/apps/index.d.ts +5 -0
- package/dist/apps/index.js +5 -0
- package/dist/apps/installation.d.ts +248 -0
- package/dist/apps/installation.js +307 -0
- package/dist/apps/operations.d.ts +8 -0
- package/dist/apps/operations.js +98 -0
- package/dist/apps/tools.d.ts +33 -0
- package/dist/apps/tools.js +17 -0
- package/dist/auth.d.ts +11 -0
- package/dist/auth.js +93 -0
- package/dist/backend-request.d.ts +3 -0
- package/dist/backend-request.js +53 -0
- package/dist/build/artifact-writer.d.ts +40 -0
- package/dist/build/artifact-writer.js +181 -0
- package/dist/build/build-jobs.d.ts +128 -0
- package/dist/build/build-jobs.js +203 -0
- package/dist/build/build-policy.d.ts +10 -0
- package/dist/build/build-policy.js +10 -0
- package/dist/build/build-step.d.ts +37 -0
- package/dist/build/build-step.js +28 -0
- package/dist/build/bundled-html.d.ts +7 -0
- package/dist/build/bundled-html.js +64 -0
- package/dist/build/candidate-builder.d.ts +68 -0
- package/dist/build/candidate-builder.js +91 -0
- package/dist/build/candidate-cli.d.ts +1 -0
- package/dist/build/candidate-cli.js +119 -0
- package/dist/build/candidate-cli.mjs +28170 -0
- package/dist/build/content-objects.d.ts +37 -0
- package/dist/build/content-objects.js +53 -0
- package/dist/build/definition-emission.d.ts +34 -0
- package/dist/build/definition-emission.js +201 -0
- package/dist/build/effect.d.ts +249 -0
- package/dist/build/effect.js +436 -0
- package/dist/build/git-environment.d.ts +9 -0
- package/dist/build/git-environment.js +60 -0
- package/dist/build/html-recipe.d.ts +20 -0
- package/dist/build/html-recipe.js +51 -0
- package/dist/build/local-build.d.ts +67 -0
- package/dist/build/local-build.js +1128 -0
- package/dist/build/snapshot-package-store.d.ts +12 -0
- package/dist/build/snapshot-package-store.js +36 -0
- package/dist/build/snapshot-program-scripts.d.ts +6 -0
- package/dist/build/snapshot-program-scripts.js +2 -0
- package/dist/build/snapshot-programs/snapshot-path.d.ts +2 -0
- package/dist/build/snapshot-programs/snapshot-path.js +6 -0
- package/dist/build/submission-contract.d.ts +828 -0
- package/dist/build/submission-contract.js +180 -0
- package/dist/build/workspace-build.d.ts +55 -0
- package/dist/build/workspace-build.js +200 -0
- package/dist/build-contract.d.ts +7 -0
- package/dist/build-contract.js +7 -0
- package/dist/build-egress.d.ts +62 -0
- package/dist/build-egress.js +148 -0
- package/dist/build-jobs.d.ts +2 -0
- package/dist/build-jobs.js +2 -0
- package/dist/build.d.ts +12 -0
- package/dist/build.js +14 -0
- package/dist/capability-descriptor.d.ts +46 -0
- package/dist/capability-descriptor.js +51 -0
- package/dist/catalog-frame.d.ts +39 -0
- package/dist/catalog-frame.js +72 -0
- package/dist/cause-class.d.ts +19 -0
- package/dist/cause-class.js +54 -0
- package/dist/cloudflare.d.ts +123 -0
- package/dist/cloudflare.js +391 -0
- package/dist/computer-terminal.d.ts +38 -0
- package/dist/computer-terminal.js +410 -0
- package/dist/computer.d.ts +60 -0
- package/dist/computer.js +33 -0
- package/dist/event-recipients.d.ts +57 -0
- package/dist/event-recipients.js +144 -0
- package/dist/facet-invocation.d.ts +4 -0
- package/dist/facet-invocation.js +45 -0
- package/dist/files.d.ts +42 -0
- package/dist/files.js +123 -0
- package/dist/frame-binding-wire-script.d.ts +2 -0
- package/dist/frame-binding-wire-script.js +1 -0
- package/dist/frame-binding-wire.d.ts +28 -0
- package/dist/frame-binding-wire.js +54 -0
- package/dist/git-admission.d.ts +52 -0
- package/dist/git-admission.js +188 -0
- package/dist/git-auth.d.ts +11 -0
- package/dist/git-auth.js +27 -0
- package/dist/git-door.d.ts +35 -0
- package/dist/git-door.js +108 -0
- package/dist/git-journal.d.ts +31 -0
- package/dist/git-journal.js +188 -0
- package/dist/git.d.ts +31 -0
- package/dist/git.js +64 -0
- package/dist/guest-policy.d.ts +9 -0
- package/dist/guest-policy.js +13 -0
- package/dist/host.d.ts +244 -0
- package/dist/host.js +1043 -0
- package/dist/index.d.ts +166 -0
- package/dist/index.js +320 -0
- package/dist/installation-consent.d.ts +79 -0
- package/dist/installation-consent.js +82 -0
- package/dist/internal/background-origins.d.ts +3 -0
- package/dist/internal/background-origins.js +8 -0
- package/dist/internal/delivery.d.ts +12 -0
- package/dist/internal/delivery.js +167 -0
- package/dist/internal/host-gate.d.ts +14 -0
- package/dist/internal/host-gate.js +62 -0
- package/dist/internal/registry-installations.d.ts +6 -0
- package/dist/internal/registry-installations.js +12 -0
- package/dist/invocation.d.ts +251 -0
- package/dist/invocation.js +377 -0
- package/dist/keys.d.ts +48 -0
- package/dist/keys.js +52 -0
- package/dist/mcp-owner.d.ts +27 -0
- package/dist/mcp-owner.js +155 -0
- package/dist/mcp.d.ts +89 -0
- package/dist/mcp.js +429 -0
- package/dist/media.d.ts +23 -0
- package/dist/media.js +12 -0
- package/dist/pack.d.ts +79 -0
- package/dist/pack.js +151 -0
- package/dist/page/capabilities.d.ts +23 -0
- package/dist/page/capabilities.js +256 -0
- package/dist/page/computer-handles.d.ts +79 -0
- package/dist/page/computer-handles.js +44 -0
- package/dist/page/gill-observation.d.ts +4 -0
- package/dist/page/gill-observation.js +31 -0
- package/dist/page/lifetime.d.ts +6 -0
- package/dist/page/lifetime.js +19 -0
- package/dist/page/mount.d.ts +163 -0
- package/dist/page/mount.js +390 -0
- package/dist/page/storage.d.ts +3 -0
- package/dist/page/storage.js +82 -0
- package/dist/page/terminal.d.ts +5 -0
- package/dist/page/terminal.js +7 -0
- package/dist/page/window-launch.d.ts +18 -0
- package/dist/page/window-launch.js +11 -0
- package/dist/page-script.d.ts +14 -0
- package/dist/page-script.js +5 -0
- package/dist/r2-files.d.ts +3 -0
- package/dist/r2-files.js +79 -0
- package/dist/release.d.ts +83 -0
- package/dist/release.js +167 -0
- package/dist/root-data.d.ts +86 -0
- package/dist/root-data.js +69 -0
- package/dist/shell/app-directory.d.ts +542 -0
- package/dist/shell/app-directory.js +199 -0
- package/dist/shell/browser-actions.d.ts +7 -0
- package/dist/shell/browser-actions.js +56 -0
- package/dist/shell/command-host.d.ts +112 -0
- package/dist/shell/command-host.js +502 -0
- package/dist/shell/commands.d.ts +25 -0
- package/dist/shell/commands.js +78 -0
- package/dist/shell/index.d.ts +8 -0
- package/dist/shell/index.js +8 -0
- package/dist/shell/keys.d.ts +41 -0
- package/dist/shell/keys.js +134 -0
- package/dist/shell/shell-module.d.ts +55 -0
- package/dist/shell/shell-module.js +86 -0
- package/dist/shell/window-manager.d.ts +124 -0
- package/dist/shell/window-manager.js +711 -0
- package/dist/shell/workbench-contract.d.ts +68 -0
- package/dist/shell/workbench-contract.js +1 -0
- package/dist/shell/workbench-directory.d.ts +332 -0
- package/dist/shell/workbench-directory.js +35 -0
- package/dist/shell/workbench-host-types.d.ts +96 -0
- package/dist/shell/workbench-host-types.js +1 -0
- package/dist/shell/workbench-host.d.ts +34 -0
- package/dist/shell/workbench-host.js +347 -0
- package/dist/shell/workbench-model.d.ts +192 -0
- package/dist/shell/workbench-model.js +53 -0
- package/dist/shell/workbench.d.ts +4 -0
- package/dist/shell/workbench.js +2 -0
- package/dist/source/fork.d.ts +27 -0
- package/dist/source/fork.js +46 -0
- package/dist/source/index.d.ts +10 -0
- package/dist/source/index.js +10 -0
- package/dist/source/seed-receipt.d.ts +53 -0
- package/dist/source/seed-receipt.js +18 -0
- package/dist/source/team-source.d.ts +32 -0
- package/dist/source/team-source.js +42 -0
- package/dist/source/template-push.d.ts +33 -0
- package/dist/source/template-push.js +223 -0
- package/dist/source/template-repository.d.ts +4 -0
- package/dist/source/template-repository.js +21 -0
- package/dist/source/template-seed.d.ts +68 -0
- package/dist/source/template-seed.js +80 -0
- package/dist/standalone/agent-directory.d.ts +4 -0
- package/dist/standalone/agent-directory.js +4 -0
- package/dist/standalone/agents.d.ts +220 -0
- package/dist/standalone/agents.js +795 -0
- package/dist/standalone/app-builds-binding.d.ts +36 -0
- package/dist/standalone/app-builds-binding.js +119 -0
- package/dist/standalone/app-set.d.ts +91 -0
- package/dist/standalone/app-set.js +395 -0
- package/dist/standalone/app-source.d.ts +41 -0
- package/dist/standalone/app-source.js +59 -0
- package/dist/standalone/builds.d.ts +18 -0
- package/dist/standalone/builds.js +101 -0
- package/dist/standalone/computer-egress.d.ts +37 -0
- package/dist/standalone/computer-egress.js +111 -0
- package/dist/standalone/computer.d.ts +122 -0
- package/dist/standalone/computer.js +714 -0
- package/dist/standalone/install-apps-cli.d.ts +2 -0
- package/dist/standalone/install-apps-cli.js +126 -0
- package/dist/standalone/instance.d.ts +35 -0
- package/dist/standalone/instance.js +74 -0
- package/dist/standalone/media.d.ts +76 -0
- package/dist/standalone/media.js +227 -0
- package/dist/standalone/preview.d.ts +51 -0
- package/dist/standalone/preview.js +167 -0
- package/dist/standalone/registry.d.ts +102 -0
- package/dist/standalone/registry.js +193 -0
- package/dist/standalone/tenancy.d.ts +42 -0
- package/dist/standalone/tenancy.js +241 -0
- package/dist/standalone/worker.d.ts +55 -0
- package/dist/standalone/worker.js +147 -0
- package/dist/standalone/workflow-authority-readers.d.ts +16 -0
- package/dist/standalone/workflow-authority-readers.js +35 -0
- package/dist/standalone/workflow-binding.d.ts +216 -0
- package/dist/standalone/workflow-binding.js +217 -0
- package/dist/standalone/workflow-events.d.ts +46 -0
- package/dist/standalone/workflow-events.js +39 -0
- package/dist/standalone/workflow-git-capture.d.ts +7 -0
- package/dist/standalone/workflow-git-capture.js +62 -0
- package/dist/standalone/workflow-host.d.ts +931 -0
- package/dist/standalone/workflow-host.js +751 -0
- package/dist/standalone/workflow-node-capture.d.ts +13 -0
- package/dist/standalone/workflow-node-capture.js +72 -0
- package/dist/standalone/workflow-root-authority.d.ts +27 -0
- package/dist/standalone/workflow-root-authority.js +87 -0
- package/dist/standalone/workflow-schedules.d.ts +52 -0
- package/dist/standalone/workflow-schedules.js +40 -0
- package/dist/standalone/workflow-trigger-host.d.ts +1694 -0
- package/dist/standalone/workflow-trigger-host.js +52 -0
- package/dist/standalone/workflows.d.ts +657 -0
- package/dist/standalone/workflows.js +554 -0
- package/dist/storage-frame.d.ts +64 -0
- package/dist/storage-frame.js +28 -0
- package/dist/tenancy/auth0.d.ts +64 -0
- package/dist/tenancy/auth0.js +198 -0
- package/dist/tenancy/cloudflare/rate-limit.d.ts +8 -0
- package/dist/tenancy/cloudflare/rate-limit.js +19 -0
- package/dist/tenancy/cloudflare/registry.d.ts +50 -0
- package/dist/tenancy/cloudflare/registry.js +301 -0
- package/dist/tenancy/cloudflare.d.ts +2 -0
- package/dist/tenancy/cloudflare.js +2 -0
- package/dist/tenancy/cold-directory.d.ts +13 -0
- package/dist/tenancy/cold-directory.js +43 -0
- package/dist/tenancy/contracts.d.ts +195 -0
- package/dist/tenancy/contracts.js +1 -0
- package/dist/tenancy/index.d.ts +19 -0
- package/dist/tenancy/index.js +156 -0
- package/dist/tenancy/owner-key.d.ts +26 -0
- package/dist/tenancy/owner-key.js +63 -0
- package/dist/tenancy/oyster.d.ts +4 -0
- package/dist/tenancy/oyster.js +25 -0
- package/dist/tenancy/registry-record.d.ts +7 -0
- package/dist/tenancy/registry-record.js +44 -0
- package/dist/tenancy/registry.d.ts +27 -0
- package/dist/tenancy/registry.js +138 -0
- package/dist/tenancy/resolve.d.ts +7 -0
- package/dist/tenancy/resolve.js +64 -0
- package/dist/tenant.d.ts +81 -0
- package/dist/tenant.js +71 -0
- package/dist/testing/capabilities.d.ts +29 -0
- package/dist/testing/capabilities.js +130 -0
- package/dist/testing/catalog.d.ts +3 -0
- package/dist/testing/catalog.js +53 -0
- package/dist/testing/index.d.ts +10 -0
- package/dist/testing/index.js +10 -0
- package/dist/testing/installation.d.ts +11 -0
- package/dist/testing/installation.js +25 -0
- package/dist/testing/native-worker.d.ts +21 -0
- package/dist/testing/native-worker.js +106 -0
- package/dist/testing/worker.d.ts +18 -0
- package/dist/testing/worker.js +67 -0
- package/dist/testing/workers-runtime.d.ts +24 -0
- package/dist/testing/workers-runtime.js +33 -0
- package/dist/webhook-delivery.d.ts +14 -0
- package/dist/webhook-delivery.js +17 -0
- package/dist/workers-git.d.ts +285 -0
- package/dist/workers-git.js +1174 -0
- package/dist/workers.d.ts +352 -0
- package/dist/workers.js +868 -0
- package/docs/api/README.md +82 -0
- package/docs/api/agents.md +332 -0
- package/docs/api/app-storage.md +209 -0
- package/docs/api/apps.md +1375 -0
- package/docs/api/auth.md +63 -0
- package/docs/api/build-contract.md +1304 -0
- package/docs/api/build.md +1712 -0
- package/docs/api/cloudflare.md +421 -0
- package/docs/api/computer.md +203 -0
- package/docs/api/files.md +117 -0
- package/docs/api/git.md +470 -0
- package/docs/api/host.md +1328 -0
- package/docs/api/index.md +430 -0
- package/docs/api/invocation.md +738 -0
- package/docs/api/keys.md +175 -0
- package/docs/api/mcp.md +301 -0
- package/docs/api/media.md +40 -0
- package/docs/api/modules.md +50 -0
- package/docs/api/pack.md +141 -0
- package/docs/api/page/mount.md +298 -0
- package/docs/api/root-data.md +111 -0
- package/docs/api/shell/app-directory.md +599 -0
- package/docs/api/shell/command-host.md +224 -0
- package/docs/api/shell/commands.md +197 -0
- package/docs/api/shell/keys.md +183 -0
- package/docs/api/shell/shell-module.md +204 -0
- package/docs/api/shell/window-manager.md +267 -0
- package/docs/api/shell/workbench-directory.md +119 -0
- package/docs/api/shell/workbench.md +205 -0
- package/docs/api/shell.md +1915 -0
- package/docs/api/tenancy/auth0.md +110 -0
- package/docs/api/tenancy/cloudflare.md +221 -0
- package/docs/api/tenancy/cold-directory.md +46 -0
- package/docs/api/tenancy/owner-key.md +63 -0
- package/docs/api/tenancy/registry.md +77 -0
- package/docs/api/tenancy/resolve.md +69 -0
- package/docs/api/tenancy.md +430 -0
- package/docs/api/tenant.md +194 -0
- package/docs/api/workers-git.md +1646 -0
- package/docs/api/workers.md +1691 -0
- package/docs/site/agent-quickstart.mdx +346 -0
- package/examples/standalone/worker.ts +7 -0
- package/examples/standalone/wrangler.jsonc +82 -0
- package/package.json +300 -3
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host recipients of App events: durable, per-recipient delivery records the
|
|
3
|
+
* host commits when it accepts an emit, and drains after the emitter's write
|
|
4
|
+
* has returned. A recipient that is down, slow or refusing never reaches the
|
|
5
|
+
* emitter; its outcome is recorded on its own row as pending, delivered or
|
|
6
|
+
* failed, and retried on the owner's alarm.
|
|
7
|
+
*/
|
|
8
|
+
import * as Schema from "effect/Schema";
|
|
9
|
+
import { failureDiagnostic } from "./cause-class.js";
|
|
10
|
+
/**
|
|
11
|
+
* Retained D5 baseline: a ten-second attempt bound, and a terminal failure
|
|
12
|
+
* after six failed attempts (the first plus one per retry delay).
|
|
13
|
+
*/
|
|
14
|
+
export const RECIPIENT_ATTEMPT_MS = 10_000;
|
|
15
|
+
export const RECIPIENT_RETRY_DELAYS_MS = [
|
|
16
|
+
10_000, 60_000, 300_000, 1_800_000, 7_200_000,
|
|
17
|
+
];
|
|
18
|
+
const DRAIN_PAGE = 16;
|
|
19
|
+
const eventCodec = Schema.fromJsonString(Schema.Struct({
|
|
20
|
+
source: Schema.Struct({
|
|
21
|
+
installationId: Schema.String,
|
|
22
|
+
appId: Schema.String,
|
|
23
|
+
releaseId: Schema.String,
|
|
24
|
+
}),
|
|
25
|
+
type: Schema.String,
|
|
26
|
+
eventId: Schema.String,
|
|
27
|
+
payload: Schema.Struct({
|
|
28
|
+
type: Schema.String,
|
|
29
|
+
timestamp: Schema.String,
|
|
30
|
+
data: Schema.Unknown,
|
|
31
|
+
}),
|
|
32
|
+
}));
|
|
33
|
+
const decodeEvent = Schema.decodeUnknownSync(eventCodec);
|
|
34
|
+
const encodeEvent = Schema.encodeSync(eventCodec);
|
|
35
|
+
const dueRow = Schema.Struct({
|
|
36
|
+
recipient: Schema.String,
|
|
37
|
+
event_id: Schema.String,
|
|
38
|
+
body: Schema.String,
|
|
39
|
+
attempts: Schema.Number,
|
|
40
|
+
});
|
|
41
|
+
const deliveryRow = Schema.Struct({
|
|
42
|
+
recipient: Schema.String,
|
|
43
|
+
event_id: Schema.String,
|
|
44
|
+
type: Schema.String,
|
|
45
|
+
state: Schema.Literals(["pending", "delivered", "failed"]),
|
|
46
|
+
attempts: Schema.Number,
|
|
47
|
+
last_error: Schema.NullOr(Schema.String),
|
|
48
|
+
updated_at: Schema.Number,
|
|
49
|
+
});
|
|
50
|
+
const nextRow = Schema.Struct({ at: Schema.NullOr(Schema.Number) });
|
|
51
|
+
function diagnostic(cause) {
|
|
52
|
+
const { causeClass, platform, message } = failureDiagnostic(cause);
|
|
53
|
+
return [causeClass, ...(platform ?? []), message]
|
|
54
|
+
.filter((part) => part !== undefined)
|
|
55
|
+
.join(": ");
|
|
56
|
+
}
|
|
57
|
+
async function bounded(attempt, ms) {
|
|
58
|
+
let timer;
|
|
59
|
+
try {
|
|
60
|
+
await Promise.race([
|
|
61
|
+
attempt,
|
|
62
|
+
new Promise((_resolve, reject) => {
|
|
63
|
+
timer = setTimeout(() => {
|
|
64
|
+
reject(new DOMException("Recipient delivery timed out", "TimeoutError"));
|
|
65
|
+
}, ms);
|
|
66
|
+
}),
|
|
67
|
+
]);
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
clearTimeout(timer);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** The owner's SQLite records, its alarm and the one typed delivery port. */
|
|
74
|
+
export function createRecipientDeliveries(options) {
|
|
75
|
+
const { sql } = options;
|
|
76
|
+
sql.exec(`CREATE TABLE IF NOT EXISTS oyster_recipient_deliveries (
|
|
77
|
+
recipient TEXT NOT NULL, event_id TEXT NOT NULL, type TEXT NOT NULL, body TEXT NOT NULL,
|
|
78
|
+
state TEXT NOT NULL, attempts INTEGER NOT NULL, next_at INTEGER NOT NULL,
|
|
79
|
+
last_error TEXT, updated_at INTEGER NOT NULL,
|
|
80
|
+
PRIMARY KEY (recipient, event_id)) STRICT`);
|
|
81
|
+
sql.exec(`CREATE INDEX IF NOT EXISTS oyster_recipient_deliveries_due
|
|
82
|
+
ON oyster_recipient_deliveries(state, next_at)`);
|
|
83
|
+
const attemptMs = options.attemptMs ?? RECIPIENT_ATTEMPT_MS;
|
|
84
|
+
const nextDue = () => Schema.decodeUnknownSync(nextRow)(sql
|
|
85
|
+
.exec("SELECT MIN(next_at) AS at FROM oyster_recipient_deliveries WHERE state = 'pending'")
|
|
86
|
+
.toArray()[0]).at;
|
|
87
|
+
return {
|
|
88
|
+
/**
|
|
89
|
+
* Record one pending row per recipient (idempotent per event id). The
|
|
90
|
+
* caller commits it with its other rows and then wakes for `now`.
|
|
91
|
+
*/
|
|
92
|
+
enqueue(event, recipients) {
|
|
93
|
+
const now = options.now();
|
|
94
|
+
const body = encodeEvent(event);
|
|
95
|
+
let queued = 0;
|
|
96
|
+
for (const recipient of new Set(recipients))
|
|
97
|
+
queued += sql
|
|
98
|
+
.exec("INSERT INTO oyster_recipient_deliveries (recipient, event_id, type, body, state, attempts, next_at, last_error, updated_at) VALUES (?, ?, ?, ?, 'pending', 0, ?, NULL, ?) ON CONFLICT DO NOTHING RETURNING event_id", recipient, event.eventId, event.type, body, now, now)
|
|
99
|
+
.toArray().length;
|
|
100
|
+
return queued;
|
|
101
|
+
},
|
|
102
|
+
/** Attempt every due row once, record each outcome, and re-arm for the next retry. */
|
|
103
|
+
async drain() {
|
|
104
|
+
const now = options.now();
|
|
105
|
+
const due = sql
|
|
106
|
+
.exec("SELECT recipient, event_id, body, attempts FROM oyster_recipient_deliveries WHERE state = 'pending' AND next_at <= ? ORDER BY next_at LIMIT ?", now, DRAIN_PAGE)
|
|
107
|
+
.toArray()
|
|
108
|
+
.map((row) => Schema.decodeUnknownSync(dueRow)(row));
|
|
109
|
+
await Promise.all(due.map(async (row) => {
|
|
110
|
+
// The attempt's cause becomes this row's recorded diagnostic.
|
|
111
|
+
const failure = await bounded(options.deliver(row.recipient, decodeEvent(row.body)), attemptMs).then(() => null, (cause) => diagnostic(cause));
|
|
112
|
+
const attempts = row.attempts + 1;
|
|
113
|
+
sql.exec("UPDATE oyster_recipient_deliveries SET state = ?, attempts = ?, next_at = ?, last_error = ?, updated_at = ? WHERE recipient = ? AND event_id = ?", failure === null
|
|
114
|
+
? "delivered"
|
|
115
|
+
: attempts > RECIPIENT_RETRY_DELAYS_MS.length
|
|
116
|
+
? "failed"
|
|
117
|
+
: "pending", attempts, now + (RECIPIENT_RETRY_DELAYS_MS[attempts - 1] ?? 0), failure, options.now(), row.recipient, row.event_id);
|
|
118
|
+
}));
|
|
119
|
+
const next = nextDue();
|
|
120
|
+
if (next !== null)
|
|
121
|
+
await options.wake(next);
|
|
122
|
+
},
|
|
123
|
+
/** The earliest pending retry, if any. */
|
|
124
|
+
nextDue,
|
|
125
|
+
/** Recent deliveries to one recipient, newest first. */
|
|
126
|
+
deliveries(recipient) {
|
|
127
|
+
return sql
|
|
128
|
+
.exec("SELECT recipient, event_id, type, state, attempts, last_error, updated_at FROM oyster_recipient_deliveries WHERE recipient = ? ORDER BY updated_at DESC LIMIT 50", recipient)
|
|
129
|
+
.toArray()
|
|
130
|
+
.map((raw) => {
|
|
131
|
+
const row = Schema.decodeUnknownSync(deliveryRow)(raw);
|
|
132
|
+
return {
|
|
133
|
+
recipient: row.recipient,
|
|
134
|
+
eventId: row.event_id,
|
|
135
|
+
type: row.type,
|
|
136
|
+
state: row.state,
|
|
137
|
+
attempts: row.attempts,
|
|
138
|
+
lastError: row.last_error,
|
|
139
|
+
updatedAt: row.updated_at,
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Trusted Loader facade: code is cached, while each invocation receives its own bindings. */
|
|
2
|
+
export declare function facetInvocationModule(exportName: string): string;
|
|
3
|
+
/** The facade's private maintained decoder module, bundled at package build time. */
|
|
4
|
+
export declare const facetFrameBindingModule: string;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { frameBindingWireScript } from "#frame-binding-wire-script";
|
|
2
|
+
import { appInstanceErrorCodeSchema } from "./index.js";
|
|
3
|
+
/** Trusted Loader facade: code is cached, while each invocation receives its own bindings. */
|
|
4
|
+
export function facetInvocationModule(exportName) {
|
|
5
|
+
return `import { DurableObject } from "cloudflare:workers";
|
|
6
|
+
import * as backend from "./backend.js";
|
|
7
|
+
import { createFrameBindingWire } from "./frame-binding-wire.js";
|
|
8
|
+
const Backend = backend[${JSON.stringify(exportName)}];
|
|
9
|
+
const wire = createFrameBindingWire(${JSON.stringify(appInstanceErrorCodeSchema.literals)});
|
|
10
|
+
// Only the App's own class chain is callable; platform and Object members never are.
|
|
11
|
+
function methodOf(method) {
|
|
12
|
+
for (
|
|
13
|
+
let prototype = Backend.prototype;
|
|
14
|
+
prototype !== null &&
|
|
15
|
+
prototype !== DurableObject.prototype &&
|
|
16
|
+
prototype !== Object.prototype;
|
|
17
|
+
prototype = Object.getPrototypeOf(prototype)
|
|
18
|
+
) {
|
|
19
|
+
const descriptor = Object.getOwnPropertyDescriptor(prototype, method);
|
|
20
|
+
if (descriptor === undefined) continue;
|
|
21
|
+
if (method === "constructor" || typeof descriptor.value !== "function")
|
|
22
|
+
break;
|
|
23
|
+
return descriptor.value;
|
|
24
|
+
}
|
|
25
|
+
throw new Error("App method unavailable");
|
|
26
|
+
}
|
|
27
|
+
export class AppInvocation extends DurableObject {
|
|
28
|
+
async invoke(method, args, bindings) {
|
|
29
|
+
if (method === "fetch") throw new Error("App method unavailable");
|
|
30
|
+
return await methodOf(method).apply(new Backend(this.ctx, wire.wrap(bindings)), args);
|
|
31
|
+
}
|
|
32
|
+
async http(input, bindings) {
|
|
33
|
+
const request = new Request(input.url, {
|
|
34
|
+
method: input.method, headers: input.headers,
|
|
35
|
+
...(input.body === null ? {} : {body: input.body}),
|
|
36
|
+
});
|
|
37
|
+
const response = await methodOf("fetch").call(new Backend(this.ctx, wire.wrap(bindings)), request);
|
|
38
|
+
return {status: response.status, statusText: response.statusText,
|
|
39
|
+
headers: [...response.headers], body: response.body};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
`;
|
|
43
|
+
}
|
|
44
|
+
/** The facade's private maintained decoder module, bundled at package build time. */
|
|
45
|
+
export const facetFrameBindingModule = frameBindingWireScript;
|
package/dist/files.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { OysterCapability } from "./host.js";
|
|
2
|
+
/** One entry of a directory listing, in Agar's `computer.list` contract. */
|
|
3
|
+
export type FileEntry = Readonly<{
|
|
4
|
+
name: string;
|
|
5
|
+
type: "file" | "directory" | "symlink";
|
|
6
|
+
size: number;
|
|
7
|
+
modifiedAt: number;
|
|
8
|
+
}>;
|
|
9
|
+
/** A page of a directory, in Agar's `computer.list` contract. */
|
|
10
|
+
export type FileListing = Readonly<{
|
|
11
|
+
path: string;
|
|
12
|
+
entries: readonly FileEntry[];
|
|
13
|
+
nextOffset: number | null;
|
|
14
|
+
}>;
|
|
15
|
+
/** What a conditional replace did, in Agar's `computer.writeFile` contract. */
|
|
16
|
+
export type FileReplaceOutcome = Readonly<{
|
|
17
|
+
kind: "replaced";
|
|
18
|
+
path: string;
|
|
19
|
+
bytes: number;
|
|
20
|
+
}> | Readonly<{
|
|
21
|
+
kind: "conflict";
|
|
22
|
+
}>;
|
|
23
|
+
/**
|
|
24
|
+
* Where an Oyster's files live. Paths are absolute (`/notes/a.md`) and
|
|
25
|
+
* already normalized by the routes; a source never sees `..`.
|
|
26
|
+
*/
|
|
27
|
+
export type FileSource = Readonly<{
|
|
28
|
+
/** One page of a directory's entries, or `null` when it does not exist. */
|
|
29
|
+
list(path: string, limit: number, offset: number): Promise<FileListing | null>;
|
|
30
|
+
/** A file's bytes, or `null` when it does not exist. */
|
|
31
|
+
read(path: string): Promise<Uint8Array | null>;
|
|
32
|
+
/** Create or replace a file. */
|
|
33
|
+
write(path: string, content: Uint8Array): Promise<void>;
|
|
34
|
+
/** Create a directory. */
|
|
35
|
+
mkdir(path: string): Promise<void>;
|
|
36
|
+
/** Write `content` only if the file still holds `expectedContent`. */
|
|
37
|
+
replace(path: string, expectedContent: string, content: string): Promise<FileReplaceOutcome>;
|
|
38
|
+
}>;
|
|
39
|
+
/** Normalize an absolute path; `null` when it is not one or escapes the root. */
|
|
40
|
+
export declare function normalizeFilePath(raw: string): string | null;
|
|
41
|
+
/** File capabilities sharing the existing `/files/` wire and one file source. */
|
|
42
|
+
export declare function fileCapabilities(source: FileSource): readonly OysterCapability[];
|
package/dist/files.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Files for Apps on a standalone Oyster: one file-source contract, routes the App
|
|
3
|
+
* page calls. Apps reach files through Agar's
|
|
4
|
+
* `computer.list`, `computer.readFile` and `computer.writeFile` contracts, so
|
|
5
|
+
* an App written for a Computer (an editor, a workflow step) works against
|
|
6
|
+
* the Oyster's workspace unchanged. Other sources (a git repository) implement
|
|
7
|
+
* the same `FileSource`.
|
|
8
|
+
*/
|
|
9
|
+
import { Hono } from "hono";
|
|
10
|
+
import * as Schema from "effect/Schema";
|
|
11
|
+
import { computerList, computerReadFile, computerWriteFile, } from "@fungi.computer/agar/computer";
|
|
12
|
+
const MAX_FILE_BYTES = 25 * 1024 * 1024;
|
|
13
|
+
const MAX_LIST = 1000;
|
|
14
|
+
/** Normalize an absolute path; `null` when it is not one or escapes the root. */
|
|
15
|
+
export function normalizeFilePath(raw) {
|
|
16
|
+
if (!raw.startsWith("/") || raw.length > 4096 || raw.includes("\0"))
|
|
17
|
+
return null;
|
|
18
|
+
const parts = [];
|
|
19
|
+
for (const part of raw.split("/")) {
|
|
20
|
+
if (part === "" || part === ".")
|
|
21
|
+
continue;
|
|
22
|
+
if (part === "..")
|
|
23
|
+
return null;
|
|
24
|
+
parts.push(part);
|
|
25
|
+
}
|
|
26
|
+
return `/${parts.join("/")}`;
|
|
27
|
+
}
|
|
28
|
+
const replaceSchema = Schema.Struct({
|
|
29
|
+
path: Schema.String,
|
|
30
|
+
expectedContent: Schema.String,
|
|
31
|
+
content: Schema.String,
|
|
32
|
+
});
|
|
33
|
+
const mkdirSchema = Schema.Struct({ path: Schema.String });
|
|
34
|
+
function refusal(status, error) {
|
|
35
|
+
return Response.json({ error }, { status });
|
|
36
|
+
}
|
|
37
|
+
/** File capabilities sharing the existing `/files/` wire and one file source. */
|
|
38
|
+
export function fileCapabilities(source) {
|
|
39
|
+
const routes = createFileRoutes(source);
|
|
40
|
+
const shared = { base: "/files/", routes };
|
|
41
|
+
return [
|
|
42
|
+
{
|
|
43
|
+
...shared,
|
|
44
|
+
id: "computer.list",
|
|
45
|
+
permission: "computer.list",
|
|
46
|
+
version: computerList.version,
|
|
47
|
+
methods: { list: { kind: "json", method: "GET", path: "list" } },
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
...shared,
|
|
51
|
+
id: "computer.readFile",
|
|
52
|
+
permission: "computer.readFile",
|
|
53
|
+
version: computerReadFile.version,
|
|
54
|
+
methods: { open: { kind: "read", method: "GET", path: "content" } },
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
...shared,
|
|
58
|
+
id: "computer.writeFile",
|
|
59
|
+
permission: "computer.writeFile",
|
|
60
|
+
version: computerWriteFile.version,
|
|
61
|
+
methods: {
|
|
62
|
+
open: { kind: "write", method: "PUT", path: "content" },
|
|
63
|
+
mkdir: { kind: "json", method: "POST", path: "mkdir" },
|
|
64
|
+
replace: { kind: "json", method: "POST", path: "replace" },
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
}
|
|
69
|
+
function createFileRoutes(source) {
|
|
70
|
+
const routes = new Hono();
|
|
71
|
+
const pathOf = (raw) => normalizeFilePath(raw ?? "");
|
|
72
|
+
routes.get("/list", async (context) => {
|
|
73
|
+
const path = pathOf(context.req.query("path"));
|
|
74
|
+
if (path === null)
|
|
75
|
+
return refusal(400, "Invalid path");
|
|
76
|
+
const limit = Math.min(MAX_LIST, Math.max(1, Number(context.req.query("limit") ?? "256") || 256));
|
|
77
|
+
const offset = Math.max(0, Number(context.req.query("offset") ?? "0") || 0);
|
|
78
|
+
const listing = await source.list(path, limit, offset);
|
|
79
|
+
return listing === null
|
|
80
|
+
? refusal(404, "No such directory")
|
|
81
|
+
: Response.json(listing);
|
|
82
|
+
});
|
|
83
|
+
routes.get("/content", async (context) => {
|
|
84
|
+
const path = pathOf(context.req.query("path"));
|
|
85
|
+
if (path === null)
|
|
86
|
+
return refusal(400, "Invalid path");
|
|
87
|
+
const bytes = await source.read(path);
|
|
88
|
+
return bytes === null
|
|
89
|
+
? refusal(404, "No such file")
|
|
90
|
+
: new Response(bytes, {
|
|
91
|
+
headers: { "Content-Type": "application/octet-stream" },
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
routes.put("/content", async (context) => {
|
|
95
|
+
const path = pathOf(context.req.query("path"));
|
|
96
|
+
if (path === null || path === "/")
|
|
97
|
+
return refusal(400, "Invalid path");
|
|
98
|
+
const length = Number(context.req.header("content-length") ?? "0");
|
|
99
|
+
if (length > MAX_FILE_BYTES)
|
|
100
|
+
return refusal(413, "File too large");
|
|
101
|
+
const bytes = new Uint8Array(await context.req.arrayBuffer());
|
|
102
|
+
if (bytes.length > MAX_FILE_BYTES)
|
|
103
|
+
return refusal(413, "File too large");
|
|
104
|
+
await source.write(path, bytes);
|
|
105
|
+
return new Response(null, { status: 204 });
|
|
106
|
+
});
|
|
107
|
+
routes.post("/mkdir", async (context) => {
|
|
108
|
+
const body = Schema.decodeUnknownOption(mkdirSchema)(await context.req.json());
|
|
109
|
+
const path = body._tag === "Some" ? normalizeFilePath(body.value.path) : null;
|
|
110
|
+
if (path === null || path === "/")
|
|
111
|
+
return refusal(400, "Invalid path");
|
|
112
|
+
await source.mkdir(path);
|
|
113
|
+
return new Response(null, { status: 204 });
|
|
114
|
+
});
|
|
115
|
+
routes.post("/replace", async (context) => {
|
|
116
|
+
const body = Schema.decodeUnknownOption(replaceSchema)(await context.req.json());
|
|
117
|
+
const path = body._tag === "Some" ? normalizeFilePath(body.value.path) : null;
|
|
118
|
+
if (body._tag === "None" || path === null)
|
|
119
|
+
return refusal(400, "Invalid request");
|
|
120
|
+
return Response.json(await source.replace(path, body.value.expectedContent, body.value.content));
|
|
121
|
+
});
|
|
122
|
+
return routes;
|
|
123
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const frameBindingWireScript = "var X;function l(e,t,r){function n(u,c){if(u._zod||Object.defineProperty(u,\"_zod\",{value:{def:c,constr:s,traits:new Set},enumerable:!1}),u._zod.traits.has(e))return;u._zod.traits.add(e),t(u,c);let a=s.prototype,p=Object.keys(a);for(let f=0;f<p.length;f++){let d=p[f];d in u||(u[d]=a[d].bind(u))}}let o=r?.Parent??Object;class i extends o{}Object.defineProperty(i,\"name\",{value:e});function s(u){var c;let a=r?.Parent?new i:this;n(a,u),(c=a._zod).deferred??(c.deferred=[]);for(let p of a._zod.deferred)p();return a}return Object.defineProperty(s,\"init\",{value:n}),Object.defineProperty(s,Symbol.hasInstance,{value:u=>r?.Parent&&u instanceof r.Parent?!0:u?._zod?.traits?.has(e)}),Object.defineProperty(s,\"name\",{value:e}),s}var $=class extends Error{constructor(){super(\"Encountered Promise during synchronous parse. Use .parseAsync() instead.\")}};(X=globalThis).__zod_globalConfig??(X.__zod_globalConfig={});var j=globalThis.__zod_globalConfig;function v(e){return e&&Object.assign(j,e),j}function H(e){let t=Object.values(e).filter(n=>typeof n==\"number\");return Object.entries(e).filter(([n,o])=>t.indexOf(+n)===-1).map(([n,o])=>o)}function Q(e,t){return typeof t==\"bigint\"?t.toString():t}function D(e){return{get value(){{let r=e();return Object.defineProperty(this,\"value\",{value:r}),r}throw new Error(\"cached value already set\")}}}function ee(e){let t=e.startsWith(\"^\")?1:0,r=e.endsWith(\"$\")?e.length-1:e.length;return e.slice(t,r)}var Y=Symbol(\"evaluating\");function g(e,t,r){let n;Object.defineProperty(e,t,{get(){if(n!==Y)return n===void 0&&(n=Y,n=r()),n},set(o){Object.defineProperty(e,t,{value:o})},configurable:!0})}var L=\"captureStackTrace\"in Error?Error.captureStackTrace:(...e)=>{};function A(e){return typeof e==\"object\"&&e!==null&&!Array.isArray(e)}function te(e){if(A(e)===!1)return!1;let t=e.constructor;if(t===void 0||typeof t!=\"function\")return!0;let r=t.prototype;return!(A(r)===!1||Object.prototype.hasOwnProperty.call(r,\"isPrototypeOf\")===!1)}var re=new Set([\"string\",\"number\",\"symbol\"]);function C(e){return e.replace(/[.*+?^${}()|[\\]\\\\]/g,\"\\\\$&\")}function U(e,t,r){let n=new e._zod.constr(t??e._zod.def);return(!t||r?.parent)&&(n._zod.parent=e),n}function _(e){let t=e;if(!t)return{};if(typeof t==\"string\")return{error:()=>t};if(t?.message!==void 0){if(t?.error!==void 0)throw new Error(\"Cannot specify both `message` and `error` params\");t.error=t.message}return delete t.message,typeof t.error==\"string\"?{...t,error:()=>t.error}:t}function ne(e){return Object.keys(e).filter(t=>e[t]._zod.optin===\"optional\"&&e[t]._zod.optout===\"optional\")}var xt={safeint:[Number.MIN_SAFE_INTEGER,Number.MAX_SAFE_INTEGER],int32:[-2147483648,2147483647],uint32:[0,4294967295],float32:[-34028234663852886e22,34028234663852886e22],float64:[-Number.MAX_VALUE,Number.MAX_VALUE]};function y(e,t=0){if(e.aborted===!0)return!0;for(let r=t;r<e.issues.length;r++)if(e.issues[r]?.continue!==!0)return!0;return!1}function oe(e,t=0){if(e.aborted===!0)return!0;for(let r=t;r<e.issues.length;r++)if(e.issues[r]?.continue===!1)return!0;return!1}function b(e,t){return t.map(r=>{var n;return(n=r).path??(n.path=[]),r.path.unshift(e),r})}function T(e){return typeof e==\"string\"?e:e?.message}function Z(e,t,r){let n=e.message?e.message:T(e.inst?._zod.def?.error?.(e))??T(t?.error?.(e))??T(r.customError?.(e))??T(r.localeError?.(e))??\"Invalid input\",{inst:o,continue:i,input:s,...u}=e;return u.path??(u.path=[]),u.message=n,t?.reportInput&&(u.input=s),u}function B(...e){let[t,r,n]=e;return typeof t==\"string\"?{message:t,code:\"custom\",input:r,inst:n}:{...t}}var ie=(e,t)=>{e.name=\"$ZodError\",Object.defineProperty(e,\"_zod\",{value:e._zod,enumerable:!1}),Object.defineProperty(e,\"issues\",{value:t,enumerable:!1}),e.message=JSON.stringify(t,Q,2),Object.defineProperty(e,\"toString\",{value:()=>e.message,enumerable:!1})},se=l(\"$ZodError\",ie),M=l(\"$ZodError\",ie,{Parent:Error});var We=e=>(t,r,n,o)=>{let i=n?{...n,async:!1}:{async:!1},s=t._zod.run({value:r,issues:[]},i);if(s instanceof Promise)throw new $;if(s.issues.length){let u=new(o?.Err??e)(s.issues.map(c=>Z(c,i,v())));throw L(u,o?.callee),u}return s.value},w=We(M),Ke=e=>async(t,r,n,o)=>{let i=n?{...n,async:!0}:{async:!0},s=t._zod.run({value:r,issues:[]},i);if(s instanceof Promise&&(s=await s),s.issues.length){let u=new(o?.Err??e)(s.issues.map(c=>Z(c,i,v())));throw L(u,o?.callee),u}return s.value},k=Ke(M),Ge=e=>(t,r,n)=>{let o=n?{...n,async:!1}:{async:!1},i=t._zod.run({value:r,issues:[]},o);if(i instanceof Promise)throw new $;return i.issues.length?{success:!1,error:new(e??se)(i.issues.map(s=>Z(s,o,v())))}:{success:!0,data:i.value}},I=Ge(M),Je=e=>async(t,r,n)=>{let o=n?{...n,async:!0}:{async:!0},i=t._zod.run({value:r,issues:[]},o);return i instanceof Promise&&(i=await i),i.issues.length?{success:!1,error:new e(i.issues.map(s=>Z(s,o,v())))}:{success:!0,data:i.value}},S=Je(M);var qe=\"(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))\",Xe=new RegExp(`^${qe}$`);var ue=e=>{let t=e?`[\\\\s\\\\S]{${e?.minimum??0},${e?.maximum??\"\"}}`:\"[\\\\s\\\\S]*\";return new RegExp(`^${t}$`)};var ce=/^-?\\d+(?:\\.\\d+)?$/;var V=l(\"$ZodCheck\",(e,t)=>{var r;e._zod??(e._zod={}),e._zod.def=t,(r=e._zod).onattach??(r.onattach=[])});var pe={major:4,minor:4,patch:3};var h=l(\"$ZodType\",(e,t)=>{var r;e??(e={}),e._zod.def=t,e._zod.bag=e._zod.bag||{},e._zod.version=pe;let n=[...e._zod.def.checks??[]];e._zod.traits.has(\"$ZodCheck\")&&n.unshift(e);for(let o of n)for(let i of o._zod.onattach)i(e);if(n.length===0)(r=e._zod).deferred??(r.deferred=[]),e._zod.deferred?.push(()=>{e._zod.run=e._zod.parse});else{let o=(s,u,c)=>{let a=y(s),p;for(let f of u){if(f._zod.def.when){if(oe(s)||!f._zod.def.when(s))continue}else if(a)continue;let d=s.issues.length,m=f._zod.check(s);if(m instanceof Promise&&c?.async===!1)throw new $;if(p||m instanceof Promise)p=(p??Promise.resolve()).then(async()=>{await m,s.issues.length!==d&&(a||(a=y(s,d)))});else{if(s.issues.length===d)continue;a||(a=y(s,d))}}return p?p.then(()=>s):s},i=(s,u,c)=>{if(y(s))return s.aborted=!0,s;let a=o(u,n,c);if(a instanceof Promise){if(c.async===!1)throw new $;return a.then(p=>e._zod.parse(p,c))}return e._zod.parse(a,c)};e._zod.run=(s,u)=>{if(u.skipChecks)return e._zod.parse(s,u);if(u.direction===\"backward\"){let a=e._zod.parse({value:s.value,issues:[]},{...u,skipChecks:!0});return a instanceof Promise?a.then(p=>i(p,s,u)):i(a,s,u)}let c=e._zod.parse(s,u);if(c instanceof Promise){if(u.async===!1)throw new $;return c.then(a=>o(a,n,u))}return o(c,n,u)}}g(e,\"~standard\",()=>({validate:o=>{try{let i=I(e,o);return i.success?{value:i.data}:{issues:i.error?.issues}}catch{return S(e,o).then(s=>s.success?{value:s.data}:{issues:s.error?.issues})}},vendor:\"zod\",version:1}))}),xe=l(\"$ZodString\",(e,t)=>{h.init(e,t),e._zod.pattern=[...e?._zod.bag?.patterns??[]].pop()??ue(e._zod.bag),e._zod.parse=(r,n)=>{if(t.coerce)try{r.value=String(r.value)}catch{}return typeof r.value==\"string\"||r.issues.push({expected:\"string\",code:\"invalid_type\",input:r.value,inst:e}),r}});var ge=l(\"$ZodUnknown\",(e,t)=>{h.init(e,t),e._zod.parse=r=>r}),ze=l(\"$ZodNever\",(e,t)=>{h.init(e,t),e._zod.parse=(r,n)=>(r.issues.push({expected:\"never\",code:\"invalid_type\",input:r.value,inst:e}),r)});function le(e,t,r){e.issues.length&&t.issues.push(...b(r,e.issues)),t.value[r]=e.value}var $e=l(\"$ZodArray\",(e,t)=>{h.init(e,t),e._zod.parse=(r,n)=>{let o=r.value;if(!Array.isArray(o))return r.issues.push({expected:\"array\",code:\"invalid_type\",input:o,inst:e}),r;r.value=Array(o.length);let i=[];for(let s=0;s<o.length;s++){let u=o[s],c=t.element._zod.run({value:u,issues:[]},n);c instanceof Promise?i.push(c.then(a=>le(a,r,s))):le(c,r,s)}return i.length?Promise.all(i).then(()=>r):r}});function R(e,t,r,n,o,i){let s=r in n;if(e.issues.length){if(o&&i&&!s)return;t.issues.push(...b(r,e.issues))}if(!s&&!o){e.issues.length||t.issues.push({code:\"invalid_type\",expected:\"nonoptional\",input:void 0,path:[r]});return}e.value===void 0?s&&(t.value[r]=void 0):t.value[r]=e.value}function He(e){let t=Object.keys(e.shape);for(let n of t)if(!e.shape?.[n]?._zod?.traits?.has(\"$ZodType\"))throw new Error(`Invalid element at key \"${n}\": expected a Zod schema`);let r=ne(e.shape);return{...e,keys:t,keySet:new Set(t),numKeys:t.length,optionalKeys:new Set(r)}}function Qe(e,t,r,n,o,i){let s=[],u=o.keySet,c=o.catchall._zod,a=c.def.type,p=c.optin===\"optional\",f=c.optout===\"optional\";for(let d in t){if(d===\"__proto__\"||u.has(d))continue;if(a===\"never\"){s.push(d);continue}let m=c.run({value:t[d],issues:[]},n);m instanceof Promise?e.push(m.then(z=>R(z,r,d,t,p,f))):R(m,r,d,t,p,f)}return s.length&&r.issues.push({code:\"unrecognized_keys\",keys:s,input:t,inst:i}),e.length?Promise.all(e).then(()=>r):r}var ve=l(\"$ZodObject\",(e,t)=>{if(h.init(e,t),!Object.getOwnPropertyDescriptor(t,\"shape\")?.get){let u=t.shape;Object.defineProperty(t,\"shape\",{get:()=>{let c={...u};return Object.defineProperty(t,\"shape\",{value:c}),c}})}let n=D(()=>He(t));g(e._zod,\"propValues\",()=>{let u=t.shape,c={};for(let a in u){let p=u[a]._zod;if(p.values){c[a]??(c[a]=new Set);for(let f of p.values)c[a].add(f)}}return c});let o=A,i=t.catchall,s;e._zod.parse=(u,c)=>{s??(s=n.value);let a=u.value;if(!o(a))return u.issues.push({expected:\"object\",code:\"invalid_type\",input:a,inst:e}),u;u.value={};let p=[],f=s.shape;for(let d of s.keys){let m=f[d],z=m._zod.optin===\"optional\",q=m._zod.optout===\"optional\",N=m._zod.run({value:a[d],issues:[]},c);N instanceof Promise?p.push(N.then(Be=>R(Be,u,d,a,z,q))):R(N,u,d,a,z,q)}return i?Qe(p,a,u,c,n.value,e):p.length?Promise.all(p).then(()=>u):u}});function fe(e,t,r,n){for(let i of e)if(i.issues.length===0)return t.value=i.value,t;let o=e.filter(i=>!y(i));return o.length===1?(t.value=o[0].value,o[0]):(t.issues.push({code:\"invalid_union\",input:t.value,inst:r,errors:e.map(i=>i.issues.map(s=>Z(s,n,v())))}),t)}var be=l(\"$ZodUnion\",(e,t)=>{h.init(e,t),g(e._zod,\"optin\",()=>t.options.some(n=>n._zod.optin===\"optional\")?\"optional\":void 0),g(e._zod,\"optout\",()=>t.options.some(n=>n._zod.optout===\"optional\")?\"optional\":void 0),g(e._zod,\"values\",()=>{if(t.options.every(n=>n._zod.values))return new Set(t.options.flatMap(n=>Array.from(n._zod.values)))}),g(e._zod,\"pattern\",()=>{if(t.options.every(n=>n._zod.pattern)){let n=t.options.map(o=>o._zod.pattern);return new RegExp(`^(${n.map(o=>ee(o.source)).join(\"|\")})$`)}});let r=t.options.length===1?t.options[0]._zod.run:null;e._zod.parse=(n,o)=>{if(r)return r(n,o);let i=!1,s=[];for(let u of t.options){let c=u._zod.run({value:n.value,issues:[]},o);if(c instanceof Promise)s.push(c),i=!0;else{if(c.issues.length===0)return c;s.push(c)}}return i?Promise.all(s).then(u=>fe(u,n,e,o)):fe(s,n,e,o)}});var Ze=l(\"$ZodDiscriminatedUnion\",(e,t)=>{t.inclusive=!1,be.init(e,t);let r=e._zod.parse;g(e._zod,\"propValues\",()=>{let o={};for(let i of t.options){let s=i._zod.propValues;if(!s||Object.keys(s).length===0)throw new Error(`Invalid discriminated union option at index \"${t.options.indexOf(i)}\"`);for(let[u,c]of Object.entries(s)){o[u]||(o[u]=new Set);for(let a of c)o[u].add(a)}}return o});let n=D(()=>{let o=t.options,i=new Map;for(let s of o){let u=s._zod.propValues?.[t.discriminator];if(!u||u.size===0)throw new Error(`Invalid discriminated union option at index \"${t.options.indexOf(s)}\"`);for(let c of u){if(i.has(c))throw new Error(`Duplicate discriminator value \"${String(c)}\"`);i.set(c,s)}}return i});e._zod.parse=(o,i)=>{let s=o.value;if(!A(s))return o.issues.push({code:\"invalid_type\",expected:\"object\",input:s,inst:e}),o;let u=n.value.get(s?.[t.discriminator]);return u?u._zod.run(o,i):t.unionFallback||i.direction===\"backward\"?r(o,i):(o.issues.push({code:\"invalid_union\",errors:[],note:\"No matching discriminator\",discriminator:t.discriminator,options:Array.from(n.value.keys()),input:s,path:[t.discriminator],inst:e}),o)}});var W=l(\"$ZodTuple\",(e,t)=>{h.init(e,t);let r=t.items;e._zod.parse=(n,o)=>{let i=n.value;if(!Array.isArray(i))return n.issues.push({input:i,inst:e,expected:\"tuple\",code:\"invalid_type\"}),n;n.value=[];let s=[],u=de(r,\"optin\"),c=de(r,\"optout\");if(!t.rest){if(i.length<u)return n.issues.push({code:\"too_small\",minimum:u,inclusive:!0,input:i,inst:e,origin:\"array\"}),n;i.length>r.length&&n.issues.push({code:\"too_big\",maximum:r.length,inclusive:!0,input:i,inst:e,origin:\"array\"})}let a=new Array(r.length);for(let p=0;p<r.length;p++){let f=r[p]._zod.run({value:i[p],issues:[]},o);f instanceof Promise?s.push(f.then(d=>{a[p]=d})):a[p]=f}if(t.rest){let p=r.length-1,f=i.slice(r.length);for(let d of f){p++;let m=t.rest._zod.run({value:d,issues:[]},o);m instanceof Promise?s.push(m.then(z=>me(z,n,p))):me(m,n,p)}}return s.length?Promise.all(s).then(()=>he(a,n,r,i,c)):he(a,n,r,i,c)}});function de(e,t){for(let r=e.length-1;r>=0;r--)if(e[r]._zod[t]!==\"optional\")return r+1;return 0}function me(e,t,r){e.issues.length&&t.issues.push(...b(r,e.issues)),t.value[r]=e.value}function he(e,t,r,n,o){for(let i=0;i<r.length;i++){let s=e[i],u=i<n.length;if(s.issues.length){if(!u&&i>=o){t.value.length=i;break}t.issues.push(...b(i,s.issues))}t.value[i]=s.value}for(let i=t.value.length-1;i>=n.length&&(r[i]._zod.optout===\"optional\"&&t.value[i]===void 0);i--)t.value.length=i;return t}var ye=l(\"$ZodRecord\",(e,t)=>{h.init(e,t),e._zod.parse=(r,n)=>{let o=r.value;if(!te(o))return r.issues.push({expected:\"record\",code:\"invalid_type\",input:o,inst:e}),r;let i=[],s=t.keyType._zod.values;if(s){r.value={};let u=new Set;for(let a of s)if(typeof a==\"string\"||typeof a==\"number\"||typeof a==\"symbol\"){u.add(typeof a==\"number\"?a.toString():a);let p=t.keyType._zod.run({value:a,issues:[]},n);if(p instanceof Promise)throw new Error(\"Async schemas not supported in object keys currently\");if(p.issues.length){r.issues.push({code:\"invalid_key\",origin:\"record\",issues:p.issues.map(m=>Z(m,n,v())),input:a,path:[a],inst:e});continue}let f=p.value,d=t.valueType._zod.run({value:o[a],issues:[]},n);d instanceof Promise?i.push(d.then(m=>{m.issues.length&&r.issues.push(...b(a,m.issues)),r.value[f]=m.value})):(d.issues.length&&r.issues.push(...b(a,d.issues)),r.value[f]=d.value)}let c;for(let a in o)u.has(a)||(c=c??[],c.push(a));c&&c.length>0&&r.issues.push({code:\"unrecognized_keys\",input:o,inst:e,keys:c})}else{r.value={};for(let u of Reflect.ownKeys(o)){if(u===\"__proto__\"||!Object.prototype.propertyIsEnumerable.call(o,u))continue;let c=t.keyType._zod.run({value:u,issues:[]},n);if(c instanceof Promise)throw new Error(\"Async schemas not supported in object keys currently\");if(typeof u==\"string\"&&ce.test(u)&&c.issues.length){let f=t.keyType._zod.run({value:Number(u),issues:[]},n);if(f instanceof Promise)throw new Error(\"Async schemas not supported in object keys currently\");f.issues.length===0&&(c=f)}if(c.issues.length){t.mode===\"loose\"?r.value[u]=o[u]:r.issues.push({code:\"invalid_key\",origin:\"record\",issues:c.issues.map(f=>Z(f,n,v())),input:u,path:[u],inst:e});continue}let p=t.valueType._zod.run({value:o[u],issues:[]},n);p instanceof Promise?i.push(p.then(f=>{f.issues.length&&r.issues.push(...b(u,f.issues)),r.value[c.value]=f.value})):(p.issues.length&&r.issues.push(...b(u,p.issues)),r.value[c.value]=p.value)}}return i.length?Promise.all(i).then(()=>r):r}});var we=l(\"$ZodEnum\",(e,t)=>{h.init(e,t);let r=H(t.entries),n=new Set(r);e._zod.values=n,e._zod.pattern=new RegExp(`^(${r.filter(o=>re.has(typeof o)).map(o=>typeof o==\"string\"?C(o):o.toString()).join(\"|\")})$`),e._zod.parse=(o,i)=>{let s=o.value;return n.has(s)||o.issues.push({code:\"invalid_value\",values:r,input:s,inst:e}),o}}),ke=l(\"$ZodLiteral\",(e,t)=>{if(h.init(e,t),t.values.length===0)throw new Error(\"Cannot create literal schema with no valid values\");let r=new Set(t.values);e._zod.values=r,e._zod.pattern=new RegExp(`^(${t.values.map(n=>typeof n==\"string\"?C(n):n?C(n.toString()):String(n)).join(\"|\")})$`),e._zod.parse=(n,o)=>{let i=n.value;return r.has(i)||n.issues.push({code:\"invalid_value\",values:t.values,input:i,inst:e}),n}});var Pe=l(\"$ZodFunction\",(e,t)=>(h.init(e,t),e._def=t,e._zod.def=t,e.implement=r=>{if(typeof r!=\"function\")throw new Error(\"implement() must be called with a function\");return function(...n){let o=e._def.input?w(e._def.input,n):n,i=Reflect.apply(r,this,o);return e._def.output?w(e._def.output,i):i}},e.implementAsync=r=>{if(typeof r!=\"function\")throw new Error(\"implementAsync() must be called with a function\");return async function(...n){let o=e._def.input?await k(e._def.input,n):n,i=await Reflect.apply(r,this,o);return e._def.output?await k(e._def.output,i):i}},e._zod.parse=(r,n)=>typeof r.value!=\"function\"?(r.issues.push({code:\"invalid_type\",expected:\"function\",input:r.value,inst:e}),r):(e._def.output&&e._def.output._zod.def.type===\"promise\"?r.value=e.implementAsync(r.value):r.value=e.implement(r.value),r),e.input=(...r)=>{let n=e.constructor;return Array.isArray(r[0])?new n({type:\"function\",input:new W({type:\"tuple\",items:r[0],rest:r[1]}),output:e._def.output}):new n({type:\"function\",input:r[0],output:e._def.output})},e.output=r=>{let n=e.constructor;return new n({type:\"function\",input:e._def.input,output:r})},e)),Ae=l(\"$ZodPromise\",(e,t)=>{h.init(e,t),e._zod.parse=(r,n)=>Promise.resolve(r.value).then(o=>t.innerType._zod.run({value:o,issues:[]},n))});var Ee=l(\"$ZodCustom\",(e,t)=>{V.init(e,t),h.init(e,t),e._zod.parse=(r,n)=>r,e._zod.check=r=>{let n=r.value,o=t.fn(n);if(o instanceof Promise)return o.then(i=>_e(i,r,n,e));_e(o,r,n,e)}});function _e(e,t,r,n){if(!e){let o={code:\"custom\",input:r,inst:n,path:[...n._zod.def.path??[]],continue:!n._zod.def.abort};n._zod.def.params&&(o.params=n._zod.def.params),t.issues.push(B(o))}}function Me(e,t){return new e({type:\"string\",..._(t)})}function Ie(e){return new e({type:\"unknown\"})}function Se(e,t){return new e({type:\"never\",..._(t)})}function Te(e,t,r){let n=_(r);return n.abort??(n.abort=!0),new e({type:\"custom\",check:\"custom\",fn:t,...n})}var x=l(\"ZodMiniType\",(e,t)=>{if(!e._zod)throw new Error(\"Uninitialized schema in ZodMiniType.\");h.init(e,t),e.def=t,e.type=t.type,e.parse=(r,n)=>w(e,r,n,{callee:e.parse}),e.safeParse=(r,n)=>I(e,r,n),e.parseAsync=async(r,n)=>k(e,r,n,{callee:e.parseAsync}),e.safeParseAsync=async(r,n)=>S(e,r,n),e.check=(...r)=>e.clone({...t,checks:[...t.checks??[],...r.map(n=>typeof n==\"function\"?{_zod:{check:n,def:{check:\"custom\"},onattach:[]}}:n)]},{parent:!0}),e.with=e.check,e.clone=(r,n)=>U(e,r,n),e.brand=()=>e,e.register=((r,n)=>(r.add(e,n),e)),e.apply=r=>r(e)}),rt=l(\"ZodMiniString\",(e,t)=>{xe.init(e,t),x.init(e,t)});function O(e){return Me(rt,e)}var nt=l(\"ZodMiniUnknown\",(e,t)=>{ge.init(e,t),x.init(e,t)});function P(){return Ie(nt)}var ot=l(\"ZodMiniNever\",(e,t)=>{ze.init(e,t),x.init(e,t)});function it(e){return Se(ot,e)}var st=l(\"ZodMiniArray\",(e,t)=>{$e.init(e,t),x.init(e,t)});function K(e,t){return new st({type:\"array\",element:e,..._(t)})}var Re=l(\"ZodMiniObject\",(e,t)=>{ve.init(e,t),x.init(e,t),g(e,\"shape\",()=>t.shape)});function Oe(e,t){let r={type:\"object\",shape:e??{},..._(t)};return new Re(r)}function G(e,t){return new Re({type:\"object\",shape:e,catchall:it(),..._(t)})}var ut=l(\"ZodMiniDiscriminatedUnion\",(e,t)=>{Ze.init(e,t),x.init(e,t)});function Ne(e,t,r){return new ut({type:\"union\",options:t,discriminator:e,..._(r)})}var ct=l(\"ZodMiniTuple\",(e,t)=>{W.init(e,t),x.init(e,t)});function at(e,t,r){let n=t instanceof h,o=n?r:t,i=n?t:null;return new ct({type:\"tuple\",items:e,rest:i,..._(o)})}var Ce=l(\"ZodMiniRecord\",(e,t)=>{ye.init(e,t),x.init(e,t)});function je(e,t,r){return!t||!t._zod?new Ce({type:\"record\",keyType:O(),valueType:e,..._(t)}):new Ce({type:\"record\",keyType:e,valueType:t,..._(r)})}var pt=l(\"ZodMiniEnum\",(e,t)=>{we.init(e,t),x.init(e,t),e.options=Object.values(t.entries)});function Fe(e,t){let r=Array.isArray(e)?Object.fromEntries(e.map(n=>[n,n])):e;return new pt({type:\"enum\",entries:r,..._(t)})}var lt=l(\"ZodMiniLiteral\",(e,t)=>{ke.init(e,t),x.init(e,t)});function J(e,t){return new lt({type:\"literal\",values:Array.isArray(e)?e:[e],..._(t)})}var ft=l(\"ZodMiniPromise\",(e,t)=>{Ae.init(e,t),x.init(e,t)});function De(e){return new ft({type:\"promise\",innerType:e})}var dt=l(\"ZodMiniCustom\",(e,t)=>{Ee.init(e,t),x.init(e,t)});function mt(e,t){return Te(dt,e??(()=>!0),t)}function Le(e,t={}){let r=mt(n=>n instanceof e,t);return r._zod.bag.Class=e,r._zod.check=n=>{n.value instanceof e||n.issues.push({code:\"invalid_type\",expected:e.name,input:n.value,inst:r,path:[...r._zod.def.path??[]]})},r}var ht=l(\"ZodMiniFunction\",(e,t)=>{Pe.init(e,t),x.init(e,t)});function Ue(e){return new ht({type:\"function\",input:Array.isArray(e?.input)?at(e?.input):e?.input??K(P()),output:e?.output??P()})}var _t=je(O(),Le(Object));function go(e){let t=Ne(\"kind\",[G({kind:J(\"result\"),value:P()}),G({kind:J(\"local_refusal\"),code:Fe(e)})]);class r extends Error{constructor(u){super(`App binding refused: ${u}`);this.code=u}code;name=\"AppInstanceError\"}let n=i=>t.parse(i),o=i=>{let s=t.parse(i);if(s.kind===\"local_refusal\")throw new r(s.code);return s.value};return{decode:n,wrap(i){return Object.fromEntries(Object.entries(_t.parse(i)).map(([s,u])=>[s,new Proxy({},{get(c,a){let p=O().safeParse(a);if(!(!p.success||p.data===\"then\"))return async(...f)=>{let d=Ue({input:K(P()),output:De(P())}),m=Oe({[p.data]:d}).parse(Object.create(u))[p.data];if(m===void 0)throw new Error(\"App binding method unavailable\");return o(await m.apply(u,[...f]))}}})]))}}}export{go as createFrameBindingWire};\n";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as z from "zod/mini";
|
|
2
|
+
import type { AppInstanceErrorCode } from "./index.js";
|
|
3
|
+
/** Private RPC envelope. Domain results, including domain refusals, stay inside value. */
|
|
4
|
+
export type FrameBindingReply<Value> = Readonly<{
|
|
5
|
+
kind: "result";
|
|
6
|
+
value: Value;
|
|
7
|
+
}> | Readonly<{
|
|
8
|
+
kind: "local_refusal";
|
|
9
|
+
code: AppInstanceErrorCode;
|
|
10
|
+
}>;
|
|
11
|
+
/** Native entrypoint methods carry the envelope; guest interfaces are restored by wrap. */
|
|
12
|
+
export type FrameBindingWire<Methods> = {
|
|
13
|
+
[Name in keyof Methods]: Methods[Name] extends (...args: infer Args) => Promise<infer Value> ? (...args: Args) => Promise<FrameBindingReply<Value>> : never;
|
|
14
|
+
};
|
|
15
|
+
declare const frameStubsSchema: z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniCustom<Object, Object>>;
|
|
16
|
+
type FrameBindingStubs = z.output<typeof frameStubsSchema>;
|
|
17
|
+
/** The host supplies its one closed refusal vocabulary to both copies of this decoder. */
|
|
18
|
+
export declare function createFrameBindingWire<const Codes extends readonly [string, ...string[]]>(codes: Codes): {
|
|
19
|
+
decode: (raw: unknown) => {
|
|
20
|
+
kind: "result";
|
|
21
|
+
value: unknown;
|
|
22
|
+
} | {
|
|
23
|
+
kind: "local_refusal";
|
|
24
|
+
code: ({ [k_1 in Codes[number]]: k_1; } extends infer T ? { [k in keyof T]: T[k]; } : never)[Codes[number]];
|
|
25
|
+
};
|
|
26
|
+
wrap(bindings: FrameBindingStubs): object;
|
|
27
|
+
};
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as z from "zod/mini";
|
|
2
|
+
const frameStubsSchema = z.record(z.string(), z.instanceof(Object));
|
|
3
|
+
/** The host supplies its one closed refusal vocabulary to both copies of this decoder. */
|
|
4
|
+
export function createFrameBindingWire(codes) {
|
|
5
|
+
const replySchema = z.discriminatedUnion("kind", [
|
|
6
|
+
z.strictObject({ kind: z.literal("result"), value: z.unknown() }),
|
|
7
|
+
z.strictObject({ kind: z.literal("local_refusal"), code: z.enum(codes) }),
|
|
8
|
+
]);
|
|
9
|
+
class LocalRefusal extends Error {
|
|
10
|
+
code;
|
|
11
|
+
name = "AppInstanceError";
|
|
12
|
+
constructor(code) {
|
|
13
|
+
super(`App binding refused: ${code}`);
|
|
14
|
+
this.code = code;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const decode = (raw) => replySchema.parse(raw);
|
|
18
|
+
const unwrap = (raw) => {
|
|
19
|
+
const reply = replySchema.parse(raw);
|
|
20
|
+
if (reply.kind === "local_refusal")
|
|
21
|
+
throw new LocalRefusal(reply.code);
|
|
22
|
+
return reply.value;
|
|
23
|
+
};
|
|
24
|
+
return {
|
|
25
|
+
decode,
|
|
26
|
+
wrap(bindings) {
|
|
27
|
+
return Object.fromEntries(Object.entries(frameStubsSchema.parse(bindings)).map(([name, stub]) => {
|
|
28
|
+
return [
|
|
29
|
+
name,
|
|
30
|
+
new Proxy({}, {
|
|
31
|
+
get(_target, method) {
|
|
32
|
+
const named = z.string().safeParse(method);
|
|
33
|
+
if (!named.success || named.data === "then")
|
|
34
|
+
return undefined;
|
|
35
|
+
return async (...args) => {
|
|
36
|
+
const callSchema = z.function({
|
|
37
|
+
input: z.array(z.unknown()),
|
|
38
|
+
output: z.promise(z.unknown()),
|
|
39
|
+
});
|
|
40
|
+
// Select one native method without enumerating the opaque stub.
|
|
41
|
+
const call = z
|
|
42
|
+
.object({ [named.data]: callSchema })
|
|
43
|
+
.parse(Object.create(stub))[named.data];
|
|
44
|
+
if (call === undefined)
|
|
45
|
+
throw new Error("App binding method unavailable");
|
|
46
|
+
return unwrap(await call.apply(stub, [...args]));
|
|
47
|
+
};
|
|
48
|
+
},
|
|
49
|
+
}),
|
|
50
|
+
];
|
|
51
|
+
}));
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/** One admitted path for external and installation-authored repository writes. */
|
|
2
|
+
import { type AppGitPushFact, type GitBinding, type GitExchangeRequest, type GitExchangeResponse, type GitPushQuestion, type GitPushVerdict, type GitWriter } from "@fungi.computer/agar/git";
|
|
3
|
+
import type { HostActor } from "@fungi.computer/puffball/app-method-runtime";
|
|
4
|
+
import type { AppDeclaration } from "@fungi.computer/puffball/app";
|
|
5
|
+
import type { Installation } from "./apps/installation.js";
|
|
6
|
+
/** Installation-scoped storage; transport and credentials stay with its provider adapter. */
|
|
7
|
+
export type GitStore = GitBinding;
|
|
8
|
+
/** Live host authority, reopened before every provider call. Null is an anonymous reader. */
|
|
9
|
+
export type GitGrants = Readonly<{
|
|
10
|
+
/** Reopen the host authority for this repository and requested scope. */
|
|
11
|
+
admits(writer: GitWriter | null, repository: string, scope: "read" | "write"): Promise<boolean>;
|
|
12
|
+
}>;
|
|
13
|
+
/** Reuse the live installation/verified Release rule without opening provider storage. */
|
|
14
|
+
export declare function createInstallationGitAdmission(ports: Readonly<{
|
|
15
|
+
currentInstallation(): Promise<Installation | null>;
|
|
16
|
+
declaration(current: Installation): Promise<AppDeclaration | null>;
|
|
17
|
+
}>): GitGrants["admits"];
|
|
18
|
+
/** Repository rules owned by the selected App; a missing or malformed answer refuses. */
|
|
19
|
+
export type GitPolicy = Readonly<{
|
|
20
|
+
/** Ask the selected App to decide every ref update in the whole push. */
|
|
21
|
+
authorize(question: GitPushQuestion): Promise<GitPushVerdict>;
|
|
22
|
+
}>;
|
|
23
|
+
/** Durable accepted-ref journal; record commits before the provider result is acknowledged. */
|
|
24
|
+
export type GitJournal = Readonly<{
|
|
25
|
+
/** Read the last durably accepted hash for a ref, or null if unknown. */
|
|
26
|
+
tip(repository: string, ref: string): Promise<string | null>;
|
|
27
|
+
/** Persist accepted refs and their fact before acknowledging the push. */
|
|
28
|
+
record(fact: AppGitPushFact): Promise<void>;
|
|
29
|
+
}>;
|
|
30
|
+
/** Host-held read/write path; Apps receive only its installation-authored binding. */
|
|
31
|
+
export type AdmittedGit = Readonly<{
|
|
32
|
+
/** Admit an external writer through host authority, App policy and the journal. */
|
|
33
|
+
push(writer: Extract<GitWriter, {
|
|
34
|
+
kind: "actor";
|
|
35
|
+
}>, repository: string, request: GitExchangeRequest): Promise<GitExchangeResponse>;
|
|
36
|
+
/** Recheck host read authority before exchanging provider bytes. */
|
|
37
|
+
read(writer: Extract<GitWriter, {
|
|
38
|
+
kind: "actor";
|
|
39
|
+
}> | null, repository: string, request: GitExchangeRequest): Promise<GitExchangeResponse>;
|
|
40
|
+
/** Bind installation-authored operations; null attribution is lifecycle-only. */
|
|
41
|
+
binding(onBehalfOf: HostActor | null): GitBinding;
|
|
42
|
+
}>;
|
|
43
|
+
/** Fixed order: host grant, Workflow ceiling, App policy, provider exchange, durable journal. */
|
|
44
|
+
export declare function createAdmittedGit(ports: Readonly<{
|
|
45
|
+
installationId: string;
|
|
46
|
+
store: GitStore;
|
|
47
|
+
grants: GitGrants;
|
|
48
|
+
policy: GitPolicy;
|
|
49
|
+
journal: GitJournal;
|
|
50
|
+
/** Host-held invocation closure, checked after asynchronous authority admission. */
|
|
51
|
+
beforeProvider?: (() => void) | undefined;
|
|
52
|
+
}>): AdmittedGit;
|