ankka 0.11.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.
Files changed (92) hide show
  1. package/README.md +1 -0
  2. package/dist/_proto/ankka/protocol/v1/client_pb.d.ts +910 -0
  3. package/dist/_proto/ankka/protocol/v1/client_pb.js +239 -23
  4. package/dist/_proto/ankka/protocol/v1/consumer_pb.d.ts +10 -1
  5. package/dist/_proto/ankka/protocol/v1/consumer_pb.js +1 -1
  6. package/dist/_proto/ankka/protocol/v1/discovery_pb.d.ts +177 -0
  7. package/dist/_proto/ankka/protocol/v1/discovery_pb.js +51 -36
  8. package/dist/_proto/ankka/protocol/v1/endpoint_pb.d.ts +26 -0
  9. package/dist/_proto/ankka/protocol/v1/endpoint_pb.js +15 -10
  10. package/dist/_proto/ankka/protocol/v1/event_sourced_pb.d.ts +86 -0
  11. package/dist/_proto/ankka/protocol/v1/event_sourced_pb.js +11 -1
  12. package/dist/_proto/ankka/protocol/v1/mcp_pb.d.ts +120 -0
  13. package/dist/_proto/ankka/protocol/v1/mcp_pb.js +29 -0
  14. package/dist/_proto/ankka/protocol/v1/payload_pb.d.ts +61 -0
  15. package/dist/_proto/ankka/protocol/v1/payload_pb.js +18 -1
  16. package/dist/_proto/ankka/protocol/v1/view_pb.d.ts +7 -1
  17. package/dist/_proto/ankka/protocol/v1/view_pb.js +1 -1
  18. package/dist/_proto/ankka/protocol/v1/wasm_pb.d.ts +12 -0
  19. package/dist/_proto/ankka/protocol/v1/wasm_pb.js +1 -1
  20. package/dist/agent.d.ts +10 -0
  21. package/dist/agent.js +20 -0
  22. package/dist/autonomous.d.ts +5 -0
  23. package/dist/autonomous.js +10 -0
  24. package/dist/client-keys.d.ts +89 -0
  25. package/dist/client-keys.js +182 -0
  26. package/dist/client.d.ts +96 -3
  27. package/dist/client.js +228 -6
  28. package/dist/consumer.d.ts +23 -1
  29. package/dist/consumer.js +25 -1
  30. package/dist/context.d.ts +25 -1
  31. package/dist/context.js +5 -2
  32. package/dist/contract.d.ts +4 -0
  33. package/dist/contract.js +2 -0
  34. package/dist/effects/common.d.ts +7 -0
  35. package/dist/effects/common.js +9 -1
  36. package/dist/effects/eventSourced.d.ts +26 -1
  37. package/dist/effects/eventSourced.js +58 -5
  38. package/dist/endpoint.d.ts +10 -0
  39. package/dist/endpoint.js +20 -0
  40. package/dist/eventSourcedEntity.d.ts +8 -3
  41. package/dist/graph.d.ts +14 -1
  42. package/dist/graph.js +17 -1
  43. package/dist/index.d.ts +10 -4
  44. package/dist/index.js +8 -2
  45. package/dist/keyedView.d.ts +4 -1
  46. package/dist/keyedView.js +7 -1
  47. package/dist/kinds.js +2 -0
  48. package/dist/mcp-endpoint.d.ts +144 -0
  49. package/dist/mcp-endpoint.js +256 -0
  50. package/dist/routes.d.ts +46 -3
  51. package/dist/routes.js +56 -3
  52. package/dist/server/discovery.d.ts +12 -0
  53. package/dist/server/discovery.js +115 -4
  54. package/dist/server/eventSourced.d.ts +4 -1
  55. package/dist/server/eventSourced.js +24 -6
  56. package/dist/server/http.d.ts +8 -0
  57. package/dist/server/http.js +12 -4
  58. package/dist/server/mcp.d.ts +17 -0
  59. package/dist/server/mcp.js +157 -0
  60. package/dist/server/server.d.ts +11 -1
  61. package/dist/server/server.js +19 -0
  62. package/dist/server/stateless.js +5 -4
  63. package/dist/server/storageEndpoint.d.ts +14 -0
  64. package/dist/server/storageEndpoint.js +82 -0
  65. package/dist/service.d.ts +12 -1
  66. package/dist/service.js +73 -5
  67. package/dist/sigv4.d.ts +47 -0
  68. package/dist/sigv4.js +149 -0
  69. package/dist/spec.d.ts +8 -2
  70. package/dist/spec.js +62 -6
  71. package/dist/standing.d.ts +18 -0
  72. package/dist/standing.js +26 -0
  73. package/dist/startFrom.d.ts +4 -0
  74. package/dist/startFrom.js +8 -1
  75. package/dist/storage.d.ts +288 -0
  76. package/dist/storage.js +1081 -0
  77. package/dist/testkit/index.d.ts +1 -1
  78. package/dist/testkit/integration.d.ts +9 -0
  79. package/dist/testkit/integration.js +5 -1
  80. package/dist/testkit/kinds.d.ts +25 -9
  81. package/dist/testkit/kinds.js +53 -28
  82. package/dist/testkit/unit.d.ts +31 -3
  83. package/dist/testkit/unit.js +55 -11
  84. package/dist/timedAction.d.ts +10 -0
  85. package/dist/timedAction.js +20 -0
  86. package/dist/version.d.ts +1 -1
  87. package/dist/version.js +1 -1
  88. package/dist/view.d.ts +18 -3
  89. package/dist/view.js +13 -4
  90. package/dist/workflow.d.ts +8 -0
  91. package/dist/workflow.js +15 -0
  92. package/package.json +1 -1
