@loams/client 0.1.0 → 0.1.2

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 (56) hide show
  1. package/README.md +104 -4
  2. package/dist/app.d.ts +149 -0
  3. package/dist/app.js +317 -0
  4. package/dist/builders/filter.d.ts +96 -0
  5. package/dist/builders/filter.js +190 -0
  6. package/dist/builders/schema.d.ts +50 -0
  7. package/dist/builders/schema.js +92 -0
  8. package/dist/builders/search.d.ts +159 -0
  9. package/dist/builders/search.js +307 -0
  10. package/dist/gen/facade.d.ts +267 -2
  11. package/dist/gen/facade.js +194 -0
  12. package/dist/handle.d.ts +67 -0
  13. package/dist/handle.js +107 -0
  14. package/dist/index.d.ts +13 -5
  15. package/dist/index.js +13 -3
  16. package/dist/loams.d.ts +78 -3
  17. package/dist/loams.js +84 -6
  18. package/dist/node.js +1 -1
  19. package/dist/runtime/batch.d.ts +29 -0
  20. package/dist/runtime/batch.js +95 -0
  21. package/dist/runtime/call.d.ts +63 -9
  22. package/dist/runtime/call.js +243 -41
  23. package/dist/runtime/consistency.d.ts +65 -11
  24. package/dist/runtime/consistency.js +212 -46
  25. package/dist/runtime/errors.d.ts +29 -2
  26. package/dist/runtime/errors.js +26 -0
  27. package/dist/runtime/machine-key.d.ts +33 -0
  28. package/dist/runtime/machine-key.js +174 -0
  29. package/dist/runtime/pagination.js +8 -2
  30. package/dist/runtime/subscribe.d.ts +37 -0
  31. package/dist/runtime/subscribe.js +128 -0
  32. package/dist/runtime/token-source.d.ts +1 -1
  33. package/dist/runtime/token-source.js +1 -1
  34. package/dist/runtime/transports.d.ts +22 -1
  35. package/dist/runtime/transports.js +28 -2
  36. package/dist/vendor/proto/collection-v1.d.ts +3 -0
  37. package/dist/vendor/proto/collection-v1.js +7 -0
  38. package/dist/vendor/proto/gen/loams/auth/v1/auth_pb.d.ts +7 -2
  39. package/dist/vendor/proto/gen/loams/auth/v1/auth_pb.js +5 -1
  40. package/dist/vendor/proto/gen/loams/collection/v1/collection_pb.d.ts +1610 -0
  41. package/dist/vendor/proto/gen/loams/collection/v1/collection_pb.js +399 -0
  42. package/dist/vendor/proto/gen/loams/collection/v1/document_pb.d.ts +1054 -0
  43. package/dist/vendor/proto/gen/loams/collection/v1/document_pb.js +327 -0
  44. package/dist/vendor/proto/gen/loams/collection/v1/query_pb.d.ts +2101 -0
  45. package/dist/vendor/proto/gen/loams/collection/v1/query_pb.js +608 -0
  46. package/dist/vendor/proto/gen/loams/graph/v1/graph_pb.d.ts +2299 -0
  47. package/dist/vendor/proto/gen/loams/graph/v1/graph_pb.js +596 -0
  48. package/dist/vendor/proto/gen/loams/instance/v1/instance_pb.d.ts +23 -7
  49. package/dist/vendor/proto/gen/loams/instance/v1/instance_pb.js +20 -5
  50. package/dist/vendor/proto/gen/loams/operations/v1/operations_pb.d.ts +399 -0
  51. package/dist/vendor/proto/gen/loams/operations/v1/operations_pb.js +118 -0
  52. package/dist/vendor/proto/gen/loams/sql/v1/sql_pb.d.ts +182 -0
  53. package/dist/vendor/proto/gen/loams/sql/v1/sql_pb.js +72 -0
  54. package/dist/vendor/proto/gen/loams/stream/v1/stream_pb.d.ts +688 -0
  55. package/dist/vendor/proto/gen/loams/stream/v1/stream_pb.js +180 -0
  56. package/package.json +2 -2
