@pylonsync/functions 0.12.0 → 0.13.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/dist/index.d.ts +1 -1
- package/dist/production-build.d.ts +1 -1
- package/dist/types.d.ts +38 -5
- package/package.json +1 -1
- package/src/index.ts +2 -0
- package/src/production-build.ts +17 -4
- package/src/runtime-shard-ticket.test.ts +87 -37
- package/src/runtime.ts +19 -0
- package/src/types.ts +44 -5
package/dist/index.d.ts
CHANGED
|
@@ -26,4 +26,4 @@ export type { WorkflowDefinition, WorkflowRun, WorkflowRunRequest, WorkflowRunne
|
|
|
26
26
|
export { resetDb, installTestIsolation } from "./testing";
|
|
27
27
|
export { slugifyName, availableSlug } from "./slugify";
|
|
28
28
|
export type { SsrResponse, SsrCookieOptions, SsrMetadata, Sitemap, SitemapEntry, Robots, RobotsRule, } from "./ssr-runtime";
|
|
29
|
-
export type { QueryCtx, MutationCtx, ActionCtx, DbReader, DbWriter, Stream, Scheduler, AuthInfo, AuthMode, AuthRequirement, FnDefinition, Validator, AnyValidator, ValidatorSchema, InferValidator, InferArgs, RequireMember, RequireMemberOptions, Shards, MemberRow, Workflows, VectorSearchQuery, VectorSearchResult, SearchResult, PaginationResult, Llm, LlmMessage, LlmContentBlock, LlmTool, LlmCompleteRequest, LlmCompleteResponse, LlmStreamEvent, Rooms, Domains, TenantDomainResult, TenantDomainDns, DomainAvailability, DomainContact, RegisterDomainOptions, RegisteredDomainResult, } from "./types";
|
|
29
|
+
export type { QueryCtx, MutationCtx, ActionCtx, DbReader, DbWriter, Stream, Scheduler, AuthInfo, AuthMode, AuthRequirement, FnDefinition, Validator, AnyValidator, ValidatorSchema, InferValidator, InferArgs, RequireMember, RequireMemberOptions, Shards, ShardsReader, ShardInfo, MemberRow, Workflows, VectorSearchQuery, VectorSearchResult, SearchResult, PaginationResult, Llm, LlmMessage, LlmContentBlock, LlmTool, LlmCompleteRequest, LlmCompleteResponse, LlmStreamEvent, Rooms, Domains, TenantDomainResult, TenantDomainDns, DomainAvailability, DomainContact, RegisterDomainOptions, RegisteredDomainResult, } from "./types";
|
|
@@ -24,7 +24,7 @@ export interface ProductionBuildResult {
|
|
|
24
24
|
export declare function buildProduction(opts: ProductionBuildOptions): Promise<ProductionBuildResult>;
|
|
25
25
|
/** The artifact-relative path for one `build.include` entry. Throws when the
|
|
26
26
|
* entry cannot be copied into the artifact as-is. */
|
|
27
|
-
export declare function checkInclude(path: any, cwd: string, outDir: string, inc: string, src: string): string;
|
|
27
|
+
export declare function checkInclude(path: any, cwd: string, outDir: string, inc: string, src: string, label?: string): string;
|
|
28
28
|
/**
|
|
29
29
|
* Every app module the SSR runtime can import: files named by convention
|
|
30
30
|
* anywhere under the app dir, plus every component and layout the manifest
|
package/dist/types.d.ts
CHANGED
|
@@ -766,6 +766,39 @@ export interface Shards {
|
|
|
766
766
|
claims?: Record<string, unknown>;
|
|
767
767
|
ttlSecs?: number;
|
|
768
768
|
}): Promise<string>;
|
|
769
|
+
/**
|
|
770
|
+
* Start shard `shardId` of a kind declared with `shard({...})` in app.ts.
|
|
771
|
+
* `params` reach the module's `init`. Actions only: a mutation's
|
|
772
|
+
* rollback cannot undo it.
|
|
773
|
+
*
|
|
774
|
+
* Throws `SHARD_EXISTS` when the id is running, `SHARD_LIMIT_REACHED`
|
|
775
|
+
* at the kind's `maxInstances`, `SHARD_KIND_NOT_FOUND`,
|
|
776
|
+
* `SHARD_ID_INVALID`, or `SHARD_INIT_FAILED` when `init` refuses.
|
|
777
|
+
*/
|
|
778
|
+
create(kind: string, shardId: string, params?: unknown): Promise<ShardInfo>;
|
|
779
|
+
/** Stop a shard and close its subscribers' connections. Resolves to
|
|
780
|
+
* `false` when no shard has that id. Actions only. */
|
|
781
|
+
stop(shardId: string): Promise<boolean>;
|
|
782
|
+
/** A running shard, or `null`. */
|
|
783
|
+
get(shardId: string): Promise<ShardInfo | null>;
|
|
784
|
+
/** Every running shard. */
|
|
785
|
+
list(): Promise<ShardInfo[]>;
|
|
786
|
+
}
|
|
787
|
+
/** `ctx.shards` in a query or mutation: tickets and reads, no start or stop. */
|
|
788
|
+
export type ShardsReader = Pick<Shards, "ticket" | "get" | "list">;
|
|
789
|
+
/** A running shard, from `ctx.shards.create`, `get`, or `list`. */
|
|
790
|
+
export interface ShardInfo {
|
|
791
|
+
id: string;
|
|
792
|
+
/** The shard kind's name. */
|
|
793
|
+
kind: string;
|
|
794
|
+
/** Ticks run so far. */
|
|
795
|
+
tick: number;
|
|
796
|
+
subscribers: number;
|
|
797
|
+
/** False once the shard stopped (finished, idle, or failed) and before
|
|
798
|
+
* the host removes it. */
|
|
799
|
+
running: boolean;
|
|
800
|
+
/** Why the module stopped, when it trapped. */
|
|
801
|
+
error?: string;
|
|
769
802
|
}
|
|
770
803
|
/** Context for query handlers (read-only).
|
|
771
804
|
*
|
|
@@ -792,8 +825,8 @@ export interface QueryCtx<R extends AuthRequirement = "optional"> {
|
|
|
792
825
|
requireMember: RequireMember;
|
|
793
826
|
/** Signed file-download URLs — see {@link Files}. */
|
|
794
827
|
files: Files;
|
|
795
|
-
/** Shard tickets — see {@link Shards}. */
|
|
796
|
-
shards:
|
|
828
|
+
/** Shard tickets and reads — see {@link Shards}. */
|
|
829
|
+
shards: ShardsReader;
|
|
797
830
|
/**
|
|
798
831
|
* Fires when the host cancels this call (idle timeout exceeded).
|
|
799
832
|
* Thread it into `fetch(url, { signal: ctx.signal })` or SDK calls so
|
|
@@ -821,8 +854,8 @@ export interface MutationCtx<R extends AuthRequirement = "optional"> {
|
|
|
821
854
|
workflows: Workflows;
|
|
822
855
|
/** Signed file-download URLs — see {@link Files}. */
|
|
823
856
|
files: Files;
|
|
824
|
-
/** Shard tickets — see {@link Shards}. */
|
|
825
|
-
shards:
|
|
857
|
+
/** Shard tickets and reads — see {@link Shards}. */
|
|
858
|
+
shards: ShardsReader;
|
|
826
859
|
/** Create a typed error that triggers rollback. */
|
|
827
860
|
error(code: string, message: string): Error;
|
|
828
861
|
/** Assert org membership (optionally a role) — see {@link RequireMember}. */
|
|
@@ -992,7 +1025,7 @@ export interface ActionCtx<R extends AuthRequirement = "optional"> {
|
|
|
992
1025
|
env: Record<string, string>;
|
|
993
1026
|
/** Signed file-download URLs — see {@link Files}. */
|
|
994
1027
|
files: Files;
|
|
995
|
-
/** Shard tickets — see {@link Shards}. */
|
|
1028
|
+
/** Shard tickets, reads, and start/stop — see {@link Shards}. */
|
|
996
1029
|
shards: Shards;
|
|
997
1030
|
/** Run a registered query within its own read transaction. */
|
|
998
1031
|
runQuery<T = unknown>(fnName: string, args: Record<string, unknown>): Promise<T>;
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
package/src/production-build.ts
CHANGED
|
@@ -234,6 +234,18 @@ export async function buildProduction(
|
|
|
234
234
|
if (!fs.existsSync(src)) throw new Error(`build.include: "${inc}" does not exist`);
|
|
235
235
|
fs.cpSync(src, path.join(outDir, rel), { recursive: true, dereference: true });
|
|
236
236
|
}
|
|
237
|
+
// Shard modules, at the paths app.ts gives, which the runtime reads at boot.
|
|
238
|
+
for (const def of (manifest.shards ?? []) as Array<{ name: string; wasm: string }>) {
|
|
239
|
+
const src = path.resolve(cwd, def.wasm);
|
|
240
|
+
const rel = checkInclude(path, cwd, outDir, def.wasm, src, `shard "${def.name}" wasm`);
|
|
241
|
+
if (!fs.existsSync(src)) {
|
|
242
|
+
throw new Error(
|
|
243
|
+
`shard "${def.name}": ${def.wasm} does not exist; run \`pylon shards build\` first`,
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
fs.mkdirSync(path.dirname(path.join(outDir, rel)), { recursive: true });
|
|
247
|
+
fs.copyFileSync(src, path.join(outDir, rel));
|
|
248
|
+
}
|
|
237
249
|
|
|
238
250
|
fs.writeFileSync(path.join(outDir, "pylon.manifest.json"), manifestRaw, "utf8");
|
|
239
251
|
const pkg = JSON.parse(fs.readFileSync(path.join(here, "..", "package.json"), "utf8"));
|
|
@@ -317,21 +329,22 @@ export function checkInclude(
|
|
|
317
329
|
outDir: string,
|
|
318
330
|
inc: string,
|
|
319
331
|
src: string,
|
|
332
|
+
label = "build.include",
|
|
320
333
|
): string {
|
|
321
334
|
const rel = path.relative(cwd, src);
|
|
322
335
|
const isOutside = (r: string) =>
|
|
323
336
|
r === ".." || r.startsWith(`..${path.sep}`) || r.startsWith("../") || path.isAbsolute(r);
|
|
324
|
-
if (rel === "") throw new Error(
|
|
337
|
+
if (rel === "") throw new Error(`${label}: "${inc}" is the project directory`);
|
|
325
338
|
if (isOutside(rel)) {
|
|
326
|
-
throw new Error(
|
|
339
|
+
throw new Error(`${label}: "${inc}" is outside the project directory`);
|
|
327
340
|
}
|
|
328
341
|
// Neither the output directory nor anything that contains it or is in it.
|
|
329
342
|
if (!isOutside(path.relative(src, outDir)) || !isOutside(path.relative(outDir, src))) {
|
|
330
|
-
throw new Error(
|
|
343
|
+
throw new Error(`${label}: "${inc}" overlaps the output directory`);
|
|
331
344
|
}
|
|
332
345
|
const top = rel.split(/[\\/]/)[0];
|
|
333
346
|
if (RESERVED_INCLUDE_NAMES.has(top)) {
|
|
334
|
-
throw new Error(
|
|
347
|
+
throw new Error(`${label}: "${inc}" would write into the artifact's own "${top}"`);
|
|
335
348
|
}
|
|
336
349
|
return rel;
|
|
337
350
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `ctx.shards
|
|
3
|
-
*
|
|
2
|
+
* `ctx.shards` — the frames the runtime sends to the host (`ticket`,
|
|
3
|
+
* `create`, `stop`, `get`, `list`) and the values it returns. The host signs (it holds the secret); this
|
|
4
4
|
* checks the TS half. Same child-process NDJSON harness as
|
|
5
5
|
* runtime-email.test.ts.
|
|
6
6
|
*/
|
|
@@ -18,24 +18,16 @@ function parseFrames(text: string): Record<string, unknown>[] {
|
|
|
18
18
|
.map((l) => JSON.parse(l) as Record<string, unknown>);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
/** Run one function in a real runtime child; `host` answers its frames. */
|
|
22
|
+
async function runFunction(
|
|
23
|
+
source: string,
|
|
24
|
+
host: (frame: Record<string, unknown>) => unknown,
|
|
25
|
+
hostFrameTypes: string[],
|
|
26
|
+
fnType: "mutation" | "action" = "mutation",
|
|
27
|
+
): Promise<{ value: unknown; hostFrames: Record<string, unknown>[] }> {
|
|
28
|
+
const dir = mkdtempSync(join(tmpdir(), "pylon-fn-shards-"));
|
|
23
29
|
mkdirSync(join(dir, "functions"));
|
|
24
|
-
writeFileSync(
|
|
25
|
-
join(dir, "functions", "join.ts"),
|
|
26
|
-
`export default {
|
|
27
|
-
type: "mutation",
|
|
28
|
-
handler: async (ctx) => {
|
|
29
|
-
const ticket = await ctx.shards.ticket("zone-3", {
|
|
30
|
-
subscriberId: "char_12",
|
|
31
|
-
claims: { realm: "north" },
|
|
32
|
-
ttlSecs: 30,
|
|
33
|
-
});
|
|
34
|
-
return { ticket };
|
|
35
|
-
},
|
|
36
|
-
};
|
|
37
|
-
`,
|
|
38
|
-
);
|
|
30
|
+
writeFileSync(join(dir, "functions", "run.ts"), source);
|
|
39
31
|
const proc = Bun.spawn([process.execPath, RUNTIME, "./functions"], {
|
|
40
32
|
cwd: dir,
|
|
41
33
|
stdin: "pipe",
|
|
@@ -45,43 +37,101 @@ test("ctx.shards.ticket sends sign_shard_ticket and returns the host's ticket",
|
|
|
45
37
|
const reader = (proc.stdout as ReadableStream<Uint8Array>).getReader();
|
|
46
38
|
const decoder = new TextDecoder();
|
|
47
39
|
let buffered = "";
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
40
|
+
let consumed = 0;
|
|
41
|
+
const next = async (): Promise<Record<string, unknown>> => {
|
|
42
|
+
for (;;) {
|
|
43
|
+
const frames = parseFrames(buffered);
|
|
44
|
+
if (frames.length > consumed) return frames[consumed++];
|
|
51
45
|
const { done, value } = await reader.read();
|
|
52
|
-
if (done)
|
|
46
|
+
if (done) throw new Error("runtime exited");
|
|
53
47
|
buffered += decoder.decode(value, { stream: true });
|
|
54
|
-
frames = parseFrames(buffered);
|
|
55
48
|
}
|
|
56
|
-
return frames;
|
|
57
49
|
};
|
|
58
50
|
const send = (msg: Record<string, unknown>) =>
|
|
59
51
|
(proc.stdin as import("bun").FileSink).write(JSON.stringify(msg) + "\n");
|
|
60
52
|
|
|
61
|
-
|
|
53
|
+
while ((await next()).type !== "ready");
|
|
62
54
|
send({
|
|
63
55
|
type: "call",
|
|
64
56
|
call_id: "c_1",
|
|
65
|
-
fn_name: "
|
|
66
|
-
fn_type:
|
|
57
|
+
fn_name: "run",
|
|
58
|
+
fn_type: fnType,
|
|
67
59
|
args: {},
|
|
68
60
|
auth: { user_id: "u1", is_admin: false, tenant_id: null },
|
|
69
61
|
});
|
|
62
|
+
const hostFrames: Record<string, unknown>[] = [];
|
|
63
|
+
try {
|
|
64
|
+
for (;;) {
|
|
65
|
+
const frame = await next();
|
|
66
|
+
if (frame.type === "return") return { value: frame.value, hostFrames };
|
|
67
|
+
if (frame.type === "error") throw new Error(JSON.stringify(frame));
|
|
68
|
+
if (hostFrameTypes.includes(frame.type as string)) {
|
|
69
|
+
hostFrames.push(frame);
|
|
70
|
+
send({ type: "result", call_id: "c_1", data: host(frame) });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
} finally {
|
|
74
|
+
proc.kill();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
70
77
|
|
|
71
|
-
|
|
72
|
-
|
|
78
|
+
test("ctx.shards.ticket sends sign_shard_ticket and returns the host's ticket", async () => {
|
|
79
|
+
const { value, hostFrames } = await runFunction(
|
|
80
|
+
`export default {
|
|
81
|
+
type: "mutation",
|
|
82
|
+
handler: async (ctx) => {
|
|
83
|
+
const ticket = await ctx.shards.ticket("zone-3", {
|
|
84
|
+
subscriberId: "char_12",
|
|
85
|
+
claims: { realm: "north" },
|
|
86
|
+
ttlSecs: 30,
|
|
87
|
+
});
|
|
88
|
+
return { ticket };
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
`,
|
|
92
|
+
() => "v1.payload.sig",
|
|
93
|
+
["sign_shard_ticket"],
|
|
94
|
+
);
|
|
95
|
+
expect(hostFrames[0]).toMatchObject({
|
|
73
96
|
call_id: "c_1",
|
|
74
97
|
shard: "zone-3",
|
|
75
98
|
subscriber_id: "char_12",
|
|
76
99
|
claims: { realm: "north" },
|
|
77
100
|
ttl_secs: 30,
|
|
78
101
|
});
|
|
79
|
-
|
|
102
|
+
expect(value).toEqual({ ticket: "v1.payload.sig" });
|
|
103
|
+
});
|
|
80
104
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
105
|
+
test("ctx.shards.create/get/list/stop in an action send shard_op frames", async () => {
|
|
106
|
+
const info = { id: "m1", kind: "arena", tick: 0, subscribers: 0, running: true };
|
|
107
|
+
const { value, hostFrames } = await runFunction(
|
|
108
|
+
`export default {
|
|
109
|
+
type: "action",
|
|
110
|
+
handler: async (ctx) => {
|
|
111
|
+
const created = await ctx.shards.create("arena", "m1", { fog: true });
|
|
112
|
+
const got = await ctx.shards.get("m1");
|
|
113
|
+
const all = await ctx.shards.list();
|
|
114
|
+
const stopped = await ctx.shards.stop("m1");
|
|
115
|
+
return { created, got, all, stopped };
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
`,
|
|
119
|
+
(frame) => {
|
|
120
|
+
switch (frame.op) {
|
|
121
|
+
case "create":
|
|
122
|
+
case "get":
|
|
123
|
+
return info;
|
|
124
|
+
case "list":
|
|
125
|
+
return [info];
|
|
126
|
+
default:
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
["shard_op"],
|
|
131
|
+
"action",
|
|
132
|
+
);
|
|
133
|
+
expect(hostFrames.map((f) => f.op)).toEqual(["create", "get", "list", "stop"]);
|
|
134
|
+
expect(hostFrames[0]).toMatchObject({ kind: "arena", id: "m1", params: { fog: true } });
|
|
135
|
+
expect(hostFrames[3]).toMatchObject({ id: "m1" });
|
|
136
|
+
expect(value).toEqual({ created: info, got: info, all: [info], stopped: true });
|
|
87
137
|
});
|
package/src/runtime.ts
CHANGED
|
@@ -23,6 +23,7 @@ import type {
|
|
|
23
23
|
EmailSender,
|
|
24
24
|
Files,
|
|
25
25
|
Shards,
|
|
26
|
+
ShardInfo,
|
|
26
27
|
Stream,
|
|
27
28
|
Scheduler,
|
|
28
29
|
Llm,
|
|
@@ -535,6 +536,24 @@ function buildShards(callId: string): Shards {
|
|
|
535
536
|
ttl_secs: opts?.ttlSecs,
|
|
536
537
|
}) as Promise<string>;
|
|
537
538
|
},
|
|
539
|
+
async create(kind, shardId, params) {
|
|
540
|
+
return rpc(callId, {
|
|
541
|
+
type: "shard_op",
|
|
542
|
+
op: "create",
|
|
543
|
+
kind,
|
|
544
|
+
id: shardId,
|
|
545
|
+
params: params ?? {},
|
|
546
|
+
}) as Promise<ShardInfo>;
|
|
547
|
+
},
|
|
548
|
+
async stop(shardId) {
|
|
549
|
+
return rpc(callId, { type: "shard_op", op: "stop", id: shardId }) as Promise<boolean>;
|
|
550
|
+
},
|
|
551
|
+
async get(shardId) {
|
|
552
|
+
return rpc(callId, { type: "shard_op", op: "get", id: shardId }) as Promise<ShardInfo | null>;
|
|
553
|
+
},
|
|
554
|
+
async list() {
|
|
555
|
+
return rpc(callId, { type: "shard_op", op: "list" }) as Promise<ShardInfo[]>;
|
|
556
|
+
},
|
|
538
557
|
};
|
|
539
558
|
}
|
|
540
559
|
|
package/src/types.ts
CHANGED
|
@@ -861,6 +861,45 @@ export interface Shards {
|
|
|
861
861
|
shardId: string,
|
|
862
862
|
opts?: { subscriberId?: string; claims?: Record<string, unknown>; ttlSecs?: number },
|
|
863
863
|
): Promise<string>;
|
|
864
|
+
|
|
865
|
+
/**
|
|
866
|
+
* Start shard `shardId` of a kind declared with `shard({...})` in app.ts.
|
|
867
|
+
* `params` reach the module's `init`. Actions only: a mutation's
|
|
868
|
+
* rollback cannot undo it.
|
|
869
|
+
*
|
|
870
|
+
* Throws `SHARD_EXISTS` when the id is running, `SHARD_LIMIT_REACHED`
|
|
871
|
+
* at the kind's `maxInstances`, `SHARD_KIND_NOT_FOUND`,
|
|
872
|
+
* `SHARD_ID_INVALID`, or `SHARD_INIT_FAILED` when `init` refuses.
|
|
873
|
+
*/
|
|
874
|
+
create(kind: string, shardId: string, params?: unknown): Promise<ShardInfo>;
|
|
875
|
+
|
|
876
|
+
/** Stop a shard and close its subscribers' connections. Resolves to
|
|
877
|
+
* `false` when no shard has that id. Actions only. */
|
|
878
|
+
stop(shardId: string): Promise<boolean>;
|
|
879
|
+
|
|
880
|
+
/** A running shard, or `null`. */
|
|
881
|
+
get(shardId: string): Promise<ShardInfo | null>;
|
|
882
|
+
|
|
883
|
+
/** Every running shard. */
|
|
884
|
+
list(): Promise<ShardInfo[]>;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
/** `ctx.shards` in a query or mutation: tickets and reads, no start or stop. */
|
|
888
|
+
export type ShardsReader = Pick<Shards, "ticket" | "get" | "list">;
|
|
889
|
+
|
|
890
|
+
/** A running shard, from `ctx.shards.create`, `get`, or `list`. */
|
|
891
|
+
export interface ShardInfo {
|
|
892
|
+
id: string;
|
|
893
|
+
/** The shard kind's name. */
|
|
894
|
+
kind: string;
|
|
895
|
+
/** Ticks run so far. */
|
|
896
|
+
tick: number;
|
|
897
|
+
subscribers: number;
|
|
898
|
+
/** False once the shard stopped (finished, idle, or failed) and before
|
|
899
|
+
* the host removes it. */
|
|
900
|
+
running: boolean;
|
|
901
|
+
/** Why the module stopped, when it trapped. */
|
|
902
|
+
error?: string;
|
|
864
903
|
}
|
|
865
904
|
|
|
866
905
|
/** Context for query handlers (read-only).
|
|
@@ -888,8 +927,8 @@ export interface QueryCtx<R extends AuthRequirement = "optional"> {
|
|
|
888
927
|
requireMember: RequireMember;
|
|
889
928
|
/** Signed file-download URLs — see {@link Files}. */
|
|
890
929
|
files: Files;
|
|
891
|
-
/** Shard tickets — see {@link Shards}. */
|
|
892
|
-
shards:
|
|
930
|
+
/** Shard tickets and reads — see {@link Shards}. */
|
|
931
|
+
shards: ShardsReader;
|
|
893
932
|
/**
|
|
894
933
|
* Fires when the host cancels this call (idle timeout exceeded).
|
|
895
934
|
* Thread it into `fetch(url, { signal: ctx.signal })` or SDK calls so
|
|
@@ -918,8 +957,8 @@ export interface MutationCtx<R extends AuthRequirement = "optional"> {
|
|
|
918
957
|
workflows: Workflows;
|
|
919
958
|
/** Signed file-download URLs — see {@link Files}. */
|
|
920
959
|
files: Files;
|
|
921
|
-
/** Shard tickets — see {@link Shards}. */
|
|
922
|
-
shards:
|
|
960
|
+
/** Shard tickets and reads — see {@link Shards}. */
|
|
961
|
+
shards: ShardsReader;
|
|
923
962
|
/** Create a typed error that triggers rollback. */
|
|
924
963
|
error(code: string, message: string): Error;
|
|
925
964
|
/** Assert org membership (optionally a role) — see {@link RequireMember}. */
|
|
@@ -1093,7 +1132,7 @@ export interface ActionCtx<R extends AuthRequirement = "optional"> {
|
|
|
1093
1132
|
env: Record<string, string>;
|
|
1094
1133
|
/** Signed file-download URLs — see {@link Files}. */
|
|
1095
1134
|
files: Files;
|
|
1096
|
-
/** Shard tickets — see {@link Shards}. */
|
|
1135
|
+
/** Shard tickets, reads, and start/stop — see {@link Shards}. */
|
|
1097
1136
|
shards: Shards;
|
|
1098
1137
|
/** Run a registered query within its own read transaction. */
|
|
1099
1138
|
runQuery<T = unknown>(
|