@fungi.computer/puffball 0.0.0-stage → 0.0.1
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 +443 -2
- package/dist/app-api.d.ts +7 -0
- package/dist/app-api.js +61 -0
- package/dist/app-backend-binding-vocabulary.d.ts +299 -0
- package/dist/app-backend-binding-vocabulary.js +257 -0
- package/dist/app-backend-bindings.d.ts +21 -0
- package/dist/app-backend-bindings.js +47 -0
- package/dist/app-declaration.d.ts +46 -0
- package/dist/app-declaration.js +115 -0
- package/dist/app-event-facts.d.ts +40 -0
- package/dist/app-event-facts.js +28 -0
- package/dist/app-method-context.d.ts +109 -0
- package/dist/app-method-context.js +9 -0
- package/dist/app-method-runtime.d.ts +86 -0
- package/dist/app-method-runtime.js +122 -0
- package/dist/app-origin-paths.d.ts +6 -0
- package/dist/app-origin-paths.js +6 -0
- package/dist/app-vocabulary.d.ts +189 -0
- package/dist/app-vocabulary.js +103 -0
- package/dist/app.d.ts +379 -0
- package/dist/app.js +71 -0
- package/dist/artifact-dispositions.d.ts +115 -0
- package/dist/artifact-dispositions.js +234 -0
- package/dist/assets.d.ts +54 -0
- package/dist/assets.js +89 -0
- package/dist/backend.d.ts +41 -0
- package/dist/backend.js +29 -0
- package/dist/background-replay.d.ts +115 -0
- package/dist/background-replay.js +175 -0
- package/dist/buyer-references.d.ts +69 -0
- package/dist/buyer-references.js +154 -0
- package/dist/cloudflare-assets.d.ts +48 -0
- package/dist/cloudflare-assets.js +105 -0
- package/dist/cloudflare.d.ts +254 -0
- package/dist/cloudflare.js +1619 -0
- package/dist/effect.d.ts +73 -0
- package/dist/effect.js +325 -0
- package/dist/git-representation.d.ts +13 -0
- package/dist/git-representation.js +16 -0
- package/dist/inbox.d.ts +132 -0
- package/dist/inbox.js +81 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +85 -0
- package/dist/internal/app-description.d.ts +14 -0
- package/dist/internal/app-description.js +98 -0
- package/dist/internal/app-release-artifact-reader.d.ts +26 -0
- package/dist/internal/app-release-artifact-reader.js +1 -0
- package/dist/internal/app-release-contract.d.ts +540 -0
- package/dist/internal/app-release-contract.js +169 -0
- package/dist/internal/app-release-schema.d.ts +1676 -0
- package/dist/internal/app-release-schema.js +386 -0
- package/dist/internal/app-release-store.d.ts +47 -0
- package/dist/internal/app-release-store.js +1 -0
- package/dist/internal/background-sync.d.ts +7 -0
- package/dist/internal/background-sync.js +10 -0
- package/dist/internal/deterministic-memory-app-release-store.d.ts +14 -0
- package/dist/internal/deterministic-memory-app-release-store.js +319 -0
- package/dist/internal/failure.d.ts +2 -0
- package/dist/internal/failure.js +9 -0
- package/dist/internal/immutable-release-material.d.ts +3 -0
- package/dist/internal/immutable-release-material.js +13 -0
- package/dist/internal/promise-app-release-store-adapter.d.ts +9 -0
- package/dist/internal/promise-app-release-store-adapter.js +65 -0
- package/dist/internal/promise-artifact-reader-adapter.d.ts +9 -0
- package/dist/internal/promise-artifact-reader-adapter.js +18 -0
- package/dist/internal/public-route-sql.d.ts +10 -0
- package/dist/internal/public-route-sql.js +239 -0
- package/dist/internal/public-website-authority.d.ts +338 -0
- package/dist/internal/public-website-authority.js +50 -0
- package/dist/internal/public-website-delivery.d.ts +59 -0
- package/dist/internal/public-website-delivery.js +232 -0
- package/dist/internal/public-website-fetch.d.ts +8 -0
- package/dist/internal/public-website-fetch.js +54 -0
- package/dist/internal/release-transition-material.d.ts +3 -0
- package/dist/internal/release-transition-material.js +11 -0
- package/dist/internal/verified-artifact-snapshot.d.ts +41 -0
- package/dist/internal/verified-artifact-snapshot.js +91 -0
- package/dist/public-route-moderation.d.ts +83 -0
- package/dist/public-route-moderation.js +236 -0
- package/dist/public-route-policy.d.ts +51 -0
- package/dist/public-route-policy.js +88 -0
- package/dist/public-routes.d.ts +34 -0
- package/dist/public-routes.js +136 -0
- package/dist/publisher.d.ts +61 -0
- package/dist/publisher.js +16 -0
- package/dist/safety-takedown.d.ts +56 -0
- package/dist/safety-takedown.js +146 -0
- package/dist/seller-closure-notice.d.ts +14 -0
- package/dist/seller-closure-notice.js +14 -0
- package/dist/seller-closure.d.ts +15 -0
- package/dist/seller-closure.js +31 -0
- package/dist/testing.d.ts +8 -0
- package/dist/testing.js +47 -0
- package/docs/api/README.md +26 -0
- package/docs/api/app-backend-binding-vocabulary.md +276 -0
- package/docs/api/app-backend-bindings.md +141 -0
- package/docs/api/app-event-facts.md +110 -0
- package/docs/api/app-method-context.md +166 -0
- package/docs/api/app-method-runtime.md +330 -0
- package/docs/api/backend.md +108 -0
- package/docs/api/background-replay.md +516 -0
- package/docs/api/cloudflare.md +1044 -0
- package/docs/api/effect.md +2429 -0
- package/docs/api/git-representation.md +56 -0
- package/docs/api/inbox.md +289 -0
- package/docs/api/index.md +2868 -0
- package/docs/api/publisher.md +147 -0
- package/docs/api/testing.md +31 -0
- package/package.json +158 -3
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { appDeclaredGillSchema, } from "@fungi.computer/gills";
|
|
2
|
+
import { appBackendBindingsSchema, appBackendBindingOperation, } from "./app-backend-bindings.js";
|
|
3
|
+
import * as Schema from "effect/Schema";
|
|
4
|
+
import { backgroundSyncAllowed } from "./internal/background-sync.js";
|
|
5
|
+
import { appApiMethodNameSchema, appApiSchema } from "./app-api.js";
|
|
6
|
+
import * as SchemaParser from "effect/SchemaParser";
|
|
7
|
+
import * as SchemaAST from "effect/SchemaAST";
|
|
8
|
+
import * as SchemaTransformation from "effect/SchemaTransformation";
|
|
9
|
+
import { appCapabilityIds, appHandlers, appRoles, appStorageScopes, } from "./app-vocabulary.js";
|
|
10
|
+
export const appRoleSchema = Schema.Literals(appRoles);
|
|
11
|
+
export const appCapabilityIdSchema = Schema.Literals(appCapabilityIds);
|
|
12
|
+
export const appHandlerSchema = Schema.Literals(appHandlers);
|
|
13
|
+
const unique = {
|
|
14
|
+
message: "App declaration entries must be unique",
|
|
15
|
+
};
|
|
16
|
+
const isUnique = (values) => new Set(values).size === values.length;
|
|
17
|
+
/** Chords the shell reserves for its leader; no App may declare them. */
|
|
18
|
+
const reservedBindings = new Set([
|
|
19
|
+
"super+k",
|
|
20
|
+
"meta+k",
|
|
21
|
+
"cmd+k",
|
|
22
|
+
"mod+k",
|
|
23
|
+
"ctrl+k",
|
|
24
|
+
"control+k",
|
|
25
|
+
]);
|
|
26
|
+
const meaningful = Schema.String.pipe(Schema.check(Schema.isMaxLength(256)), Schema.check(Schema.makeFilter((value) => value.trim().length > 0, {
|
|
27
|
+
message: "Declared command text must not be blank",
|
|
28
|
+
})));
|
|
29
|
+
const order = { order: Schema.optional(Schema.Int) };
|
|
30
|
+
const ordered = Schema.Struct(order);
|
|
31
|
+
/**
|
|
32
|
+
* One declared command: a Whistle definition's identity and projections.
|
|
33
|
+
* Runtime-only facts (availability, typed actions, slash argument grammar)
|
|
34
|
+
* are not part of the digest-covered declaration and are dropped on decode.
|
|
35
|
+
*/
|
|
36
|
+
export const appDeclaredCommandSchema = Schema.Struct({
|
|
37
|
+
id: meaningful.pipe(Schema.check(Schema.makeFilter((id) => !id.includes(":"), {
|
|
38
|
+
message: "Declared command IDs are namespace-local",
|
|
39
|
+
}))),
|
|
40
|
+
title: meaningful,
|
|
41
|
+
category: meaningful,
|
|
42
|
+
description: Schema.optional(Schema.String.pipe(Schema.check(Schema.isMaxLength(1024)))),
|
|
43
|
+
sigil: Schema.optional(meaningful.pipe(Schema.check(Schema.isMaxLength(64)))),
|
|
44
|
+
projections: Schema.optional(Schema.Struct({
|
|
45
|
+
bindings: Schema.optional(Schema.Struct({
|
|
46
|
+
...order,
|
|
47
|
+
values: Schema.Array(meaningful).pipe(Schema.check(Schema.isMaxLength(16)), Schema.check(Schema.makeFilter((values) => !values.some((binding) => reservedBindings.has(binding.trim().toLowerCase())), { message: "Apps cannot bind the reserved leader chord" }))),
|
|
48
|
+
})),
|
|
49
|
+
agent: Schema.optional(Schema.Struct({ ...order, keybind: Schema.optional(Schema.String) })),
|
|
50
|
+
help: Schema.optional(ordered),
|
|
51
|
+
menu: Schema.optional(ordered),
|
|
52
|
+
palette: Schema.optional(ordered),
|
|
53
|
+
slash: Schema.optional(Schema.Struct({
|
|
54
|
+
...order,
|
|
55
|
+
aliases: Schema.Array(meaningful).pipe(Schema.check(Schema.isMaxLength(16))),
|
|
56
|
+
})),
|
|
57
|
+
})),
|
|
58
|
+
});
|
|
59
|
+
const commandsAreUnique = (commands) => isUnique(commands.map((command) => command.id)) &&
|
|
60
|
+
isUnique(commands.flatMap((command) => command.projections?.slash?.aliases ?? []));
|
|
61
|
+
const gillsAreUnique = (gills) => isUnique(gills.map((gill) => gill.id));
|
|
62
|
+
/** Maintained grammar for one bounded key in an App's private storage. */
|
|
63
|
+
export const appStorageKeyNameSchema = Schema.String.check(Schema.isPattern(/^[a-z][a-z0-9.-]{0,63}$/));
|
|
64
|
+
const storageKeyFields = Schema.Struct({
|
|
65
|
+
key: appStorageKeyNameSchema,
|
|
66
|
+
scopes: Schema.Array(Schema.Literals(appStorageScopes)).check(Schema.isMinLength(1), Schema.makeFilter(isUnique, unique)),
|
|
67
|
+
maxBytes: Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 65536 })),
|
|
68
|
+
});
|
|
69
|
+
// Keep the nested storage contract strict when the declaration drops
|
|
70
|
+
// runtime-only command fields.
|
|
71
|
+
export const appStorageKeySchema = Schema.declareConstructor()([storageKeyFields], ([fields]) => (input, _self, options) => SchemaParser.decodeUnknownEffect(fields)(input, {
|
|
72
|
+
...options,
|
|
73
|
+
onExcessProperty: "error",
|
|
74
|
+
}), {
|
|
75
|
+
toCodecJson: () => new SchemaAST.Link(storageKeyFields.ast, SchemaTransformation.passthrough()),
|
|
76
|
+
});
|
|
77
|
+
const appCallFields = Schema.Struct({
|
|
78
|
+
appId: Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(128)),
|
|
79
|
+
methods: Schema.Array(appApiMethodNameSchema).check(Schema.isMinLength(1), Schema.isMaxLength(16), Schema.makeFilter(isUnique, unique)),
|
|
80
|
+
});
|
|
81
|
+
const appCallSchema = Schema.declareConstructor()([appCallFields], ([fields]) => (input, _self, options) => SchemaParser.decodeUnknownEffect(fields)(input, {
|
|
82
|
+
...options,
|
|
83
|
+
onExcessProperty: "error",
|
|
84
|
+
}), {
|
|
85
|
+
toCodecJson: () => new SchemaAST.Link(appCallFields.ast, SchemaTransformation.passthrough()),
|
|
86
|
+
});
|
|
87
|
+
/** The bounded, digest-covered allowlist shown when approving member App calls. */
|
|
88
|
+
export const appCallsSchema = Schema.Array(appCallSchema).check(Schema.isMinLength(1), Schema.isMaxLength(16), Schema.makeFilter((calls) => isUnique(calls.map((call) => call.appId)), unique));
|
|
89
|
+
/** Boundary codec for the digest-covered {@link AppDeclaration}. */
|
|
90
|
+
export const appDeclarationSchema = Schema.Struct({
|
|
91
|
+
api: Schema.optionalKey(appApiSchema),
|
|
92
|
+
displayName: Schema.optional(Schema.String.pipe(Schema.check(Schema.isMinLength(1)), Schema.check(Schema.isMaxLength(64)))),
|
|
93
|
+
capabilities: Schema.Array(appCapabilityIdSchema).pipe(Schema.check(Schema.makeFilter(isUnique, unique))),
|
|
94
|
+
calls: Schema.optionalKey(appCallsSchema),
|
|
95
|
+
guidance: Schema.optional(Schema.String.pipe(Schema.check(Schema.isMinLength(1)), Schema.check(Schema.isMaxLength(12_000)))),
|
|
96
|
+
handlers: Schema.Array(appHandlerSchema).pipe(Schema.check(Schema.makeFilter(isUnique, unique))),
|
|
97
|
+
receives: Schema.optionalKey(Schema.Array(Schema.Literal("git.push")).check(Schema.isMaxLength(1), Schema.makeFilter(isUnique, unique))),
|
|
98
|
+
icon: Schema.optionalKey(Schema.String.pipe(Schema.check(Schema.isMinLength(1)), Schema.check(Schema.isMaxLength(80)))),
|
|
99
|
+
roles: Schema.Array(appRoleSchema).pipe(Schema.check(Schema.makeFilter(isUnique, unique))),
|
|
100
|
+
storage: Schema.optionalKey(Schema.Array(appStorageKeySchema).check(Schema.isMaxLength(16), Schema.makeFilter((keys) => isUnique(keys.map((entry) => entry.key)), unique))),
|
|
101
|
+
commands: Schema.optional(Schema.Array(appDeclaredCommandSchema).pipe(Schema.check(Schema.isMaxLength(64)), Schema.check(Schema.makeFilter(commandsAreUnique, {
|
|
102
|
+
message: "Declared command IDs and slash aliases must be unique",
|
|
103
|
+
})))),
|
|
104
|
+
gills: Schema.optional(Schema.Array(appDeclaredGillSchema).pipe(Schema.check(Schema.isMaxLength(16)), Schema.check(Schema.makeFilter(gillsAreUnique, {
|
|
105
|
+
message: "Declared Gill IDs must be unique",
|
|
106
|
+
})))),
|
|
107
|
+
backend: Schema.optional(Schema.Struct({
|
|
108
|
+
bindings: appBackendBindingsSchema,
|
|
109
|
+
sync: Schema.optionalKey(Schema.Struct({ method: appApiMethodNameSchema })),
|
|
110
|
+
})),
|
|
111
|
+
}).pipe(Schema.check(Schema.makeFilter((declaration) => declaration.calls === undefined ||
|
|
112
|
+
declaration.capabilities.includes("apps.call"), { message: "App calls require apps.call" })), Schema.check(Schema.makeFilter((declaration) => declaration.backend?.bindings.every((binding) => binding.name !== "BACKGROUND") ?? true, { message: "BACKGROUND is host-minted and cannot be declared by an App" })), Schema.check(Schema.makeFilter((declaration) => backgroundSyncAllowed(declaration.api, declaration.backend?.sync), {
|
|
113
|
+
message: "Background continuation must be a declared write covering all write requirements",
|
|
114
|
+
})), Schema.check(Schema.makeFilter((declaration) => !(declaration.receives?.includes("git.push") ?? false) ||
|
|
115
|
+
declaration.capabilities.includes("git.repository"), { message: "Git push reception requires git.repository" })), Schema.check(Schema.makeFilter((declaration) => declaration.backend?.bindings.every((binding) => binding.operations.every((name) => appBackendBindingOperation(binding.name, name)?.requires.every((capability) => declaration.capabilities.includes(capability)) === true)) ?? true, { message: "Backend bindings require declared App capabilities" })), Schema.check(Schema.makeFilter((declaration) => !declaration.backend?.bindings.some((binding) => binding.name === "APP_STORAGE") || (declaration.storage?.length ?? 0) > 0, { message: "App storage bindings require declared keys" })), Schema.check(Schema.makeFilter((declaration) => Object.values(declaration.api?.methods ?? {}).every((method) => method.requires.every((capability) => declaration.capabilities.includes(capability))), { message: "App API requires capabilities outside the App declaration" })));
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
/** Canonical EventSink body; installation identity is supplied by the host. */
|
|
3
|
+
export declare const appEventInputSchema: z.ZodObject<{
|
|
4
|
+
type: z.ZodString;
|
|
5
|
+
payload: z.ZodObject<{
|
|
6
|
+
type: z.ZodString;
|
|
7
|
+
timestamp: z.ZodString;
|
|
8
|
+
data: z.ZodJSONSchema;
|
|
9
|
+
}, z.core.$strict>;
|
|
10
|
+
eventId: z.ZodString;
|
|
11
|
+
}, z.core.$strict>;
|
|
12
|
+
/** Parsed EventSink input before the host attaches installation identity. */
|
|
13
|
+
export type AppEventInput = z.output<typeof appEventInputSchema>;
|
|
14
|
+
/** Host-attested repository origin of an accepted push fact. */
|
|
15
|
+
export type AppEventGitSource = NonNullable<AppEventFact["gitSource"]>;
|
|
16
|
+
/** Saved host fact delivered to the shared Workflows event router. */
|
|
17
|
+
export declare const appEventFactSchema: z.ZodObject<{
|
|
18
|
+
type: z.ZodString;
|
|
19
|
+
payload: z.ZodObject<{
|
|
20
|
+
type: z.ZodString;
|
|
21
|
+
timestamp: z.ZodString;
|
|
22
|
+
data: z.ZodJSONSchema;
|
|
23
|
+
}, z.core.$strict>;
|
|
24
|
+
eventId: z.ZodString;
|
|
25
|
+
installationId: z.ZodString;
|
|
26
|
+
appId: z.ZodString;
|
|
27
|
+
releaseId: z.ZodString;
|
|
28
|
+
gitSource: z.ZodOptional<z.ZodObject<{
|
|
29
|
+
eventId: z.ZodString;
|
|
30
|
+
repository: z.ZodString;
|
|
31
|
+
updates: z.ZodArray<z.ZodObject<{
|
|
32
|
+
ref: z.ZodString;
|
|
33
|
+
before: z.ZodString;
|
|
34
|
+
after: z.ZodString;
|
|
35
|
+
}, z.core.$strict>>;
|
|
36
|
+
providerRepository: z.ZodString;
|
|
37
|
+
}, z.core.$strict>>;
|
|
38
|
+
}, z.core.$strict>;
|
|
39
|
+
/** Canonical saved event fact for delivery to a host event router. */
|
|
40
|
+
export type AppEventFact = z.output<typeof appEventFactSchema>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import { gitPushEventIdSchema } from "./git-representation.js";
|
|
3
|
+
import { gitRepositoryNameSchema, gitRefUpdateSchema, } from "./git-representation.js";
|
|
4
|
+
const identity = z.string().min(1).max(256);
|
|
5
|
+
/** Canonical EventSink body; installation identity is supplied by the host. */
|
|
6
|
+
export const appEventInputSchema = z.strictObject({
|
|
7
|
+
type: z.string().min(1).max(128),
|
|
8
|
+
payload: z.strictObject({
|
|
9
|
+
type: z.string().min(1).max(128),
|
|
10
|
+
timestamp: z.string().min(1).max(256),
|
|
11
|
+
data: z.json(),
|
|
12
|
+
}),
|
|
13
|
+
eventId: identity,
|
|
14
|
+
});
|
|
15
|
+
/** Host-accepted push origin, distinct from event payload authority. */
|
|
16
|
+
const appEventGitSourceSchema = z.strictObject({
|
|
17
|
+
eventId: gitPushEventIdSchema,
|
|
18
|
+
repository: gitRepositoryNameSchema,
|
|
19
|
+
updates: z.array(gitRefUpdateSchema).min(1).max(256),
|
|
20
|
+
providerRepository: gitRepositoryNameSchema,
|
|
21
|
+
});
|
|
22
|
+
/** Saved host fact delivered to the shared Workflows event router. */
|
|
23
|
+
export const appEventFactSchema = appEventInputSchema.extend({
|
|
24
|
+
installationId: identity,
|
|
25
|
+
appId: identity,
|
|
26
|
+
releaseId: identity,
|
|
27
|
+
gitSource: appEventGitSourceSchema.optional(),
|
|
28
|
+
});
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import * as Schema from "effect/Schema";
|
|
2
|
+
export type { AppMethodCallContext, HostActor, WorkflowAttestation, } from "./app-method-runtime.js";
|
|
3
|
+
/** Host attribution, including paired sealed enqueue origin, uses the sole lightweight caller grammar. */
|
|
4
|
+
export declare const appMethodCallContextSchema: Schema.declareConstructor<{
|
|
5
|
+
computerId: string | null;
|
|
6
|
+
actor: {
|
|
7
|
+
kind: "member";
|
|
8
|
+
memberId: string;
|
|
9
|
+
} | {
|
|
10
|
+
kind: "agent";
|
|
11
|
+
agentId: string;
|
|
12
|
+
} | {
|
|
13
|
+
kind: "team-key";
|
|
14
|
+
keyId: string;
|
|
15
|
+
};
|
|
16
|
+
backgroundLane?: string | undefined;
|
|
17
|
+
backgroundOrigin?: {
|
|
18
|
+
method: string;
|
|
19
|
+
seal: string;
|
|
20
|
+
} | undefined;
|
|
21
|
+
role?: "member" | "owner" | undefined;
|
|
22
|
+
soleMember?: true | undefined;
|
|
23
|
+
workflow?: {
|
|
24
|
+
workflowId: string;
|
|
25
|
+
runId: string;
|
|
26
|
+
nodeId: string;
|
|
27
|
+
armedBy: {
|
|
28
|
+
kind: "member";
|
|
29
|
+
memberId: string;
|
|
30
|
+
} | {
|
|
31
|
+
kind: "agent";
|
|
32
|
+
agentId: string;
|
|
33
|
+
} | {
|
|
34
|
+
kind: "team-key";
|
|
35
|
+
keyId: string;
|
|
36
|
+
};
|
|
37
|
+
} | undefined;
|
|
38
|
+
idempotencyKey?: string | undefined;
|
|
39
|
+
}, {
|
|
40
|
+
computerId: string | null;
|
|
41
|
+
actor: {
|
|
42
|
+
kind: "member";
|
|
43
|
+
memberId: string;
|
|
44
|
+
} | {
|
|
45
|
+
kind: "agent";
|
|
46
|
+
agentId: string;
|
|
47
|
+
} | {
|
|
48
|
+
kind: "team-key";
|
|
49
|
+
keyId: string;
|
|
50
|
+
};
|
|
51
|
+
backgroundLane?: string | undefined;
|
|
52
|
+
backgroundOrigin?: {
|
|
53
|
+
method: string;
|
|
54
|
+
seal: string;
|
|
55
|
+
} | undefined;
|
|
56
|
+
role?: "member" | "owner" | undefined;
|
|
57
|
+
soleMember?: true | undefined;
|
|
58
|
+
workflow?: {
|
|
59
|
+
workflowId: string;
|
|
60
|
+
runId: string;
|
|
61
|
+
nodeId: string;
|
|
62
|
+
armedBy: {
|
|
63
|
+
kind: "member";
|
|
64
|
+
memberId: string;
|
|
65
|
+
} | {
|
|
66
|
+
kind: "agent";
|
|
67
|
+
agentId: string;
|
|
68
|
+
} | {
|
|
69
|
+
kind: "team-key";
|
|
70
|
+
keyId: string;
|
|
71
|
+
};
|
|
72
|
+
} | undefined;
|
|
73
|
+
idempotencyKey?: string | undefined;
|
|
74
|
+
}, readonly [], {
|
|
75
|
+
computerId: string | null;
|
|
76
|
+
actor: {
|
|
77
|
+
kind: "member";
|
|
78
|
+
memberId: string;
|
|
79
|
+
} | {
|
|
80
|
+
kind: "agent";
|
|
81
|
+
agentId: string;
|
|
82
|
+
} | {
|
|
83
|
+
kind: "team-key";
|
|
84
|
+
keyId: string;
|
|
85
|
+
};
|
|
86
|
+
backgroundLane?: string | undefined;
|
|
87
|
+
backgroundOrigin?: {
|
|
88
|
+
method: string;
|
|
89
|
+
seal: string;
|
|
90
|
+
} | undefined;
|
|
91
|
+
role?: "member" | "owner" | undefined;
|
|
92
|
+
soleMember?: true | undefined;
|
|
93
|
+
workflow?: {
|
|
94
|
+
workflowId: string;
|
|
95
|
+
runId: string;
|
|
96
|
+
nodeId: string;
|
|
97
|
+
armedBy: {
|
|
98
|
+
kind: "member";
|
|
99
|
+
memberId: string;
|
|
100
|
+
} | {
|
|
101
|
+
kind: "agent";
|
|
102
|
+
agentId: string;
|
|
103
|
+
} | {
|
|
104
|
+
kind: "team-key";
|
|
105
|
+
keyId: string;
|
|
106
|
+
};
|
|
107
|
+
} | undefined;
|
|
108
|
+
idempotencyKey?: string | undefined;
|
|
109
|
+
}>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as Schema from "effect/Schema";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as SchemaIssue from "effect/SchemaIssue";
|
|
4
|
+
import { parseAppMethodCallContext, } from "./app-method-runtime.js";
|
|
5
|
+
/** Host attribution, including paired sealed enqueue origin, uses the sole lightweight caller grammar. */
|
|
6
|
+
export const appMethodCallContextSchema = Schema.declareConstructor()([], () => (value, ast, options) => Effect.try({
|
|
7
|
+
try: () => parseAppMethodCallContext(value, options.onExcessProperty === "error"),
|
|
8
|
+
catch: () => new SchemaIssue.InvalidType(ast, value, options),
|
|
9
|
+
}));
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export { backgroundSyncAllowed } from "./internal/background-sync.js";
|
|
2
|
+
import * as z from "zod/mini";
|
|
3
|
+
/** Reserved JavaScript, RPC and lifecycle names are never App method names. */
|
|
4
|
+
export declare const appApiReservedMethodNames: Readonly<{
|
|
5
|
+
readonly intrinsic: readonly ["constructor", "toString", "valueOf", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "toLocaleString"];
|
|
6
|
+
readonly rpc: readonly ["then", "dup", "connect", "id", "name"];
|
|
7
|
+
readonly instance: readonly ["ctx", "env"];
|
|
8
|
+
readonly lifecycle: readonly ["gill", "onGitPush", "syncRefused", "authorizeGitPush", "fetch", "alarm", "queue", "scheduled", "tail", "trace", "email", "test", "tailStream", "webSocketMessage", "webSocketClose", "webSocketError"];
|
|
9
|
+
}>;
|
|
10
|
+
/** Actor identity attested by the host, shared by calls and Workflow arming. */
|
|
11
|
+
export declare const hostActorSchema: z.ZodMiniDiscriminatedUnion<[z.ZodMiniObject<{
|
|
12
|
+
kind: z.ZodMiniLiteral<"member">;
|
|
13
|
+
memberId: z.ZodMiniString<string>;
|
|
14
|
+
}, z.core.$strip>, z.ZodMiniObject<{
|
|
15
|
+
kind: z.ZodMiniLiteral<"agent">;
|
|
16
|
+
agentId: z.ZodMiniString<string>;
|
|
17
|
+
}, z.core.$strip>, z.ZodMiniObject<{
|
|
18
|
+
kind: z.ZodMiniLiteral<"team-key">;
|
|
19
|
+
keyId: z.ZodMiniString<string>;
|
|
20
|
+
}, z.core.$strip>], "kind">;
|
|
21
|
+
/** The host's verified member, Agent or Team-key identity. */
|
|
22
|
+
export type HostActor = z.output<typeof hostActorSchema>;
|
|
23
|
+
/** Host-held Workflow attribution, delivered separately from guest input. */
|
|
24
|
+
export declare const workflowAttestationSchema: z.ZodMiniObject<{
|
|
25
|
+
workflowId: z.ZodMiniString<string>;
|
|
26
|
+
runId: z.ZodMiniString<string>;
|
|
27
|
+
nodeId: z.ZodMiniString<string>;
|
|
28
|
+
armedBy: z.ZodMiniDiscriminatedUnion<[z.ZodMiniObject<{
|
|
29
|
+
kind: z.ZodMiniLiteral<"member">;
|
|
30
|
+
memberId: z.ZodMiniString<string>;
|
|
31
|
+
}, z.core.$strip>, z.ZodMiniObject<{
|
|
32
|
+
kind: z.ZodMiniLiteral<"agent">;
|
|
33
|
+
agentId: z.ZodMiniString<string>;
|
|
34
|
+
}, z.core.$strip>, z.ZodMiniObject<{
|
|
35
|
+
kind: z.ZodMiniLiteral<"team-key">;
|
|
36
|
+
keyId: z.ZodMiniString<string>;
|
|
37
|
+
}, z.core.$strip>], "kind">;
|
|
38
|
+
}, z.core.$strip>;
|
|
39
|
+
/** Workflow attribution shared by invocation frames and Git writers. */
|
|
40
|
+
export type WorkflowAttestation = z.output<typeof workflowAttestationSchema>;
|
|
41
|
+
/** Sole grammar for the host-minted Computer pin and actor attribution. */
|
|
42
|
+
export declare const appMethodCallerSchema: z.ZodMiniObject<{
|
|
43
|
+
computerId: z.ZodMiniNullable<z.ZodMiniString<string>>;
|
|
44
|
+
actor: z.ZodMiniDiscriminatedUnion<[z.ZodMiniObject<{
|
|
45
|
+
kind: z.ZodMiniLiteral<"member">;
|
|
46
|
+
memberId: z.ZodMiniString<string>;
|
|
47
|
+
}, z.core.$strip>, z.ZodMiniObject<{
|
|
48
|
+
kind: z.ZodMiniLiteral<"agent">;
|
|
49
|
+
agentId: z.ZodMiniString<string>;
|
|
50
|
+
}, z.core.$strip>, z.ZodMiniObject<{
|
|
51
|
+
kind: z.ZodMiniLiteral<"team-key">;
|
|
52
|
+
keyId: z.ZodMiniString<string>;
|
|
53
|
+
}, z.core.$strip>], "kind">;
|
|
54
|
+
backgroundLane: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
55
|
+
backgroundOrigin: z.ZodMiniOptional<z.ZodMiniObject<{
|
|
56
|
+
method: z.ZodMiniString<string>;
|
|
57
|
+
seal: z.ZodMiniString<string>;
|
|
58
|
+
}, z.core.$strict>>;
|
|
59
|
+
role: z.ZodMiniOptional<z.ZodMiniEnum<{
|
|
60
|
+
member: "member";
|
|
61
|
+
owner: "owner";
|
|
62
|
+
}>>;
|
|
63
|
+
soleMember: z.ZodMiniOptional<z.ZodMiniLiteral<true>>;
|
|
64
|
+
workflow: z.ZodMiniOptional<z.ZodMiniObject<{
|
|
65
|
+
workflowId: z.ZodMiniString<string>;
|
|
66
|
+
runId: z.ZodMiniString<string>;
|
|
67
|
+
nodeId: z.ZodMiniString<string>;
|
|
68
|
+
armedBy: z.ZodMiniDiscriminatedUnion<[z.ZodMiniObject<{
|
|
69
|
+
kind: z.ZodMiniLiteral<"member">;
|
|
70
|
+
memberId: z.ZodMiniString<string>;
|
|
71
|
+
}, z.core.$strip>, z.ZodMiniObject<{
|
|
72
|
+
kind: z.ZodMiniLiteral<"agent">;
|
|
73
|
+
agentId: z.ZodMiniString<string>;
|
|
74
|
+
}, z.core.$strip>, z.ZodMiniObject<{
|
|
75
|
+
kind: z.ZodMiniLiteral<"team-key">;
|
|
76
|
+
keyId: z.ZodMiniString<string>;
|
|
77
|
+
}, z.core.$strip>], "kind">;
|
|
78
|
+
}, z.core.$strip>>;
|
|
79
|
+
idempotencyKey: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
80
|
+
}, z.core.$strip>;
|
|
81
|
+
/** Trusted attribution delivered separately from App method arguments. */
|
|
82
|
+
export type AppMethodCallContext = z.output<typeof appMethodCallerSchema>;
|
|
83
|
+
/** Decode trusted host attribution through the sole runtime-neutral grammar. */
|
|
84
|
+
export declare const parseAppMethodCallContext: (value: unknown, rejectUnknownKeys?: boolean) => AppMethodCallContext;
|
|
85
|
+
/** Shared domain grammar for generated RPC method names. */
|
|
86
|
+
export declare const isAppApiMethodName: (name: string) => boolean;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
export { backgroundSyncAllowed } from "./internal/background-sync.js";
|
|
2
|
+
import * as z from "zod/mini";
|
|
3
|
+
/** Reserved JavaScript, RPC and lifecycle names are never App method names. */
|
|
4
|
+
export const appApiReservedMethodNames = Object.freeze({
|
|
5
|
+
intrinsic: [
|
|
6
|
+
"constructor",
|
|
7
|
+
"toString",
|
|
8
|
+
"valueOf",
|
|
9
|
+
"hasOwnProperty",
|
|
10
|
+
"isPrototypeOf",
|
|
11
|
+
"propertyIsEnumerable",
|
|
12
|
+
"toLocaleString",
|
|
13
|
+
],
|
|
14
|
+
rpc: ["then", "dup", "connect", "id", "name"],
|
|
15
|
+
instance: ["ctx", "env"],
|
|
16
|
+
lifecycle: [
|
|
17
|
+
"gill",
|
|
18
|
+
"onGitPush",
|
|
19
|
+
"syncRefused",
|
|
20
|
+
"authorizeGitPush",
|
|
21
|
+
"fetch",
|
|
22
|
+
"alarm",
|
|
23
|
+
"queue",
|
|
24
|
+
"scheduled",
|
|
25
|
+
"tail",
|
|
26
|
+
"trace",
|
|
27
|
+
"email",
|
|
28
|
+
"test",
|
|
29
|
+
"tailStream",
|
|
30
|
+
"webSocketMessage",
|
|
31
|
+
"webSocketClose",
|
|
32
|
+
"webSocketError",
|
|
33
|
+
],
|
|
34
|
+
});
|
|
35
|
+
/** Sole caller grammar shared by the lightweight runtime and Effect adapter. */
|
|
36
|
+
const identity = z.string().check(z.minLength(1), z.maxLength(256));
|
|
37
|
+
const memberActor = z.object({ kind: z.literal("member"), memberId: identity });
|
|
38
|
+
const agentActor = z.object({ kind: z.literal("agent"), agentId: identity });
|
|
39
|
+
const keyActor = z.object({ kind: z.literal("team-key"), keyId: identity });
|
|
40
|
+
/** Actor identity attested by the host, shared by calls and Workflow arming. */
|
|
41
|
+
export const hostActorSchema = z.discriminatedUnion("kind", [
|
|
42
|
+
memberActor,
|
|
43
|
+
agentActor,
|
|
44
|
+
keyActor,
|
|
45
|
+
]);
|
|
46
|
+
/** Host-held Workflow attribution, delivered separately from guest input. */
|
|
47
|
+
export const workflowAttestationSchema = z.object({
|
|
48
|
+
/** Published Workflow selected by the run owner. */
|
|
49
|
+
workflowId: identity,
|
|
50
|
+
/** Run identity held by the run owner. */
|
|
51
|
+
runId: identity,
|
|
52
|
+
/** Node selected by the stored published Workflow. */
|
|
53
|
+
nodeId: identity,
|
|
54
|
+
/** Actor that armed the run, re-admitted by the host at dispatch. */
|
|
55
|
+
armedBy: hostActorSchema,
|
|
56
|
+
});
|
|
57
|
+
// Keep the single origin grammar in the lightweight caller module. The host-side
|
|
58
|
+
// background-replay entry exposes this same schema without a runtime import cycle.
|
|
59
|
+
const backgroundOrigin = z.strictObject({
|
|
60
|
+
method: z.string().check(z.refine((name) => isAppApiMethodName(name))),
|
|
61
|
+
seal: z.string().check(z.regex(/^[A-Za-z0-9_-]{43}$/u)),
|
|
62
|
+
});
|
|
63
|
+
/** Sole grammar for the host-minted Computer pin and actor attribution. */
|
|
64
|
+
export const appMethodCallerSchema = z.object({
|
|
65
|
+
computerId: z.nullable(identity),
|
|
66
|
+
actor: hostActorSchema,
|
|
67
|
+
/** Host-attested enqueue eligibility for this ordinary write; absent otherwise. */
|
|
68
|
+
backgroundLane: z.optional(z.string().check(z.minLength(8), z.maxLength(263))),
|
|
69
|
+
/** Host-sealed foreground method for durable jobs; paired with backgroundLane. */
|
|
70
|
+
backgroundOrigin: z.optional(backgroundOrigin),
|
|
71
|
+
/** Team role attested by the host, never selected by App method input. */
|
|
72
|
+
role: z.optional(z.enum(["owner", "member"])),
|
|
73
|
+
/**
|
|
74
|
+
* The host attests that this member is currently the Team's only member.
|
|
75
|
+
* Present only on a member's own call; never on an Agent, Team key or
|
|
76
|
+
* Workflow node, and never selected by App method input.
|
|
77
|
+
*/
|
|
78
|
+
soleMember: z.optional(z.literal(true)),
|
|
79
|
+
/** Present only on a Workflow node invocation admitted by its host. */
|
|
80
|
+
workflow: z.optional(workflowAttestationSchema),
|
|
81
|
+
/**
|
|
82
|
+
* The host's stable key for this logical call (for example one Workflow
|
|
83
|
+
* node), the same on every retry. A method with side effects uses it to do
|
|
84
|
+
* its work at most once. Absent when the caller supplies its own request id.
|
|
85
|
+
*/
|
|
86
|
+
idempotencyKey: z.optional(z.string().check(z.minLength(1), z.maxLength(128))),
|
|
87
|
+
});
|
|
88
|
+
const strictActor = z.discriminatedUnion("kind", [
|
|
89
|
+
z.strictObject(memberActor.shape),
|
|
90
|
+
z.strictObject(agentActor.shape),
|
|
91
|
+
z.strictObject(keyActor.shape),
|
|
92
|
+
]);
|
|
93
|
+
const strictCaller = z.strictObject({
|
|
94
|
+
...appMethodCallerSchema.shape,
|
|
95
|
+
actor: strictActor,
|
|
96
|
+
workflow: z.optional(z.strictObject({
|
|
97
|
+
...workflowAttestationSchema.shape,
|
|
98
|
+
armedBy: strictActor,
|
|
99
|
+
})),
|
|
100
|
+
});
|
|
101
|
+
/** Decode trusted host attribution through the sole runtime-neutral grammar. */
|
|
102
|
+
export const parseAppMethodCallContext = (value, rejectUnknownKeys = false) => validateCaller((rejectUnknownKeys ? strictCaller : appMethodCallerSchema).parse(value));
|
|
103
|
+
function validateCaller(context) {
|
|
104
|
+
if (context.soleMember !== undefined &&
|
|
105
|
+
(context.actor.kind !== "member" || context.workflow !== undefined))
|
|
106
|
+
throw new Error("Only a member's own call can be its Team's sole member");
|
|
107
|
+
if ((context.backgroundLane === undefined) !==
|
|
108
|
+
(context.backgroundOrigin === undefined))
|
|
109
|
+
throw new Error("Background lane and origin must be attested together");
|
|
110
|
+
if (context.backgroundLane !== undefined &&
|
|
111
|
+
(context.actor.kind !== "member" ||
|
|
112
|
+
context.workflow !== undefined ||
|
|
113
|
+
context.backgroundLane !== `member:${context.actor.memberId}`))
|
|
114
|
+
throw new Error("Background lane must name an ordinary member");
|
|
115
|
+
return context;
|
|
116
|
+
}
|
|
117
|
+
/** Shared domain grammar for generated RPC method names. */
|
|
118
|
+
export const isAppApiMethodName = (name) => name.length <= 80 &&
|
|
119
|
+
/^[A-Za-z][A-Za-z0-9_]*(?:\.[A-Za-z][A-Za-z0-9_]*)*$/.test(name) &&
|
|
120
|
+
!Object.values(appApiReservedMethodNames)
|
|
121
|
+
.flat()
|
|
122
|
+
.some((reserved) => reserved === name);
|