package/dist/spec.d.ts CHANGED
@@ -1,10 +1,16 @@
1
1
  import { type MessageInitShape } from "@bufbuild/protobuf";
2
- import { Endpoint_Acl, type CallerMatcherSchema, type Spec } from "./_proto/ankka/protocol/v1/discovery_pb.ts";
2
+ import { Endpoint_Acl, type McpAclSchema, type CallerMatcherSchema, type Spec } from "./_proto/ankka/protocol/v1/discovery_pb.ts";
3
3
  import type { Registry } from "./service.ts";
4
4
  import type { Acl } from "./routes.ts";
5
- export declare const PROTOCOL_VERSION = "1.14";
5
+ export declare const PROTOCOL_VERSION = "1.21";
6
6
  export declare const SDK_NAME = "ankka-typescript";
7
+ /** An HTTP endpoint's rule; client keys and `anyOf` are refused for one at registration, so they deny here. */
7
8
  export declare function aclToProto(acl: Acl): Endpoint_Acl;
8
9
  /** The callers a CALLERS acl names, as discovery carries them; empty for every other rule. */
9
10
  export declare function callersToProto(acl: Acl): MessageInitShape<typeof CallerMatcherSchema>[];
11
+ /**
12
+ * An MCP endpoint's or tool's rule as discovery carries it (1.20): each member of an `anyOf` sets its
13
+ * own field, so `anyOf(clientKeys, authenticated)` is `client_keys` and `tokens`.
14
+ */
15
+ export declare function mcpAclToProto(acl: Acl): MessageInitShape<typeof McpAclSchema>;
10
16
  export declare function renderSpec(registry: Registry): Spec;
package/dist/spec.js CHANGED
@@ -17,11 +17,12 @@ function toolInit(t) {
17
17
  ...(approval !== undefined ? { approval } : {}),
18
18
  };
19
19
  }
20
- export const PROTOCOL_VERSION = "1.14";
20
+ export const PROTOCOL_VERSION = "1.21";
21
21
  export const SDK_NAME = "ankka-typescript";