package/dist/loams.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import type { Transport } from '@connectrpc/connect';
2
- import { type AdminAgentsModule, type AdminAuditModule, type AdminKeysModule, type AdminOrgModule, type AdminProjectsModule, type AuthModule, type CallBinding, type InstanceModule, type LiveAdminModule, type LiveModule, type ModuleBinding, type TablesModule } from './gen/facade.js';
2
+ import { type AdminAgentsModule, type AdminAuditModule, type AdminKeysModule, type AdminOrgModule, type AdminProjectsModule, type AuthModule, type CallBinding, type CollectionsModule, type DocumentsModule, type GraphModule, type SqlModule, type StreamsModule, type NamespacesModule, type SearchModule, type VectorModule, type InstanceModule, type LiveAdminModule, type LiveModule, type ModuleBinding, type TablesModule } from './gen/facade.js';
3
+ import { type documentCalls, type searchCalls, type sqlCalls, type streamCalls } from './app.js';
4
+ import { CollectionHandle } from './handle.js';
3
5
  import { type CallOptions, type ConsistencyTokenStore } from './runtime/call.js';
4
6
  import { type PageRequestOptions } from './runtime/pagination.js';
5
7
  import { SystemApi } from './system.js';
@@ -20,7 +22,7 @@ export interface LoamsOptions {
20
22
  auth?: TokenSource | string;
21
23
  /**
22
24
  * The transport. Defaults to Connect over `fetch` pointed at `endpoint`,
23
- * which is the browser, Node, Deno and Bun path (see `runtime/transports.ts`).
25
+ * which is the browser, Node and Bun path (see `runtime/transports.ts`).
24
26
  * `@loams/client/node` exports `createNodeTransport` for HTTP/2 gRPC.
25
27
  *
26
28
  * A transport carries its own base URL, so a transport passed here must be
@@ -38,8 +40,29 @@ export interface LoamsOptions {
38
40
  * read-your-writes across processes.
39
41
  */
40
42
  sessionConsistency?: boolean;
41
- /** The client's deadline, when the transport does not set one. */
43
+ /**
44
+ * The deadline of every unary call that does not set its own `timeoutMs`
45
+ * (runtime contract R12). Default 30 000 ms; `0` disables it. Streams have
46
+ * no default deadline. The budget covers a call's retries, and a call that
47
+ * runs out raises `DeadlineExceededError` with reason
48
+ * `client_deadline_exceeded`, which is never retried.
49
+ */
42
50
  timeoutMs?: number;
51
+ /**
52
+ * The namespace of every request that has a `namespace` field and leaves it
53
+ * empty (runtime contract R11). An explicit `namespace` always wins; with
54
+ * neither, such a call fails with `InvalidArgumentError` before any network
55
+ * call.
56
+ */
57
+ namespace?: string;
58
+ }
59
+ /** The environment `Loams.fromEnv` reads (R1). */
60
+ export interface LoamsEnvironment {
61
+ readonly LOAMS_ENDPOINT?: string;
62
+ readonly LOAMS_API_KEY?: string;
63
+ readonly LOAMS_TOKEN?: string;
64
+ readonly LOAMS_NAMESPACE?: string;
65
+ readonly [name: string]: string | undefined;
43
66
  }
44
67
  export type { Catalogue, VersionReport } from './system.js';
45
68
  /** The generated modules, keyed by name. A module the generator has not seen
@@ -56,6 +79,27 @@ export type LiveNamespace = LiveModule & {
56
79
  /** `loams.live.admin` — apps, deploy, roll back, index status and logs. */
57
80
  readonly admin: LiveAdminModule;
58
81
  };
82
+ /** A paged call's `<call>All` iterator, which `attachAppCalls` adds. */
83
+ type AllOf<Item> = (request?: object, options?: CallOptions) => AsyncGenerator<Item, void, undefined>;
84
+ /** `loams.namespaces`, with `listAll`. */
85
+ export type NamespacesNamespace = NamespacesModule & {
86
+ readonly listAll: AllOf<unknown>;
87
+ };
88
+ /** `loams.collections`, with `listAll` and `listVersionsAll`. */
89
+ export type CollectionsNamespace = CollectionsModule & {
90
+ readonly listAll: AllOf<unknown>;
91
+ readonly listVersionsAll: AllOf<unknown>;
92
+ };
93
+ /** `loams.documents`, with the hand-written conveniences (SDKP §2.3). */
94
+ export type DocumentsNamespace = DocumentsModule & ReturnType<typeof documentCalls>;
95
+ /** `loams.search`, with `text`, `hybrid` and `queryAll` (SDKP §2.4). */
96
+ export type SearchNamespace = SearchModule & ReturnType<typeof searchCalls>;
97
+ /** `loams.sql`, with `records` (SDKP §2.5). */
98
+ export type SqlNamespace = SqlModule & ReturnType<typeof sqlCalls>;
99
+ /** `loams.streams`, with `subscribe` and `<call>All` (SDKP §2.5, R14). */
100
+ export type StreamsNamespace = StreamsModule & ReturnType<typeof streamCalls> & {
101
+ readonly listAll: AllOf<unknown>;
102
+ };
59
103
  /** `loams.admin`: the console's administration modules (design §44 §7.2). */