22
+ /** An HTTP endpoint's rule; client keys and `anyOf` are refused for one at registration, so they deny here. */
22
23
  export function aclToProto(acl) {
23
24
  if (typeof acl === "object")
24
- return Endpoint_Acl.CALLERS;
25
+ return acl.kind === "callers" ? Endpoint_Acl.CALLERS : Endpoint_Acl.DENY_ALL;
25
26
  switch (acl) {
26
27
  case "allow-all":
27
28
  return Endpoint_Acl.ALLOW_ALL;
@@ -29,11 +30,13 @@ export function aclToProto(acl) {
29
30
  return Endpoint_Acl.DENY_ALL;
30
31
  case "authenticated":
31
32
  return Endpoint_Acl.AUTHENTICATED;
33
+ case "client-keys":
34
+ return Endpoint_Acl.DENY_ALL;
32
35
  }
33
36
  }
34
37
  /** The callers a CALLERS acl names, as discovery carries them; empty for every other rule. */
35
38
  export function callersToProto(acl) {
36
- if (typeof acl !== "object")
39
+ if (typeof acl !== "object" || acl.kind !== "callers")
37
40
  return [];
38
41
  return acl.callers.map((m) => {
39
42
  switch (m.kind) {
@@ -43,11 +46,45 @@ export function callersToProto(acl) {
43
46
  return { kind: { case: "anyInProject", value: {} } };
44
47
  case "self":
45
48
  return { kind: { case: "self", value: {} } };
49
+ case "granted":
50
+ return { kind: { case: "granted", value: {} } };
46
51
  case "service":
47
52
  return { kind: { case: "service", value: m.project !== undefined ? { name: m.name, project: m.project } : { name: m.name } } };
48
53
  }
49
54
  });
50
55
  }
56
+ /**
57
+ * An MCP endpoint's or tool's rule as discovery carries it (1.20): each member of an `anyOf` sets its
58
+ * own field, so `anyOf(clientKeys, authenticated)` is `client_keys` and `tokens`.
59
+ */
60
+ export function mcpAclToProto(acl) {
61
+ const out = { allowAll: false, denyAll: false, tokens: false, clientKeys: false, callers: [] };
62
+ const add = (rule) => {
63
+ if (typeof rule === "object") {
64
+ if (rule.kind === "anyOf")
65
+ rule.acls.forEach(add);
66
+ else
67
+ out.callers.push(...callersToProto(rule));
68
+ return;
69
+ }
70
+ switch (rule) {
71
+ case "allow-all":
72
+ out.allowAll = true;
73
+ break;
74
+ case "deny-all":
75
+ out.denyAll = true;
76
+ break;
77
+ case "authenticated":
78
+ out.tokens = true;
79
+ break;
80
+ case "client-keys":
81
+ out.clientKeys = true;
82
+ break;
83
+ }
84
+ };
85
+ add(acl);
86
+ return out;
87
+ }
51
88
  function handlerInits(handlers) {
52
89
  return [...handlers.values()]
53
90
  .map((h) => ({ name: h.name, readOnly: h.readOnly, streaming: h.streaming }))
@@ -62,6 +99,8 @@ function sourceInit(source) {
62
99
  ...(source.contract !== undefined ? { contract: { name: source.contract.name, fingerprint: source.contract.fingerprint } } : {}),
63
100
  ...(source.broker !== undefined ? { broker: source.broker } : {}),
64
101
  ...(source.parallel === true ? { parallel: true } : {}),
102
+ // 1.15: another project's topic.
103
+ ...(source.project !== undefined ? { project: source.project } : {}),
65
104
  };
66
105
  if (start === undefined)
67
106
  return { source: { case: "topic", value: source.topic }, ...options };
@@ -93,7 +132,7 @@ function componentInit(c) {
93
132
  case "autonomous-agent":
94
133
  return autonomousInit(c);
95
134
  case "event-sourced":
96
- return { ...base, handlers: handlerInits(c.handlers), detail: { case: "eventSourced", value: { snapshotEvery: c.snapshotEvery } } };
135
+ return { ...base, handlers: handlerInits(c.handlers), detail: { case: "eventSourced", value: { snapshotEvery: c.snapshotEvery, replicated: c.replicated } } };
97
136
  case "key-value":
98
137
  return { ...base, handlers: handlerInits(c.handlers), detail: { case: "keyValue", value: {} } };
99
138
  case "workflow":
@@ -113,7 +152,7 @@ function componentInit(c) {
113
152
  rowManifest: c.rowCodec.manifest,
114
153
  queries: [...c.queries],
115
154
  ...(c.version !== undefined ? { version: c.version } : {}),
116
- declaredQueries: c.declared.map((q) => ({ name: q.name, statement: q.statement })),
155
+ declaredQueries: c.declared.map((q) => ({ name: q.name, statement: q.statement, watched: q.watched ?? false })),
117
156
  },
118
157
  },
119
158
  };
@@ -128,7 +167,7 @@ function componentInit(c) {
128
167
  queries: [],
129
168
  ...(c.version !== undefined ? { version: c.version } : {}),
130
169
  sources: [...c.sources.values()].map((s) => sourceInit({ component: { kind: s.kind, id: s.id } })),
131
- declaredQueries: c.declared.map((q) => ({ name: q.name, statement: q.statement })),
170
+ declaredQueries: c.declared.map((q) => ({ name: q.name, statement: q.statement, watched: q.watched ?? false })),
132
171
  },
133
172
  },
134
173
  };
@@ -142,6 +181,7 @@ function componentInit(c) {
142
181
  source: sourceInit(c.source),
143
182
  ...(c.producesTo !== undefined ? { producesTo: c.producesTo } : {}),
144
183
  ...(c.version !== undefined ? { version: c.version } : {}),
184
+ mirroredChanges: c.mirroredChanges,
145
185
  // 1.14: the publication with its contract and broker; `producesTo` stays for an older sidecar.
146
186
  ...(c.produces !== undefined
147
187
  ? {
@@ -149,6 +189,7 @@ function componentInit(c) {
149
189
  topic: c.produces.topic,
150
190
  ...(c.produces.contract !== undefined ? { contract: { name: c.produces.contract.name, fingerprint: c.produces.contract.fingerprint } } : {}),
151
191
  ...(c.produces.broker !== undefined ? { broker: c.produces.broker } : {}),
192
+ ...(c.produces.project !== undefined ? { project: c.produces.project } : {}),
152
193
  },
153
194
  }
154
195
  : {}),
@@ -226,10 +267,25 @@ export function renderSpec(registry) {
226
267
  ...(r.acl !== undefined ? { acl: aclToProto(r.acl), allowCallers: callersToProto(r.acl) } : {}),
227
268
  })),
228
269
  }));
270
+ const mcpEndpoints = [...registry.mcpEndpoints.values()].map((e) => ({
271
+ id: e.id,
272
+ path: e.path,
273
+ ...(e.serverName !== undefined ? { name: e.serverName } : {}),
274
+ ...(e.serverVersion !== undefined ? { version: e.serverVersion } : {}),
275
+ acl: mcpAclToProto(e.acl),
276
+ tools: e.tools.map((t) => ({
277
+ name: t.name,
278
+ description: t.description,
279
+ inputSchemaJson: JSON.stringify(t.inputSchema),
280
+ ...(t.acl !== undefined ? { acl: mcpAclToProto(t.acl) } : {}),
281
+ })),
282
+ brokered: e.brokered,
283
+ }));
229
284
  return create(SpecSchema, {
230
285
  protocolVersion: PROTOCOL_VERSION,
231
286
  sdk: { name: SDK_NAME, version: VERSION },
232
287
  components,
233
288
  endpoints,
289
+ mcpEndpoints,
234
290
  });
235
291
  }
@@ -0,0 +1,18 @@
1
+ import type { WorkflowStanding } from "./_proto/ankka/protocol/v1/payload_pb.ts";
2
+ export type StandingStatus = "NotStarted" | "Running" | "Paused" | "Completed" | "Failed" | "Unknown";
3
+ export interface Standing {
4
+ readonly status: StandingStatus;
5
+ /** The step the workflow is on, or the step a pause names or follows. */
6
+ readonly step?: string;
7
+ /** Retries so far, per step. */
8
+ readonly retries: Readonly<Record<string, number>>;
9
+ /** Why it failed, when it did. */
10
+ readonly failure?: string;
11
+ }
12
+ export declare function isTerminal(standing: Standing): boolean;
13
+ /** The state was recorded before the platform stamped standings. */
14
+ export declare function isUnknown(standing: Standing): boolean;
15
+ /** @internal The standing a request carries, or `undefined` for a change from an entity or a topic. */
16
+ export declare function standingFromProto(standing: WorkflowStanding | undefined): Standing | undefined;
17
+ /** @internal The version whose runtimes read a workflow as a source. */
18
+ export declare const WORKFLOW_SOURCE_PROTOCOL: readonly [number, number];
@@ -0,0 +1,26 @@
1
+ // Where a workflow stood once the effect that recorded a state was applied (protocol 1.16).
2
+ //
3
+ // A view or a consumer whose source is a workflow is handed, with each state the workflow records,
4
+ // its standing: running, paused, completed or failed, the step it is on or waits after, the retries
5
+ // of each step and, when it failed, why. `Unknown` is the standing of a state recorded before the
6
+ // platform stamped standings. A change from an entity or a topic has none.
7
+ export function isTerminal(standing) {
8
+ return standing.status === "Completed" || standing.status === "Failed";
9
+ }
10
+ /** The state was recorded before the platform stamped standings. */
11
+ export function isUnknown(standing) {
12
+ return standing.status === "Unknown";
13
+ }
14
+ /** @internal The standing a request carries, or `undefined` for a change from an entity or a topic. */
15
+ export function standingFromProto(standing) {
16
+ if (standing === undefined)
17
+ return undefined;
18
+ return Object.freeze({
19
+ status: standing.status,
20
+ ...(standing.step ? { step: standing.step } : {}),
21
+ retries: Object.freeze({ ...standing.retries }),
22
+ ...(standing.failure ? { failure: standing.failure } : {}),
23
+ });
24
+ }
25
+ /** @internal The version whose runtimes read a workflow as a source. */
26
+ export const WORKFLOW_SOURCE_PROTOCOL = [1, 16];
@@ -24,9 +24,13 @@ export declare const START_POSITION_PROTOCOL: readonly [number, number];
24
24
  export declare function isStartFrom(value: unknown): value is StartFrom;
25
25
  /** The first protocol in which a process can declare a view's queries. */
26
26
  export declare const DECLARED_QUERY_PROTOCOL: readonly [number, number];
27
+ export declare function olderThan(protocolVersion: string, [wantMajor, wantMinor]: readonly [number, number]): boolean;
27
28
  /** Whether a sidecar speaking `protocolVersion` would ignore a start position and a version. */
28
29
  export declare function olderThanStartPositions(protocolVersion: string): boolean;
29
30
  /** Whether a sidecar speaking `protocolVersion` would ignore a view's declared queries. */
30
31
  export declare function olderThanDeclaredQueries(protocolVersion: string): boolean;
32
+ /** Whether a sidecar speaking `protocolVersion` would not know a watched query. */
33
+ export declare function olderThanViewStreams(protocolVersion: string): boolean;
31
34
  /** Whether a sidecar speaking `protocolVersion` would ignore a topic's contract, broker and parallel reading. */