60
104
  export interface AdminNamespace {
61
105
  readonly agents: AdminAgentsModule;
@@ -78,13 +122,44 @@ export declare class Loams {
78
122
  readonly admin: AdminNamespace;
79
123
  /** `loams.auth` — sessions and sign-in. */
80
124
  readonly auth: AuthModule;
125
+ /** `loams.namespaces` — the containers every collection and stream live in. */
126
+ readonly namespaces: NamespacesNamespace;
127
+ /** `loams.collections` — collections, their schemas, aliases and versions. */
128
+ readonly collections: CollectionsNamespace;
129
+ /** `loams.documents` — write, read, scroll and count documents. */
130
+ readonly documents: DocumentsNamespace;
131
+ /** `loams.search` — text, vector and hybrid search (`Search` builds one). */
132
+ readonly search: SearchNamespace;
133
+ /** `loams.vector` — the kNN half of `search`, by its own name (Q613). */
134
+ readonly vector: VectorModule;
135
+ /** `loams.graph` — Loams Graph, queried with GQL. Its package is `unstable`. */
136
+ readonly graph: GraphModule;
137
+ /** `loams.sql` — one read-only SQL statement over a namespace's collections. */
138
+ readonly sql: SqlNamespace;
139
+ /** `loams.streams` — a namespace's logs: produce, fetch, CloudEvents and `subscribe`. */
140
+ readonly streams: StreamsNamespace;
81
141
  /** The module catalogue, feature detection and the version check. */
82
142
  readonly system: SystemApi;
83
143
  /** Every generated module, by its dotted name (`'live.admin'`): the
84
144
  * catalogue a caller iterates. The same objects are reachable as
85
145
  * properties, `loams.live.admin` for `'live.admin'`. */
86
146
  readonly modules: Readonly<Record<string, unknown>>;
147
+ /** The default namespace (R11), if the client has one. */
148
+ readonly namespace: string | undefined;
87
149
  constructor(options: LoamsOptions);
150
+ /**
151
+ * A client from the environment: `LOAMS_ENDPOINT`, then `LOAMS_API_KEY` or
152
+ * `LOAMS_TOKEN`, then `LOAMS_NAMESPACE` (R1). `options` wins over the
153
+ * environment where both say something.
154
+ */
155
+ static fromEnv(options?: Partial<LoamsOptions>, environment?: LoamsEnvironment): Loams;
156
+ /**
157
+ * A handle on one collection: its document and search calls with
158
+ * `collection` (and `namespace`) filled in (SDKP §2.8). Holds no state.
159
+ */
160
+ collection(name: string, options?: {
161
+ namespace?: string;
162
+ }): CollectionHandle;
88
163
  /** The session consistency token store, when `sessionConsistency` is on. */
89
164
  get consistency(): ConsistencyTokenStore | undefined;
90
165
  /** The generated module bindings, as the SDK sees them. */
package/dist/loams.js CHANGED
@@ -16,13 +16,15 @@
16
16
  // why annotating a proto is enough to add an SDK method.
17
17
  import { Code } from '@connectrpc/connect';
18
18
  import { MODULES, PROTO_PACKAGES, PROTO_REV, SERVICE_DESCRIPTORS, } from './gen/facade.js';
19
- import { CallInvoker } from './runtime/call.js';
19
+ import { attachAppCalls } from './app.js';
20
+ import { CollectionHandle } from './handle.js';
21
+ import { CallInvoker, DEFAULT_TIMEOUT_MS, } from './runtime/call.js';
20
22
  import { ConsistencySession } from './runtime/consistency.js';
21
23
  import { LoamsError } from './runtime/errors.js';
22
24
  import { paginate } from './runtime/pagination.js';
23
25
  import { SystemApi } from './system.js';
24
26
  import { DEFAULT_MAX_RETRIES } from './runtime/retry.js';
25
- import { apiKey } from './runtime/token-source.js';
27
+ import { apiKey, envToken } from './runtime/token-source.js';
26
28
  import { createLoamsTransport } from './runtime/transports.js';
27
29
  import { watch } from './runtime/streams.js';
28
30
  /** One SDK, over one instance. */
@@ -38,12 +40,30 @@ export class Loams {
38
40
  admin;
39
41
  /** `loams.auth` — sessions and sign-in. */
40
42
  auth;
43
+ /** `loams.namespaces` — the containers every collection and stream live in. */
44
+ namespaces;
45
+ /** `loams.collections` — collections, their schemas, aliases and versions. */
46
+ collections;
47
+ /** `loams.documents` — write, read, scroll and count documents. */
48
+ documents;
49
+ /** `loams.search` — text, vector and hybrid search (`Search` builds one). */
50
+ search;
51
+ /** `loams.vector` — the kNN half of `search`, by its own name (Q613). */
52
+ vector;
53
+ /** `loams.graph` — Loams Graph, queried with GQL. Its package is `unstable`. */
54
+ graph;
55
+ /** `loams.sql` — one read-only SQL statement over a namespace's collections. */
56
+ sql;
57
+ /** `loams.streams` — a namespace's logs: produce, fetch, CloudEvents and `subscribe`. */
58
+ streams;
41
59
  /** The module catalogue, feature detection and the version check. */
42
60
  system;
43
61
  /** Every generated module, by its dotted name (`'live.admin'`): the
44
62
  * catalogue a caller iterates. The same objects are reachable as
45
63
  * properties, `loams.live.admin` for `'live.admin'`. */
46
64
  modules;
65
+ /** The default namespace (R11), if the client has one. */
66
+ namespace;
47
67
  #invoker;
48
68
  #consistency;
49
69
  constructor(options) {
@@ -59,20 +79,62 @@ export class Loams {
59
79
  // that asks for the session's consistency token reads and records through
60
80
  // the same store the client exposes.
61
81
  this.#consistency = options.sessionConsistency === true ? new ConsistencySession() : undefined;
62
- this.#invoker = new CallInvoker(transport, tokenSource, options.maxRetries ?? DEFAULT_MAX_RETRIES, this.#consistency);
82
+ this.#invoker = new CallInvoker(transport, tokenSource, options.maxRetries ?? DEFAULT_MAX_RETRIES, this.#consistency, {
83
+ namespace: options.namespace,
84
+ timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
85
+ });
86
+ this.namespace = options.namespace;
63
87
  this.modules = buildModules(this.#invoker);
88
+ attachAppCalls(this.modules, MODULES);
64
89
  const tree = nestModules(this.modules);
65
90
  this.instance = tree.instance;
66
91
  this.live = tree.live;
67
92
  this.tables = tree.tables;
68
93
  this.admin = tree.admin;
69
94
  this.auth = tree.auth;
95
+ this.namespaces = tree.namespaces;
96
+ this.collections = tree.collections;
97
+ this.documents = tree.documents;
98
+ this.search = tree.search;
99
+ this.vector = tree.vector;
100
+ this.graph = tree.graph;
101
+ this.sql = tree.sql;
102
+ this.streams = tree.streams;
70
103
  this.system = new SystemApi((request, callOptions) => this.instance.getInstance(request, callOptions), {
71
104
  endpoint: options.endpoint,
72
105
  maxRetries: options.maxRetries,
73
106
  timeoutMs: options.timeoutMs,
74
107
  });
75
108
  }
109
+ /**
110
+ * A client from the environment: `LOAMS_ENDPOINT`, then `LOAMS_API_KEY` or
111
+ * `LOAMS_TOKEN`, then `LOAMS_NAMESPACE` (R1). `options` wins over the
112
+ * environment where both say something.
113
+ */
114
+ static fromEnv(options = {}, environment = readEnvironment()) {
115
+ const endpoint = options.endpoint ?? environment.LOAMS_ENDPOINT;
116
+ if (endpoint === undefined || endpoint === '') {
117
+ throw new LoamsError('Loams.fromEnv: LOAMS_ENDPOINT is not set', {
118
+ code: Code.InvalidArgument,
119
+ metadata: { field: 'LOAMS_ENDPOINT' },
120
+ });
121
+ }
122
+ const hasKey = (environment.LOAMS_API_KEY ?? '') !== '' || (environment.LOAMS_TOKEN ?? '') !== '';
123
+ const namespace = options.namespace ?? environment.LOAMS_NAMESPACE;
124
+ return new Loams({
125
+ ...options,
126
+ endpoint,
127
+ ...(options.auth === undefined && hasKey ? { auth: envToken(environment) } : {}),
128
+ ...(namespace === undefined || namespace === '' ? {} : { namespace }),
129
+ });
130
+ }
131
+ /**
132
+ * A handle on one collection: its document and search calls with
133
+ * `collection` (and `namespace`) filled in (SDKP §2.8). Holds no state.
134
+ */
135
+ collection(name, options = {}) {
136
+ return new CollectionHandle(this.modules, name, options);
137
+ }
76
138
  /** The session consistency token store, when `sessionConsistency` is on. */
77
139
  get consistency() {
78
140
  return this.#consistency;
@@ -130,6 +192,12 @@ export class Loams {
130
192
  this.system.invalidate();
131
193
  }
132
194
  }
195
+ /** The process environment, where there is one (Node, Bun); empty in a
196
+ * browser. */
197
+ function readEnvironment() {
198
+ const scope = globalThis;
199
+ return scope.process?.env ?? {};
200
+ }
133
201
  /**
134
202
  * Builds one object per generated module: one function per generated call,
135
203
  * each delegating to the same invoker.
@@ -141,11 +209,21 @@ export class Loams {
141
209
  function buildModules(invoker) {
142
210
  const modules = {};
143
211
  for (const module of MODULES) {
144
- const descriptor = SERVICE_DESCRIPTORS[module.service];
145
- if (descriptor === undefined) {
212
+ // A module may span two services (`loams.graph` is `GraphAdminService`
213
+ // and `GraphService`), so every call's own service is registered.
214
+ const services = new Set([module.service, ...module.calls.map((call) => call.service)]);
215
+ let missing = false;
216
+ for (const service of services) {
217
+ const descriptor = SERVICE_DESCRIPTORS[service];
218
+ if (descriptor === undefined) {
219
+ missing = true;
220
+ continue;
221
+ }
222
+ invoker.register(service, descriptor);
223
+ }
224
+ if (missing) {
146
225
  continue;
147
226
  }
148
- invoker.register(module.service, descriptor);
149
227
  const calls = {
150
228
  module: module.name,
151
229
  service: module.service,
package/dist/node.js CHANGED
@@ -1,4 +1,4 @@
1
- // `@loams/client/node`: the Node, Deno and Bun entry point.
1
+ // `@loams/client/node`: the Node and Bun entry point.
2
2
  //
3
3
  // import { Loams, createNodeTransport } from '@loams/client/node';
4
4
  //
@@ -0,0 +1,29 @@
1
+ import type { CallOptions } from './call.js';
2
+ /** The default chunk bounds (§44 §7.5). */
3
+ export declare const DEFAULT_CHUNK_ROWS = 10000;
4
+ export declare const DEFAULT_CHUNK_BYTES: number;
5
+ /** How `writeBatch` chunks. */
6
+ export interface BatchOptions {
7
+ /** The most operations in one chunk. Default 10 000. */
8
+ chunkRows?: number;
9
+ /** The most bytes of operation JSON in one chunk. Default 4 MiB. A single
10
+ * operation larger than this goes in a chunk of its own. */
11
+ chunkBytes?: number;
12
+ }
13
+ /** What a finished batch reports. */
14
+ export interface BatchResult {
15
+ /** The merge of every chunk's consistency token. */
16
+ readonly token: string | undefined;
17
+ /** How many chunks were written. */
18
+ readonly chunks: number;
19
+ /** How many operations those chunks carried. */
20
+ readonly rows: number;
21
+ }
22
+ /** Sends one chunk; `idempotencyKey` is that chunk's own key. */
23
+ export type ChunkWriter<Op> = (ops: Op[], options: CallOptions & {
24
+ idempotencyKey: string;
25
+ }) => Promise<{
26
+ token?: string;
27
+ } | undefined>;
28
+ /** Splits `ops` into chunks and writes them one after another. */
29
+ export declare function writeBatch<Op>(write: ChunkWriter<Op>, ops: Iterable<Op> | AsyncIterable<Op>, options?: BatchOptions & CallOptions): Promise<BatchResult>;
@@ -0,0 +1,95 @@
1
+ // `documents.writeBatch` (runtime contract R13, SDKP §2.3; design §44 §7.5).
2
+ //
3
+ // A bulk load as a loop of ordinary `WriteDocuments` calls, chunked by rows
4
+ // and by bytes:
5
+ //
6
+ // - **One chunk in flight at a time.** Order is preserved and a failure stops
7
+ // the load at a known point, which concurrency would blur.
8
+ // - **One idempotency key per chunk**, minted before the chunk's first attempt
9
+ // and reused on every retry of that chunk, so a retried chunk is the same
10
+ // write (D610) and two chunks are never mistaken for one.
11
+ // - **The tokens merge.** The result is the merge of every chunk's
12
+ // consistency token, so a read held to it sees the whole load.
13
+ // - **The first failed chunk stops the load** with a `BatchError` that says
14
+ // how many chunks (and documents) were applied before it, and carries their
15
+ // merged token. Those chunks stay applied; nothing is rolled back.
16
+ //
17
+ // Browser-safe: sizes are measured with `TextEncoder`, not `Buffer`.
18
+ import { Code } from '@connectrpc/connect';
19
+ import { ConsistencyToken } from './consistency.js';
20
+ import { BatchError, toLoamsError } from './errors.js';
21
+ import { uuidv7 } from './uuidv7.js';
22
+ /** The default chunk bounds (§44 §7.5). */
23
+ export const DEFAULT_CHUNK_ROWS = 10_000;
24
+ export const DEFAULT_CHUNK_BYTES = 4 * 1024 * 1024;
25
+ const encoder = new TextEncoder();
26
+ function sizeOf(op) {
27
+ return encoder.encode(JSON.stringify(op, (_key, value) => typeof value === 'bigint' ? value.toString() : value)).length;
28
+ }
29
+ function mergeToken(current, next) {
30
+ if (next === undefined || next === '') {
31
+ return current;
32
+ }
33
+ try {
34
+ const parsed = ConsistencyToken.parse(next);
35
+ return current === undefined ? parsed.normalized() : current.merge(parsed);
36
+ }
37
+ catch {
38
+ return current;
39
+ }
40
+ }
41
+ /** Splits `ops` into chunks and writes them one after another. */
42
+ export async function writeBatch(write, ops, options = {}) {
43
+ const { chunkRows = DEFAULT_CHUNK_ROWS, chunkBytes = DEFAULT_CHUNK_BYTES, ...call } = options;
44
+ if (!(chunkRows > 0) || !(chunkBytes > 0)) {
45
+ throw new RangeError('writeBatch: chunkRows and chunkBytes must be positive');
46
+ }
47
+ let token;
48
+ let chunks = 0;
49
+ let rows = 0;
50
+ let pending = [];
51
+ let pendingBytes = 0;
52
+ const flush = async () => {
53
+ if (pending.length === 0) {
54
+ return;
55
+ }
56
+ const chunk = pending;
57
+ pending = [];
58
+ pendingBytes = 0;
59
+ try {
60
+ // The key is minted here, once per chunk, and the invoker reuses it on
61
+ // every retry of this chunk.
62
+ const answer = await write(chunk, { ...call, idempotencyKey: uuidv7() });
63
+ token = mergeToken(token, answer?.token);
64
+ }
65
+ catch (thrown) {
66
+ const cause = toLoamsError(thrown);
67
+ throw new BatchError(`writeBatch stopped at chunk ${chunks + 1}: ${chunks} chunks (${rows} operations) were applied before it; ${cause.message}`, {
68
+ code: cause.code === Code.Unknown ? Code.Aborted : cause.code,
69
+ reason: 'batch_partially_applied',
70
+ metadata: {
71
+ chunks_applied: String(chunks),
72
+ rows_applied: String(rows),
73
+ ...(cause.reason === undefined ? {} : { cause_reason: cause.reason }),
74
+ },
75
+ rpc: cause.rpc,
76
+ cause,
77
+ chunksApplied: chunks,
78
+ rowsApplied: rows,
79
+ token: token?.toString(),
80
+ });
81
+ }
82
+ chunks += 1;
83
+ rows += chunk.length;
84
+ };
85
+ for await (const op of ops) {
86
+ const size = sizeOf(op);
87
+ if (pending.length > 0 && (pending.length >= chunkRows || pendingBytes + size > chunkBytes)) {
88
+ await flush();
89
+ }
90
+ pending.push(op);
91
+ pendingBytes += size;
92
+ }
93
+ await flush();
94
+ return { token: token?.toString(), chunks, rows };
95
+ }
@@ -26,6 +26,14 @@ export interface CallOptions extends ConnectCallOptions {
26
26
  /** The consistency token to read at, and the store to record writes in
27
27
  * (§44 §7.4, D609). */
28
28
  consistency?: ConsistencyOptions;
29
+ /**
30
+ * This call's deadline in milliseconds (runtime contract R12). Unary calls
31
+ * default to the client's `timeoutMs` (30 s); streams have none unless one is
32
+ * given here. `0` means no deadline. The whole call, retries included, has to
33
+ * fit: a call that runs out raises `DeadlineExceededError` with reason
34
+ * `client_deadline_exceeded` and is never retried.
35
+ */
36
+ timeoutMs?: number;
29
37
  }
30
38
  /** How consistency tokens are carried (D609). */
31
39
  export interface ConsistencyOptions {
@@ -33,15 +41,18 @@ export interface ConsistencyOptions {
33
41
  * / `'eventual'` to read without waiting. */
34
42
  token?: string;
35
43
  /** Record the consistency token every response carries, so later reads are
36
- * read-your-writes without the caller threading tokens by hand. */
44
+ * read-your-writes without the caller threading tokens by hand. Unset means
45
+ * the client's session, when `sessionConsistency` is on; `false` opts this
46
+ * call out. */
37
47
  session?: ConsistencyTokenStore | true | false;
38
48
  }
39
49
  /** A session's merged consistency token (D609). Off unless a call opts in. */
40
50
  export interface ConsistencyTokenStore {
41
- /** The token to attach to the next read, or undefined for none. */
42
- current(): string | undefined;
43
- /** Folds a token the server returned into the session's. */
44
- record(token: string | undefined): void;
51
+ /** The token to attach to the next read in `scope` (a proto package), or
52
+ * undefined for none. */
53
+ current(scope?: string): string | undefined;
54
+ /** Folds a token the server returned, in `scope`, into the session's. */
55
+ record(token: string | undefined, scope?: string): void;
45
56
  }
46
57
  /** One attempt of a call, as the transport sees it. */
47
58
  export type Send = (attempt: Attempt) => Promise<unknown>;
@@ -60,6 +71,8 @@ export interface RetryPlan {
60
71
  readonly maxRetries: number;
61
72
  readonly signal?: AbortSignal;
62
73
  readonly onRefresh?: () => Promise<void>;
74
+ /** True once the call's own deadline has passed: no further attempt. */
75
+ readonly expired?: () => boolean;
63
76
  }
64
77
  /**
65
78
  * Runs `send` until it answers or the plan says stop, and returns whatever it
@@ -87,6 +100,31 @@ declared?: boolean): {
87
100
  request: unknown;
88
101
  keyed: boolean;
89
102
  };
103
+ /** The unary deadline when neither the client nor the call sets one (R12). */
104
+ export declare const DEFAULT_TIMEOUT_MS = 30000;
105
+ /** The response header a consistency token is read from (R4). */
106
+ export declare const CONSISTENCY_TOKEN_HEADER = "loams-consistency-token";
107
+ /**
108
+ * The packages whose `namespace` field is a Loams namespace (R11) and whose
109
+ * `consistency` field reads at a consistency token (R4). Other packages have
110
+ * fields of the same name that mean something else: `loams.live.v1`'s
111
+ * `CreateAppRequest.namespace` is free text naming an app's environment, and
112
+ * must never be filled from, or refused for lack of, the client's default.
113
+ */
114
+ export declare const APP_PACKAGES: ReadonlySet<string>;
115
+ /**
116
+ * The packages whose reads take the session's token as `at_least` (R4,
117
+ * SDKP-17): collections and SQL. A graph's token names one graph's stream and
118
+ * a Graph read refuses any other, so graph reads never take the session's.
119
+ */
120
+ export declare const SESSION_PACKAGES: ReadonlySet<string>;
121
+ /** Options the invoker applies to every call (R11, R12). */
122
+ export interface InvokerDefaults {
123
+ /** The namespace a request with an empty `namespace` field is sent with. */
124
+ namespace?: string;
125
+ /** The unary deadline; `0` disables it. */
126
+ timeoutMs?: number;
127
+ }
90
128
  /**
91
129
  * The client's call path: a generated `CallBinding` plus a request becomes a
92
130
  * response message, with everything the SDK owns applied on the way.
@@ -98,17 +136,31 @@ export declare class CallInvoker {
98
136
  private readonly consistency;
99
137
  private readonly clients;
100
138
  private readonly descriptors;
101
- /** Whether a call's request schema declares `idempotency_key`, per RPC. */
102
- private readonly keyed;
103
- constructor(transport: Transport, tokenSource: TokenSource | undefined, maxRetries: number, consistency: ConsistencyTokenStore | undefined);
139
+ /** What each RPC's request schema declares, per RPC. */
140
+ private readonly shapes;
141
+ private readonly defaults;
142
+ constructor(transport: Transport, tokenSource: TokenSource | undefined, maxRetries: number, consistency: ConsistencyTokenStore | undefined, defaults?: InvokerDefaults);
143
+ /** The client's default namespace, if it has one (R11). */
144
+ get namespace(): string | undefined;
104
145
  /**
105
146
  * A module's Connect client, built once from the service descriptor
106
147
  * `protoc-gen-es` generated. This is what makes the facade generated rather
107
148
  * than hand-written: the SDK never writes a client, it wraps the stubs.
108
149
  */
109
150
  register(service: string, descriptor: DescService): void;
151
+ /** What this call's request message declares. */
152
+ private shape;
110
153
  /** Whether this call's request message declares an `idempotency_key`. */
111
154
  private takesIdempotencyKey;
155
+ /**
156
+ * The request as it goes out: the default namespace filled in (R11) and the
157
+ * session's token set as `consistency.at_least` when the caller left
158
+ * `consistency` unset (R4). A request the caller set either on is left as
159
+ * written: an explicit argument always wins.
160
+ */
161
+ private prepare;
162
+ /** The deadline a unary call runs under, in milliseconds, or undefined. */
163
+ private unaryTimeout;
112
164
  /** The connect-es method a binding names, or a clear internal error. */
113
165
  private method;
114
166
  /** One unary call: the whole runtime contract, applied to one binding. */
@@ -127,6 +179,8 @@ export declare class CallInvoker {
127
179
  stream(binding: CallBinding, request: unknown, options?: CallOptions): AsyncIterable<unknown>;
128
180
  /** Wraps an iterable so every throw becomes a `LoamsError`. */
129
181
  private mapped;
130
- /** Merges the consistency token a write answered with into the session. */
182
+ /** Merges every consistency token a response carried into the session:
183
+ * `consistency_token`, `token` and `read_token`, and the
184
+ * `loams-consistency-token` header (R4, SDKP §2.1). */
131
185
  private recordConsistency;
132
186
  }