35
+ export declare function olderThanWorkflowSources(protocolVersion: string): boolean;
32
36
  export declare function olderThanContracts(protocolVersion: string): boolean;
package/dist/startFrom.js CHANGED
@@ -24,7 +24,7 @@ export function isStartFrom(value) {
24
24
  }
25
25
  /** The first protocol in which a process can declare a view's queries. */
26
26
  export const DECLARED_QUERY_PROTOCOL = [1, 13];
27
- function olderThan(protocolVersion, [wantMajor, wantMinor]) {
27
+ export function olderThan(protocolVersion, [wantMajor, wantMinor]) {
28
28
  const [major, minor] = protocolVersion.split(".").map((part) => Number.parseInt(part, 10));
29
29
  if (!Number.isFinite(major) || !Number.isFinite(minor))
30
30
  return false;
@@ -38,7 +38,14 @@ export function olderThanStartPositions(protocolVersion) {
38
38
  export function olderThanDeclaredQueries(protocolVersion) {
39
39
  return olderThan(protocolVersion, DECLARED_QUERY_PROTOCOL);
40
40
  }
41
+ /** Whether a sidecar speaking `protocolVersion` would not know a watched query. */
42
+ export function olderThanViewStreams(protocolVersion) {
43
+ return olderThan(protocolVersion, [1, 17]);
44
+ }
41
45
  /** Whether a sidecar speaking `protocolVersion` would ignore a topic's contract, broker and parallel reading. */
46
+ export function olderThanWorkflowSources(protocolVersion) {
47
+ return olderThan(protocolVersion, [1, 16]);
48
+ }
42
49
  export function olderThanContracts(protocolVersion) {
43
50
  return olderThan(protocolVersion, [1, 14]);
44
51
  }
@@ -0,0 +1,288 @@
1
+ import { CommandError } from "./effects/common.ts";
2
+ import { Credentials } from "./sigv4.ts";
3
+ import { Instant } from "./time.ts";
4
+ /** How a bucket is reached from outside the installation. */
5
+ export type BucketExposure = "private" | "signed" | "public" | "supplied";
6
+ /** What a signed URL lets its holder do with one object. */
7
+ export type SignedAccess = "read" | "keep";
8
+ /** An object read from a bucket. Read its `body` once, or call `bytes()`. */
9
+ export interface StoredObject {
10
+ readonly contentType: string;
11
+ readonly length: number;
12
+ readonly lastKept: Instant;
13
+ readonly body: ReadableStream<Uint8Array>;
14
+ /** The whole body, read from `body`. */
15
+ bytes(): Promise<Uint8Array>;
16
+ }
17
+ /** What a bucket keeps under a name, without the bytes. */
18
+ export interface ObjectInfo {
19
+ readonly name: string;
20
+ readonly contentType: string;
21
+ readonly length: number;
22
+ readonly lastKept: Instant;
23
+ }
24
+ /** One object in a listing. */
25
+ export interface ObjectEntry {
26
+ readonly name: string;
27
+ readonly length: number;
28
+ readonly lastKept: Instant;
29
+ }
30
+ /** How a put keeps its body: its content type, and its length, which a stream must declare. */
31
+ export interface PutOptions {
32
+ readonly contentType: string;
33
+ readonly length?: number;
34
+ }
35
+ /**
36
+ * One bucket. Its objects are named with `/` between segments, so a listing by prefix reads like a
37
+ * directory; it is not one. A put replaces the whole object or leaves the old one.
38
+ */
39
+ export interface Bucket {
40
+ /** The descriptor's name for it; `undefined` for the unnamed bucket. */
41
+ readonly name: string | undefined;
42
+ readonly exposure: BucketExposure;
43
+ /**
44
+ * Keeps `body` under `name`, replacing what was there. A stream declares its `length`; one that ends
45
+ * before it fails and keeps nothing, and the old object, if any, stays.
46
+ */
47
+ put(name: string, body: ReadableStream<Uint8Array> | Uint8Array, options: PutOptions): Promise<void>;
48
+ /** The object kept under `name`, or `undefined` when there is none. */
49
+ get(name: string): Promise<StoredObject | undefined>;
50
+ /** Removes what is kept under `name`. Removing nothing is not an error. */
51
+ delete(name: string): Promise<void>;
52
+ /** Every object whose name begins with `prefix`, in name order; pages are read as it is walked. */
53
+ list(prefix: string): AsyncIterable<ObjectEntry>;
54
+ /** What is kept under `name`, without its bytes, or `undefined`. */
55
+ stat(name: string): Promise<ObjectInfo | undefined>;
56
+ /**
57
+ * A URL a browser may use, until `spanSeconds` (15 minutes by default, at most seven days) have
58
+ * passed, to read or keep the object under `name`. Computed: no request reaches the store. A signed
59
+ * bucket's, or the unnamed bucket's when the service sets `exposeObjectStorage`.
60
+ */
61
+ signedUrl(name: string, access: SignedAccess, spanSeconds?: number): string;
62
+ /** The address a browser reads the object under `name` at, with no signature: a public bucket's only. */
63
+ url(name: string): string;
64
+ }
65
+ /** A service's buckets: `default`, the unnamed one, and the named ones by name. */
66
+ export interface Buckets {
67
+ /** The unnamed bucket. Refuses, naming `provisionObjectStorage`, when the service has none. */
68
+ readonly default: Bucket;
69
+ /** A bucket the descriptor's `buckets` list names; refused, naming the ones it does name, otherwise. */
70
+ bucket(name: string): Bucket;
71
+ /** Every named bucket, in no particular order. */
72
+ readonly buckets: Bucket[];
73
+ }
74
+ /**
75
+ * What an object's name, a prefix, a bucket's name and a signed URL's span may be. Every store and every
76
+ * test double applies these, and `protocol/fixtures/storage/rules.json` holds them to the other SDKs.
77
+ */
78
+ export declare const ObjectRules: Readonly<{
79
+ MAX_NAME_BYTES: 1024;
80
+ MAX_BUCKET_NAME: 32;
81
+ /** Seven days. */
82
+ MAX_SIGNED_URL_SECONDS: 604800;
83
+ /** Fifteen minutes. */
84
+ DEFAULT_SIGNED_URL_SECONDS: 900;
85
+ BUCKET_NAME_RULE: "lower-case letters, digits and '-', 1 to 32 characters";
86
+ /** What is wrong with an object's `name`, if anything. */
87
+ nameProblem(name: string): string | undefined;
88
+ /** What is wrong with a listing's `prefix`, if anything. Empty is everything. */
89
+ prefixProblem(prefix: string): string | undefined;
90
+ /** What is wrong with a bucket's name, if anything, in the descriptor's words. */
91
+ bucketNameProblem(name: string): string | undefined;
92
+ /** What is wrong with a signed URL's span, in seconds, if anything. */
93
+ spanProblem(spanSeconds: number): string | undefined;
94
+ checkName(name: string): void;
95
+ checkPrefix(prefix: string): void;
96
+ checkSpan(spanSeconds: number): void;
97
+ }>;
98
+ /** What the storage client says when it refuses: the runtime's words (`StorageRefusals` in the Scala SDK). */
99
+ export declare const StorageRefusals: Readonly<{
100
+ noBucket(): CommandError;
101
+ unknown(name: string, known: Iterable<string>): CommandError;
102
+ noCredential(): CommandError;
103
+ notExposed(): CommandError;
104
+ notSigned(bucket: string, exposure: BucketExposure): CommandError;
105
+ notPublic(bucket: string | undefined, exposure: BucketExposure): CommandError;
106
+ unreachable(endpoint: string, cause: string): CommandError;
107
+ refusedByStore(endpoint: string, status: number, detail: string): CommandError;
108
+ unreadable(endpoint: string, cause: string): CommandError;
109
+ shortBody(declared: number, read: number): CommandError;
110
+ negativeLength(length: number): CommandError;
111
+ /** TypeScript's own: a stream's length cannot be known before it is read, and S3 is told it first. */
112
+ undeclaredLength(): CommandError;
113
+ stepsOnly(): CommandError;
114
+ }>;
115
+ /** Buckets that refuse every call, naming `provisionObjectStorage`: a deployed service with no bucket. */
116
+ export declare const noStorage: Buckets;
117
+ export interface InMemoryBucketsOptions {
118
+ /** The named buckets and their exposure; more may be declared later with `declare`. */
119
+ readonly buckets?: Readonly<Record<string, BucketExposure>>;
120
+ /** Whether the service has the unnamed bucket; `true` by default. */
121
+ readonly unnamed?: boolean;
122
+ /** The unnamed bucket's exposure: `signed` by default, so a test may sign a URL for it. */
123
+ readonly unnamedExposure?: BucketExposure;
124
+ /** Where a public bucket's objects are read: `http://memory/public/<bucket>` by default, reaching nothing. */
125
+ readonly publicBase?: (bucket: string) => string;
126
+ }
127
+ /**
128
+ * A service's buckets in memory: the test kits' store, so a test of a handler that keeps or reads an
129
+ * object needs no object store and no Docker. It applies every rule the other stores apply, in the
130
+ * same words. A signed URL is computed (`http://memory/...`) and reaches nothing.
131
+ */
132
+ export declare class InMemoryBuckets implements Buckets {
133
+ #private;
134
+ constructor(options?: InMemoryBucketsOptions);
135
+ get default(): Bucket;
136
+ bucket(name: string): Bucket;
137
+ get buckets(): Bucket[];
138
+ /** Declares a named bucket, before or after the service starts. */
139
+ declare(name: string, exposure: BucketExposure): void;
140
+ /** Forgets every object; the declared buckets stay. */
141
+ clear(): void;
142
+ }
143
+ /** What a descriptor declares: whether the unnamed bucket is asked for, and each named one. */
144
+ export interface DeclaredBuckets {
145
+ readonly unnamed: boolean;
146
+ readonly buckets: Readonly<Record<string, BucketExposure>>;
147
+ }
148
+ /**
149
+ * The buckets `service.json` in `directory` declares, read where a service runs on a developer's machine
150
+ * and in a test, so the directory and memory stores refuse the names a deployed service would; `undefined`
151
+ * when there is no such file. Only the two fields that say which buckets there are.
152
+ */
153
+ export declare function declaredBuckets(directory?: string): DeclaredBuckets | undefined;
154
+ /** Where a service serves its directory store's objects. */
155
+ export declare const STORAGE_PATH = "/_ankka/storage";
156
+ /** The unnamed bucket's directory and URL segment: no bucket name begins with `_`. */
157
+ export declare const DEFAULT_SEGMENT = "_default";
158
+ export interface DirectoryBucketsOptions {
159
+ /** The buckets `service.json` declares; with none, the unnamed bucket and any name asked for, as private. */
160
+ readonly declared?: DeclaredBuckets;
161
+ /** The service's own address, where its URLs point: `http://<ANKKA_HTTP_INTERFACE or 127.0.0.1>:<ANKKA_HTTP_PORT or 9000>`. */
162
+ readonly addressBase?: () => string;
163
+ }
164
+ /** The address a local service's HTTP port answers at, as the sidecar beside it binds it. */
165
+ export declare function localAddress(env?: Readonly<Record<string, string | undefined>>): string;
166
+ /**
167
+ * A service's buckets in a directory on a developer's machine: what a service keeps when it runs with no
168
+ * variable of an object store. The runtime's layout exactly, so a Scala service and a TypeScript one read
169
+ * each other's: one directory per bucket under `root` (`_default` for the unnamed one), an object's name
170
+ * encoded into one file name under `objects/` (its hash when too long), and beside it under `meta/` its
171
+ * name, content type, length and when it was kept. A put writes a temporary file and moves it into place,
172
+ * so a stream that ends short leaves the old object.
173
+ *
174
+ * A signed URL and a public address point at the service's own HTTP port, under `/_ankka/storage`, which
175
+ * the SDK serves through the sidecar while this is the service's store.
176
+ */
177
+ export declare class DirectoryBuckets implements Buckets {
178
+ #private;
179
+ readonly root: string;
180
+ constructor(root: string, options?: DirectoryBucketsOptions);
181
+ get default(): Bucket;
182
+ bucket(name: string): Bucket;
183
+ get buckets(): Bucket[];
184
+ /** The bucket a URL's segment names, `_default` or a bucket's name, as this store would answer it. */
185
+ bySegment(segment: string): {
186
+ readonly bucket: Bucket;
187
+ readonly segment: string;
188
+ } | undefined;
189
+ /** `HMAC-SHA256(key, "<segment>/<name>|<access>|<expires>")` in hex, over the key kept in the root. */
190
+ token(segment: string, objectName: string, access: string, expires: number): string;
191
+ /** Whether `token` signs this access until `expires`, and `expires` is still to come. */
192
+ admits(segment: string, objectName: string, access: string, expires: number, token: string): boolean;
193
+ }
194
+ /**
195
+ * The deepest object name whose address keeps its slashes. The sidecar matches a process's route by
196
+ * whole segments, so the storage endpoint declares one route per depth up to this; a deeper name's
197
+ * slashes are encoded and it travels as one segment.
198
+ */
199
+ export declare const MAX_ADDRESS_SEGMENTS = 16;
200
+ /** An object's name in a directory store's address: as the runtime writes it, while the sidecar can route it. */
201
+ export declare function addressOf(objectName: string): string;
202
+ /** One S3 bucket and how to reach it. */
203
+ export interface S3Target {
204
+ readonly endpoint: URL;
205
+ readonly region: string;
206
+ readonly bucket: string;
207
+ readonly credentials: Credentials | undefined;
208
+ readonly publicEndpoint: URL | undefined;
209
+ }
210
+ /** A bucket the descriptor names: its exposure, where it is, and for a public one the base it is read at. */
211
+ export interface NamedTarget {
212
+ readonly name: string;
213
+ readonly exposure: BucketExposure;
214
+ readonly target: S3Target;
215
+ readonly publicUrl: string | undefined;
216
+ }
217
+ /** The S3 store's settings, read from the `ANKKA_S3_*` variables as the runtime's `StorageSettings` reads them. */
218
+ export interface S3Settings {
219
+ readonly unnamed: S3Target | undefined;
220
+ readonly named: readonly NamedTarget[];
221
+ /** How long one request waits for an answer: 30 seconds. */
222
+ readonly timeoutMs: number;
223
+ }
224
+ type Env = Readonly<Record<string, string | undefined>>;
225
+ /** The bucket a variable's `<NAME>` names: lower case, `_` as `-`. */
226
+ export declare function bucketNameOf(suffix: string): string;
227
+ /** The `<NAME>` of a bucket's variables: upper case, `-` as `_`. */
228
+ export declare function bucketVariableSuffix(bucket: string): string;
229
+ /**
230
+ * The S3 settings in `env`: the unnamed family `ANKKA_S3_{ENDPOINT,REGION,BUCKET,ACCESS_KEY,SECRET_KEY,PUBLIC_ENDPOINT}`;
231
+ * a named bucket the platform made, `ANKKA_S3_BUCKET_<NAME>`, `ANKKA_S3_EXPOSURE_<NAME>` and, for a public
232
+ * one, `ANKKA_S3_PUBLIC_URL_<NAME>`, sharing the unnamed family's endpoint, region and key; a supplied
233
+ * bucket, the five `ANKKA_S3_{ENDPOINT,REGION,BUCKET,ACCESS_KEY,SECRET_KEY}_<NAME>` of its own. Throws,
234
+ * naming the variable, on a family that cannot be read.
235
+ */
236
+ export declare function s3Settings(env?: Env, timeoutMs?: number): S3Settings;
237
+ /** A `ListObjectsV2` answer: its entries, and the token for the next page while it is truncated. */
238
+ export declare function parseListing(xml: string): {
239
+ readonly entries: ObjectEntry[];
240
+ readonly next: string | undefined;
241
+ };
242
+ /** One bucket in an S3 store: path-style requests, a put streamed once with an unsigned payload, a listing paged as it is walked. */
243
+ export declare class S3Bucket implements Bucket {
244
+ #private;
245
+ readonly name: string | undefined;
246
+ readonly exposure: BucketExposure;
247
+ constructor(target: S3Target, name: string | undefined, exposure: BucketExposure, publicUrl: string | undefined, timeoutMs?: number);
248
+ put(objectName: string, body: ReadableStream<Uint8Array> | Uint8Array, options: PutOptions): Promise<void>;
249
+ get(objectName: string): Promise<StoredObject | undefined>;
250
+ stat(objectName: string): Promise<ObjectInfo | undefined>;
251
+ delete(objectName: string): Promise<void>;
252
+ list(prefix: string): AsyncIterable<ObjectEntry>;
253
+ signedUrl(objectName: string, access: SignedAccess, spanSeconds?: number): string;
254
+ url(objectName: string): string;
255
+ }
256
+ /** The S3 store's buckets, by name, from `s3Settings`. */
257
+ export declare class S3Buckets implements Buckets {
258
+ #private;
259
+ constructor(settings?: S3Settings);
260
+ get default(): Bucket;
261
+ bucket(name: string): Bucket;
262
+ get buckets(): Bucket[];
263
+ }
264
+ /** What stands behind every bucket of a service. */
265
+ export type StorageBackend = "s3" | "directory" | "memory" | "unavailable";
266
+ export interface ChosenStorage {
267
+ readonly backend: StorageBackend;
268
+ readonly buckets: Buckets;
269
+ }
270
+ export interface StorageChoiceOptions {
271
+ /** Where `service.json` is read from, for the buckets it declares: the working directory by default. */
272
+ readonly serviceDirectory?: string;
273
+ /** The service's own address, for the directory store's URLs. */
274
+ readonly addressBase?: () => string;
275
+ }
276
+ /**
277
+ * The one place a process's store is chosen, as the runtime chooses its own: `ANKKA_STORAGE_BACKEND` when
278
+ * set (`s3`, `directory` or `memory`); otherwise S3 when any `ANKKA_S3_*` bucket is given, the directory
279
+ * store (`ANKKA_STORAGE_DIR`, default `.ankka/storage`) on a developer's machine — anywhere Kubernetes did
280
+ * not start the process — and no store at all on the platform. Throws, naming the variable, when the
281
+ * settings cannot be read: a service that cannot reach its buckets does not start.
282
+ */
283
+ export declare function chooseStorage(env?: Env, options?: StorageChoiceOptions): ChosenStorage;
284
+ /** The store the server chose for this process, which every component's `storage` answers unless a test gave it one. */
285
+ export declare function processStorage(): Buckets;
286
+ /** @internal The server's: the store this process was given. */
287
+ export declare function _setProcessStorage(buckets: Buckets): void;
288
+ export {};