@tier0/sdk-v2 0.1.0 → 0.2.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 (170) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +13 -11
  3. package/dist/cjs/app-identity-contract.generated.js.map +1 -1
  4. package/dist/cjs/capabilities.js.map +1 -1
  5. package/dist/cjs/cloud-server.js.map +1 -1
  6. package/dist/cjs/cloud-transport.d.ts +21 -0
  7. package/dist/cjs/cloud-transport.js +81 -1
  8. package/dist/cjs/cloud-transport.js.map +1 -1
  9. package/dist/cjs/controls.js.map +1 -1
  10. package/dist/cjs/data/bootstrap.d.ts +4 -0
  11. package/dist/cjs/data/bootstrap.js +15 -6
  12. package/dist/cjs/data/bootstrap.js.map +1 -1
  13. package/dist/cjs/data/call.d.ts +26 -9
  14. package/dist/cjs/data/call.js +179 -14
  15. package/dist/cjs/data/call.js.map +1 -1
  16. package/dist/cjs/data/client.d.ts +44 -2
  17. package/dist/cjs/data/client.js +87 -4
  18. package/dist/cjs/data/client.js.map +1 -1
  19. package/dist/cjs/data/collection.d.ts +151 -14
  20. package/dist/cjs/data/collection.js +213 -50
  21. package/dist/cjs/data/collection.js.map +1 -1
  22. package/dist/cjs/data/context.d.ts +4 -2
  23. package/dist/cjs/data/context.js +14 -3
  24. package/dist/cjs/data/context.js.map +1 -1
  25. package/dist/cjs/data/define.d.ts +9 -1
  26. package/dist/cjs/data/define.js.map +1 -1
  27. package/dist/cjs/data/error.d.ts +30 -12
  28. package/dist/cjs/data/error.js +61 -63
  29. package/dist/cjs/data/error.js.map +1 -1
  30. package/dist/cjs/data/index.d.ts +6 -3
  31. package/dist/cjs/data/index.js +7 -1
  32. package/dist/cjs/data/index.js.map +1 -1
  33. package/dist/cjs/data/schema.d.ts +7 -1
  34. package/dist/cjs/data/schema.js +54 -4
  35. package/dist/cjs/data/schema.js.map +1 -1
  36. package/dist/cjs/data/subscribe.d.ts +108 -0
  37. package/dist/cjs/data/subscribe.js +270 -0
  38. package/dist/cjs/data/subscribe.js.map +1 -0
  39. package/dist/cjs/mq/broker.js.map +1 -1
  40. package/dist/cjs/mq/client.js.map +1 -1
  41. package/dist/cjs/mq/index.js.map +1 -1
  42. package/dist/cjs/mq/types.js.map +1 -1
  43. package/dist/cjs/runtime-context.js.map +1 -1
  44. package/dist/cjs/runtime-env.js.map +1 -1
  45. package/dist/cjs/uns-v2/client.d.ts +90 -12
  46. package/dist/cjs/uns-v2/client.js +238 -63
  47. package/dist/cjs/uns-v2/client.js.map +1 -1
  48. package/dist/cjs/uns-v2/codec.js.map +1 -1
  49. package/dist/cjs/uns-v2/generated.d.ts +9619 -6923
  50. package/dist/cjs/uns-v2/generated.js +124 -119
  51. package/dist/cjs/uns-v2/generated.js.map +1 -1
  52. package/dist/cjs/uns-v2/index.js.map +1 -1
  53. package/dist/cjs/uns-v2/sse.d.ts +20 -0
  54. package/dist/cjs/uns-v2/sse.js +65 -0
  55. package/dist/cjs/uns-v2/sse.js.map +1 -0
  56. package/dist/esm/app-identity-contract.generated.js.map +1 -1
  57. package/dist/esm/capabilities.js.map +1 -1
  58. package/dist/esm/cloud-server.js.map +1 -1
  59. package/dist/esm/cloud-transport.d.ts +21 -0
  60. package/dist/esm/cloud-transport.js +81 -1
  61. package/dist/esm/cloud-transport.js.map +1 -1
  62. package/dist/esm/controls.js.map +1 -1
  63. package/dist/esm/data/bootstrap.d.ts +4 -0
  64. package/dist/esm/data/bootstrap.js +14 -5
  65. package/dist/esm/data/bootstrap.js.map +1 -1
  66. package/dist/esm/data/call.d.ts +26 -9
  67. package/dist/esm/data/call.js +177 -13
  68. package/dist/esm/data/call.js.map +1 -1
  69. package/dist/esm/data/client.d.ts +44 -2
  70. package/dist/esm/data/client.js +89 -7
  71. package/dist/esm/data/client.js.map +1 -1
  72. package/dist/esm/data/collection.d.ts +151 -14
  73. package/dist/esm/data/collection.js +212 -49
  74. package/dist/esm/data/collection.js.map +1 -1
  75. package/dist/esm/data/context.d.ts +4 -2
  76. package/dist/esm/data/context.js +13 -3
  77. package/dist/esm/data/context.js.map +1 -1
  78. package/dist/esm/data/define.d.ts +9 -1
  79. package/dist/esm/data/define.js.map +1 -1
  80. package/dist/esm/data/error.d.ts +30 -12
  81. package/dist/esm/data/error.js +61 -63
  82. package/dist/esm/data/error.js.map +1 -1
  83. package/dist/esm/data/index.d.ts +6 -3
  84. package/dist/esm/data/index.js +3 -1
  85. package/dist/esm/data/index.js.map +1 -1
  86. package/dist/esm/data/schema.d.ts +7 -1
  87. package/dist/esm/data/schema.js +54 -4
  88. package/dist/esm/data/schema.js.map +1 -1
  89. package/dist/esm/data/subscribe.d.ts +108 -0
  90. package/dist/esm/data/subscribe.js +265 -0
  91. package/dist/esm/data/subscribe.js.map +1 -0
  92. package/dist/esm/mq/broker.js.map +1 -1
  93. package/dist/esm/mq/client.js.map +1 -1
  94. package/dist/esm/mq/index.js.map +1 -1
  95. package/dist/esm/mq/types.js.map +1 -1
  96. package/dist/esm/runtime-context.js.map +1 -1
  97. package/dist/esm/runtime-env.js.map +1 -1
  98. package/dist/esm/uns-v2/client.d.ts +90 -12
  99. package/dist/esm/uns-v2/client.js +236 -63
  100. package/dist/esm/uns-v2/client.js.map +1 -1
  101. package/dist/esm/uns-v2/codec.js.map +1 -1
  102. package/dist/esm/uns-v2/generated.d.ts +9619 -6923
  103. package/dist/esm/uns-v2/generated.js +123 -118
  104. package/dist/esm/uns-v2/generated.js.map +1 -1
  105. package/dist/esm/uns-v2/index.js.map +1 -1
  106. package/dist/esm/uns-v2/sse.d.ts +20 -0
  107. package/dist/esm/uns-v2/sse.js +60 -0
  108. package/dist/esm/uns-v2/sse.js.map +1 -0
  109. package/package.json +29 -16
  110. package/skills/tier0-sdk-v2/SKILL.md +14 -14
  111. package/skills/{tier0-sdk-v2-data → tier0-sdk-v2/tier0-sdk-v2-data}/SKILL.md +3 -2
  112. package/skills/{tier0-sdk-v2-data → tier0-sdk-v2/tier0-sdk-v2-data}/references/consistency.md +3 -3
  113. package/skills/{tier0-sdk-v2-data → tier0-sdk-v2/tier0-sdk-v2-data}/references/errors.md +23 -10
  114. package/skills/{tier0-sdk-v2-data → tier0-sdk-v2/tier0-sdk-v2-data}/references/modeling.md +36 -0
  115. package/skills/tier0-sdk-v2/tier0-sdk-v2-data/references/read-write.md +172 -0
  116. package/skills/{tier0-sdk-v2-mq → tier0-sdk-v2/tier0-sdk-v2-mq}/references/quickstart.md +1 -1
  117. package/skills/{tier0-sdk-v2-source → tier0-sdk-v2/tier0-sdk-v2-source}/SKILL.md +3 -3
  118. package/skills/{tier0-sdk-v2-source → tier0-sdk-v2/tier0-sdk-v2-source}/references/create.md +3 -6
  119. package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/delete.md +39 -0
  120. package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/state.md +36 -0
  121. package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/update.md +80 -0
  122. package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/SKILL.md +6 -6
  123. package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/create.md +15 -14
  124. package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/deploy.md +11 -9
  125. package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/lifecycle.md +2 -1
  126. package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/update.md +5 -6
  127. package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/SKILL.md +55 -0
  128. package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/create.md +57 -0
  129. package/skills/{tier0-sdk-v2-uns → tier0-sdk-v2/tier0-sdk-v2-uns}/references/data-workflows.md +3 -7
  130. package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/delete.md +53 -0
  131. package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/namespace.md +38 -0
  132. package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/read.md +34 -0
  133. package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/restore.md +39 -0
  134. package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/update.md +62 -0
  135. package/skills/tier0-sdk-v2-data/references/read-write.md +0 -72
  136. package/skills/tier0-sdk-v2-source/references/delete.md +0 -47
  137. package/skills/tier0-sdk-v2-source/references/state.md +0 -37
  138. package/skills/tier0-sdk-v2-source/references/update.md +0 -61
  139. package/skills/tier0-sdk-v2-uns/SKILL.md +0 -53
  140. package/skills/tier0-sdk-v2-uns/references/create.md +0 -85
  141. package/skills/tier0-sdk-v2-uns/references/delete.md +0 -69
  142. package/skills/tier0-sdk-v2-uns/references/namespace.md +0 -75
  143. package/skills/tier0-sdk-v2-uns/references/read.md +0 -32
  144. package/skills/tier0-sdk-v2-uns/references/restore.md +0 -41
  145. package/skills/tier0-sdk-v2-uns/references/update.md +0 -86
  146. /package/skills/{tier0-sdk-v2-agent → tier0-sdk-v2/tier0-sdk-v2-agent}/SKILL.md +0 -0
  147. /package/skills/{tier0-sdk-v2-agent → tier0-sdk-v2/tier0-sdk-v2-agent}/references/results.md +0 -0
  148. /package/skills/{tier0-sdk-v2-controls → tier0-sdk-v2/tier0-sdk-v2-controls}/SKILL.md +0 -0
  149. /package/skills/{tier0-sdk-v2-data → tier0-sdk-v2/tier0-sdk-v2-data}/references/schema-evolution.md +0 -0
  150. /package/skills/{tier0-sdk-v2-files → tier0-sdk-v2/tier0-sdk-v2-files}/SKILL.md +0 -0
  151. /package/skills/{tier0-sdk-v2-files → tier0-sdk-v2/tier0-sdk-v2-files}/references/delete.md +0 -0
  152. /package/skills/{tier0-sdk-v2-files → tier0-sdk-v2/tier0-sdk-v2-files}/references/download.md +0 -0
  153. /package/skills/{tier0-sdk-v2-files → tier0-sdk-v2/tier0-sdk-v2-files}/references/multipart.md +0 -0
  154. /package/skills/{tier0-sdk-v2-files → tier0-sdk-v2/tier0-sdk-v2-files}/references/upload.md +0 -0
  155. /package/skills/{tier0-sdk-v2-files → tier0-sdk-v2/tier0-sdk-v2-files}/references/url.md +0 -0
  156. /package/skills/{tier0-sdk-v2-members → tier0-sdk-v2/tier0-sdk-v2-members}/SKILL.md +0 -0
  157. /package/skills/{tier0-sdk-v2-members → tier0-sdk-v2/tier0-sdk-v2-members}/references/app-members.md +0 -0
  158. /package/skills/{tier0-sdk-v2-members → tier0-sdk-v2/tier0-sdk-v2-members}/references/platform-members.md +0 -0
  159. /package/skills/{tier0-sdk-v2-mq → tier0-sdk-v2/tier0-sdk-v2-mq}/SKILL.md +0 -0
  160. /package/skills/{tier0-sdk-v2-notifications → tier0-sdk-v2/tier0-sdk-v2-notifications}/SKILL.md +0 -0
  161. /package/skills/{tier0-sdk-v2-notifications → tier0-sdk-v2/tier0-sdk-v2-notifications}/references/get.md +0 -0
  162. /package/skills/{tier0-sdk-v2-notifications → tier0-sdk-v2/tier0-sdk-v2-notifications}/references/send.md +0 -0
  163. /package/skills/{tier0-sdk-v2-source → tier0-sdk-v2/tier0-sdk-v2-source}/references/changes.md +0 -0
  164. /package/skills/{tier0-sdk-v2-source → tier0-sdk-v2/tier0-sdk-v2-source}/references/protocols.md +0 -0
  165. /package/skills/{tier0-sdk-v2-source → tier0-sdk-v2/tier0-sdk-v2-source}/references/read.md +0 -0
  166. /package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/delete.md +0 -0
  167. /package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/read.md +0 -0
  168. /package/skills/{tier0-sdk-v2-system → tier0-sdk-v2/tier0-sdk-v2-system}/SKILL.md +0 -0
  169. /package/skills/{tier0-sdk-v2-system → tier0-sdk-v2/tier0-sdk-v2-system}/references/info.md +0 -0
  170. /package/skills/{tier0-sdk-v2-system → tier0-sdk-v2/tier0-sdk-v2-system}/references/whoami.md +0 -0
@@ -1,14 +1,19 @@
1
1
  import { getEnvVar } from '../runtime-env.js';
2
2
  import { createBootstrap } from './bootstrap.js';
3
- import { createDataCall } from './call.js';
3
+ import { createDataTransport } from './call.js';
4
4
  import { assertAtomicBatch, Collection, toDoc } from './collection.js';
5
- import { currentDataSubject } from './context.js';
5
+ import { currentDataSubject, currentSignal } from './context.js';
6
6
  import { MAX_COLLECTIONS } from './define.js';
7
- import { DataError } from './error.js';
7
+ import { createSubscribe } from './subscribe.js';
8
+ import { parseUnsV2Fault, UnsV2Error } from '../uns-v2/client.js';
9
+ import { DataError, toDataError } from './error.js';
8
10
  import { validateStepData, validateUpdate } from './schema.js';
9
11
  import { toSortPairs } from './sort.js';
12
+ /** Contract limit of `records:queryBatch`. */
13
+ export const MAX_BATCH_QUERIES = 20;
14
+ const QUERY_NAME = /^[A-Za-z0-9_.-]{1,64}$/;
10
15
  export function createDataClient(config) {
11
- const call = createDataCall(config);
16
+ const { call, openStream } = createDataTransport(config);
12
17
  const environment = config.environment ?? (getEnvVar('NODE_ENV') === 'production' ? 'deployed' : 'preview');
13
18
  const declared = [...config.collections];
14
19
  if (declared.length > MAX_COLLECTIONS) {
@@ -27,6 +32,7 @@ export function createDataClient(config) {
27
32
  resolve: (target) => bootstrap.resolve(lookup(target)),
28
33
  recover: bootstrap.recover,
29
34
  subject: currentDataSubject,
35
+ signal: currentSignal,
30
36
  };
31
37
  const handles = new Map();
32
38
  function lookup(target) {
@@ -61,10 +67,14 @@ export function createDataClient(config) {
61
67
  const expected = options.dryRun ? 'validated' : 'committed';
62
68
  if (result.status !== expected) {
63
69
  const failed = results.filter((item) => item.error);
70
+ // A sync transaction should only fail whole; map the rest of the Outcome statuses anyway.
71
+ const execution = result.status === 'unknown' ? 'unknown' : result.status === 'partiallyCommitted' ? 'committed' : 'rolledBack';
64
72
  throw new DataError({
65
73
  message: `Transaction ${result.status}: ${failed.map((item) => `${item.opId} ${JSON.stringify(item.error)}`).join('; ') || 'no step committed'}`,
66
- status: 409,
74
+ status: execution === 'unknown' ? 503 : 409,
67
75
  code: 'TRANSACTION_FAILED',
76
+ execution,
77
+ attempts: 1,
68
78
  details: { status: result.status, results },
69
79
  });
70
80
  }
@@ -92,8 +102,80 @@ export function createDataClient(config) {
92
102
  mode: 'sync',
93
103
  ...(options.dryRun ? { dryRun: true } : {}),
94
104
  ...(options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}),
95
- }, { dataSubject: currentDataSubject() });
105
+ }, { dataSubject: currentDataSubject(), signal: currentSignal() });
96
106
  }
97
- return { collection: collection, transact, ensure: bootstrap.ensure };
107
+ async function queryBatch(queries, options = {}) {
108
+ const names = Object.keys(queries);
109
+ if (names.length < 1 || names.length > MAX_BATCH_QUERIES) {
110
+ throw new TypeError(`data.queryBatch takes 1–${MAX_BATCH_QUERIES} queries; got ${names.length}.`);
111
+ }
112
+ const bad = names.find((name) => !QUERY_NAME.test(name));
113
+ if (bad !== undefined)
114
+ throw new TypeError(`data.queryBatch query name "${bad}" must match ${QUERY_NAME}.`);
115
+ for (const name of names)
116
+ lookup(queries[name].collection);
117
+ // Items resolve their collection IDs inside the recovered call, so a retry uses fresh IDs.
118
+ const { results } = await bootstrap.recover(async () => {
119
+ const items = await Promise.all(names.map(async (name) => ({
120
+ id: name,
121
+ collectionId: await bootstrap.resolve(queries[name].collection),
122
+ ...(await queries[name].prepare()),
123
+ })));
124
+ return call('recordQueryBatch', { queries: items }, { dataSubject: currentDataSubject(), signal: currentSignal() });
125
+ });
126
+ const settled = Object.fromEntries(names.map((name) => {
127
+ const result = results[name];
128
+ if (result?.ok === true)
129
+ return [name, { ok: true, value: queries[name].parse(result.data) }];
130
+ return [name, { ok: false, error: itemError(name, result?.error) }];
131
+ }));
132
+ if (options.settle)
133
+ return settled;
134
+ const failed = names.find((name) => !settled[name].ok);
135
+ if (failed !== undefined)
136
+ throw settled[failed].error;
137
+ return Object.fromEntries(names.map((name) => [name, settled[name].value]));
138
+ }
139
+ const subscribe = createSubscribe({
140
+ openStream,
141
+ ensure: bootstrap.ensure,
142
+ rebuild: bootstrap.rebuild,
143
+ lookup,
144
+ declared,
145
+ subject: currentDataSubject,
146
+ });
147
+ return {
148
+ collection: collection,
149
+ transact,
150
+ queryBatch: queryBatch,
151
+ subscribe,
152
+ ensure: bootstrap.ensure,
153
+ };
154
+ }
155
+ /** Item errors carry no HTTP status of their own; it follows from the code. */
156
+ const ITEM_STATUS = { FORBIDDEN: 403, NOT_FOUND: 404 };
157
+ /** A queryBatch item's error as a DataError; its `upstreamStatus` is the status its code stands for. */
158
+ function itemError(name, value) {
159
+ const fault = parseUnsV2Fault(value);
160
+ if (!fault) {
161
+ return new DataError({
162
+ message: `UNS answered query "${name}" of recordQueryBatch with an invalid result.`,
163
+ status: 502,
164
+ code: 'DATA_BAD_RESPONSE',
165
+ upstreamStatus: 200,
166
+ execution: 'rolledBack',
167
+ attempts: 1,
168
+ });
169
+ }
170
+ const error = new UnsV2Error(fault.reason ?? fault.code, {
171
+ ...fault,
172
+ // The items are reads: never an unknown outcome.
173
+ execution: fault.execution === 'unknown' ? 'rolledBack' : fault.execution,
174
+ queryId: fault.queryId ?? name,
175
+ status: ITEM_STATUS[fault.code] ?? 400,
176
+ operation: 'recordQueryBatch',
177
+ data: { queryId: fault.queryId ?? name, details: fault.details ?? [] },
178
+ });
179
+ return toDataError(error);
98
180
  }
99
181
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/data/client.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Data client factory: collection handles, cross-collection transactions and\n * the shared bootstrap. An App builds one client per process from its\n * collection declarations; tests build their own with a mock `fetch`.\n */\nimport type { z } from 'zod';\nimport type { ClientConfig } from '../openapi/client.js';\nimport { getEnvVar } from '../runtime-env.js';\nimport { createBootstrap, type DataEnvironment, type EnsureState, type Logger } from './bootstrap.js';\nimport { createDataCall } from './call.js';\nimport { assertAtomicBatch, Collection, toDoc, type UnsRecord, type WithMeta } from './collection.js';\nimport { currentDataSubject } from './context.js';\nimport { MAX_COLLECTIONS, type CollectionDefinition } from './define.js';\nimport { DataError } from './error.js';\nimport { validateStepData, validateUpdate } from './schema.js';\nimport { toSortPairs, type Filter, type Sort } from './sort.js';\n\nexport type TransactAction =\n | 'record.create'\n | 'record.check'\n | 'record.read'\n | 'record.update'\n | 'record.modify'\n | 'record.replace'\n | 'record.delete'\n | 'record.query';\n\n/** One `recordTransactionExecute` item; `collection` is a declaration or alias instead of a collection ID. */\nexport interface TransactStep {\n opId: string;\n action: TransactAction;\n collection: CollectionDefinition | string;\n id?: string | { ref: string };\n filter?: Filter;\n data?: Record<string, unknown>;\n update?: Record<string, unknown>;\n upsert?: boolean;\n expectedRevision?: number;\n dependsOn?: string[];\n bindings?: { path: string; ref: string; resultPath: string }[];\n selection?: 'exactOne' | 'first';\n sort?: Sort;\n returnDocument?: 'before' | 'after';\n exists?: boolean;\n operations?: Record<string, unknown>[];\n conditions?: Filter[];\n /** `record.query`: rows to bind from; `expectCount` asserts the match count before `limit`. */\n query?: { filter?: Filter; sort?: Sort; limit?: number; expectCount?: { min?: number; max?: number } };\n arrayFilters?: Record<string, unknown>[];\n}\n\nexport interface TransactResultItem {\n opId: string;\n action: string;\n id: string;\n status: string;\n collectionId?: string;\n revision?: number;\n /** Before/after snapshot when requested, as a flat document. */\n document?: WithMeta<Record<string, unknown>>;\n data?: unknown;\n query?: unknown;\n error?: unknown;\n}\n\nexport interface TransactResult {\n status: string;\n results: TransactResultItem[];\n}\n\n/** Credentials as for `createClient`; `transportTimeoutMs` defaults to 15 s here, the UNS statement timeout. */\nexport interface DataClientConfig extends ClientConfig {\n collections: readonly CollectionDefinition[];\n /** Defaults to \"deployed\" when NODE_ENV is production, otherwise \"preview\". */\n environment?: DataEnvironment;\n logger?: Logger;\n}\n\nexport interface DataClient {\n /** Typed handle for a declaration listed in `collections`. */\n collection<C extends CollectionDefinition>(definition: C): Collection<z.output<C['schema']>, z.input<C['schema']>>;\n /** Handle by alias, typed by the caller. */\n collection<T extends object = Record<string, unknown>>(alias: string): Collection<T>;\n /**\n * Atomic multi-record, multi-collection write (`recordTransactionExecute`, sync),\n * at most 200 steps. Steps can depend on each other (`dependsOn`) and bind\n * earlier results (`bindings`). This is not a Mongo session: everything is\n * sent at once.\n */\n transact(steps: readonly TransactStep[], options?: { dryRun?: boolean; idempotencyKey?: string }): Promise<TransactResult>;\n /** Ensures every declaration now (normally lazy on first access). */\n ensure(): Promise<EnsureState>;\n}\n\nexport function createDataClient(config: DataClientConfig): DataClient {\n const call = createDataCall(config);\n const environment = config.environment ?? (getEnvVar('NODE_ENV') === 'production' ? 'deployed' : 'preview');\n const declared = [...config.collections];\n if (declared.length > MAX_COLLECTIONS) {\n throw new Error(`An App declares at most ${MAX_COLLECTIONS} collections; got ${declared.length}.`);\n }\n const seen = new Set<string>();\n for (const definition of declared) {\n const key = definition.alias.toLowerCase();\n if (seen.has(key)) throw new Error(`Collection alias \"${definition.alias}\" is declared twice (aliases are case-insensitive).`);\n seen.add(key);\n }\n\n const bootstrap = createBootstrap({ call, collections: declared, environment, logger: config.logger ?? console });\n const runtime = {\n call,\n resolve: (target: CollectionDefinition | string) => bootstrap.resolve(lookup(target)),\n recover: bootstrap.recover,\n subject: currentDataSubject,\n };\n const handles = new Map<CollectionDefinition, Collection<object>>();\n\n function lookup(target: CollectionDefinition | string): CollectionDefinition {\n if (typeof target === 'string') {\n const found = declared.find((definition) => definition.alias.toLowerCase() === target.toLowerCase());\n if (!found) throw new Error(`Collection \"${target}\" is not declared in this data client's collections.`);\n return found;\n }\n if (!declared.includes(target)) {\n throw new Error(`Collection \"${target.alias}\" is declared but missing from the \\`collections\\` passed to createDataClient.`);\n }\n return target;\n }\n\n function collection(target: CollectionDefinition | string) {\n const definition = lookup(target);\n let handle = handles.get(definition);\n if (!handle) {\n handle = new Collection<object>(definition, runtime);\n handles.set(definition, handle);\n }\n return handle;\n }\n\n async function transact(\n steps: readonly TransactStep[],\n options: { dryRun?: boolean; idempotencyKey?: string } = {},\n ): Promise<TransactResult> {\n assertAtomicBatch('data.transact', steps.length);\n // Steps resolve their collection IDs inside the recovered call, so a retry uses fresh IDs.\n const result = await bootstrap.recover(() => sendTransaction(steps, options));\n const results = (result.results ?? []).map(({ document, ...item }) => ({\n ...item,\n ...(document ? { document: toDoc<Record<string, unknown>>(document) } : {}),\n }));\n const expected = options.dryRun ? 'validated' : 'committed';\n if (result.status !== expected) {\n const failed = results.filter((item) => item.error);\n throw new DataError({\n message: `Transaction ${result.status}: ${failed.map((item) => `${item.opId} ${JSON.stringify(item.error)}`).join('; ') || 'no step committed'}`,\n status: 409,\n code: 'TRANSACTION_FAILED',\n details: { status: result.status, results },\n });\n }\n return { status: result.status, results };\n }\n\n async function sendTransaction(steps: readonly TransactStep[], options: { dryRun?: boolean; idempotencyKey?: string }) {\n const items = await Promise.all(\n steps.map(async ({ collection: target, sort, query, ...step }) => {\n const definition = lookup(target);\n const item: Record<string, unknown> = { ...step, collectionId: await bootstrap.resolve(definition) };\n if (sort) item.sort = toSortPairs(sort);\n if (query) item.query = { ...query, ...(query.sort ? { sort: toSortPairs(query.sort) } : {}) };\n if (step.data) {\n // Fields bound to earlier results are filled by UNS at commit; everything literal validates now.\n const bound = (step.bindings ?? []).flatMap((binding) => (binding.path.startsWith('data.') ? [binding.path.slice(5).split('.')[0]] : []));\n item.data = validateStepData(definition, step.data, bound);\n }\n if (step.update) item.update = validateUpdate(definition, step.update);\n return item;\n }),\n );\n return call<{ status: string; results?: (Omit<TransactResultItem, 'document'> & { document?: UnsRecord })[] }>(\n 'recordTransactionExecute',\n {\n items,\n mode: 'sync',\n ...(options.dryRun ? { dryRun: true } : {}),\n ...(options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}),\n },\n { dataSubject: currentDataSubject() },\n );\n }\n\n return { collection: collection as DataClient['collection'], transact, ensure: bootstrap.ensure };\n}\n"]}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/data/client.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Data client factory: collection handles, cross-collection transactions and\n * the shared bootstrap. An App builds one client per process from its\n * collection declarations; tests build their own with a mock `fetch`.\n */\nimport type { z } from 'zod';\nimport type { ClientConfig } from '../openapi/client.js';\nimport { getEnvVar } from '../runtime-env.js';\nimport { createBootstrap, type DataEnvironment, type EnsureState, type Logger } from './bootstrap.js';\nimport { createDataTransport } from './call.js';\nimport { assertAtomicBatch, Collection, toDoc, type BatchQuery, type UnsRecord, type WithMeta } from './collection.js';\nimport { currentDataSubject, currentSignal } from './context.js';\nimport { MAX_COLLECTIONS, type CollectionDefinition } from './define.js';\nimport { createSubscribe, type SubscribeOptions, type SubscriptionEvent } from './subscribe.js';\nimport { parseUnsV2Fault, UnsV2Error } from '../uns-v2/client.js';\nimport { DataError, toDataError } from './error.js';\nimport { validateStepData, validateUpdate } from './schema.js';\nimport { toSortPairs, type Filter, type Sort } from './sort.js';\n\nexport type TransactAction =\n | 'record.create'\n | 'record.check'\n | 'record.read'\n | 'record.update'\n | 'record.modify'\n | 'record.replace'\n | 'record.delete'\n | 'record.query';\n\n/** One `recordTransactionExecute` item; `collection` is a declaration or alias instead of a collection ID. */\nexport interface TransactStep {\n opId: string;\n action: TransactAction;\n collection: CollectionDefinition | string;\n id?: string | { ref: string };\n filter?: Filter;\n data?: Record<string, unknown>;\n update?: Record<string, unknown>;\n upsert?: boolean;\n expectedRevision?: number;\n dependsOn?: string[];\n bindings?: { path: string; ref: string; resultPath: string }[];\n selection?: 'exactOne' | 'first';\n sort?: Sort;\n returnDocument?: 'before' | 'after';\n exists?: boolean;\n operations?: Record<string, unknown>[];\n conditions?: Filter[];\n /** `record.query`: rows to bind from; `expectCount` asserts the match count before `limit`. */\n query?: { filter?: Filter; sort?: Sort; limit?: number; expectCount?: { min?: number; max?: number } };\n arrayFilters?: Record<string, unknown>[];\n}\n\nexport interface TransactResultItem {\n opId: string;\n action: string;\n id: string;\n status: string;\n collectionId?: string;\n revision?: number;\n /** Before/after snapshot when requested, as a flat document. */\n document?: WithMeta<Record<string, unknown>>;\n data?: unknown;\n query?: unknown;\n error?: unknown;\n}\n\nexport interface TransactResult {\n status: string;\n results: TransactResultItem[];\n}\n\n/** Named reads for `data.queryBatch`: 1–20 entries, names matching `[A-Za-z0-9_.-]{1,64}`. */\nexport type BatchQueries = Record<string, BatchQuery<unknown>>;\nexport type BatchResults<Q extends BatchQueries> = { [K in keyof Q]: Q[K] extends BatchQuery<infer R> ? R : never };\nexport type SettledBatchResults<Q extends BatchQueries> = {\n [K in keyof Q]: Q[K] extends BatchQuery<infer R> ? { ok: true; value: R } | { ok: false; error: DataError } : never;\n};\n\n/** Contract limit of `records:queryBatch`. */\nexport const MAX_BATCH_QUERIES = 20;\nconst QUERY_NAME = /^[A-Za-z0-9_.-]{1,64}$/;\n\n/** Credentials as for `createClient`; `transportTimeoutMs` (per attempt) defaults to 25 s here, see DEFAULT_DATA_TIMEOUT_MS. */\nexport interface DataClientConfig extends ClientConfig {\n collections: readonly CollectionDefinition[];\n /** Defaults to \"deployed\" when NODE_ENV is production, otherwise \"preview\". */\n environment?: DataEnvironment;\n logger?: Logger;\n}\n\nexport interface DataClient {\n /** Typed handle for a declaration listed in `collections`. */\n collection<C extends CollectionDefinition>(definition: C): Collection<z.output<C['schema']>, z.input<C['schema']>>;\n /** Handle by alias, typed by the caller. */\n collection<T extends object = Record<string, unknown>>(alias: string): Collection<T>;\n /**\n * Atomic multi-record, multi-collection write (`recordTransactionExecute`, sync),\n * at most 200 steps. Steps can depend on each other (`dependsOn`) and bind\n * earlier results (`bindings`). This is not a Mongo session: everything is\n * sent at once.\n */\n transact(steps: readonly TransactStep[], options?: { dryRun?: boolean; idempotencyKey?: string }): Promise<TransactResult>;\n /**\n * Committed record changes of declared collections (all by default), as\n * one async iterable that reconnects and resumes by itself; see\n * `SubscribeOptions`. Events carry identities only; changes in collections\n * with employee data rules arrive as collection-level notices unless\n * `restricted: \"record\"` is asked for. Open it inside a request to watch as\n * that employee, or in `runAsApp` for one App-wide stream a server fans out.\n */\n subscribe(options?: SubscribeOptions): AsyncIterable<SubscriptionEvent>;\n /**\n * Up to 20 reads of declared collections in one request, answered from one\n * snapshot: what a page shows (a list page with its total, counters, a\n * lookup by ID) in one round trip. Build each read with\n * `collection.query.*`; results come back under the same names.\n *\n * A read UNS refuses on its own (invalid filter, no grant) rejects the\n * whole call with its DataError; with `{ settle: true }` every result is\n * `{ ok, value }` or `{ ok: false, error }` instead. A statement timeout or\n * a batch over 2000 rows / 8 MiB always fails the whole call.\n */\n queryBatch<Q extends BatchQueries>(queries: Q): Promise<BatchResults<Q>>;\n queryBatch<Q extends BatchQueries>(queries: Q, options: { settle: true }): Promise<SettledBatchResults<Q>>;\n /** Ensures every declaration now (normally lazy on first access). */\n ensure(): Promise<EnsureState>;\n}\n\nexport function createDataClient(config: DataClientConfig): DataClient {\n const { call, openStream } = createDataTransport(config);\n const environment = config.environment ?? (getEnvVar('NODE_ENV') === 'production' ? 'deployed' : 'preview');\n const declared = [...config.collections];\n if (declared.length > MAX_COLLECTIONS) {\n throw new Error(`An App declares at most ${MAX_COLLECTIONS} collections; got ${declared.length}.`);\n }\n const seen = new Set<string>();\n for (const definition of declared) {\n const key = definition.alias.toLowerCase();\n if (seen.has(key)) throw new Error(`Collection alias \"${definition.alias}\" is declared twice (aliases are case-insensitive).`);\n seen.add(key);\n }\n\n const bootstrap = createBootstrap({ call, collections: declared, environment, logger: config.logger ?? console });\n const runtime = {\n call,\n resolve: (target: CollectionDefinition | string) => bootstrap.resolve(lookup(target)),\n recover: bootstrap.recover,\n subject: currentDataSubject,\n signal: currentSignal,\n };\n const handles = new Map<CollectionDefinition, Collection<object>>();\n\n function lookup(target: CollectionDefinition | string): CollectionDefinition {\n if (typeof target === 'string') {\n const found = declared.find((definition) => definition.alias.toLowerCase() === target.toLowerCase());\n if (!found) throw new Error(`Collection \"${target}\" is not declared in this data client's collections.`);\n return found;\n }\n if (!declared.includes(target)) {\n throw new Error(`Collection \"${target.alias}\" is declared but missing from the \\`collections\\` passed to createDataClient.`);\n }\n return target;\n }\n\n function collection(target: CollectionDefinition | string) {\n const definition = lookup(target);\n let handle = handles.get(definition);\n if (!handle) {\n handle = new Collection<object>(definition, runtime);\n handles.set(definition, handle);\n }\n return handle;\n }\n\n async function transact(\n steps: readonly TransactStep[],\n options: { dryRun?: boolean; idempotencyKey?: string } = {},\n ): Promise<TransactResult> {\n assertAtomicBatch('data.transact', steps.length);\n // Steps resolve their collection IDs inside the recovered call, so a retry uses fresh IDs.\n const result = await bootstrap.recover(() => sendTransaction(steps, options));\n const results = (result.results ?? []).map(({ document, ...item }) => ({\n ...item,\n ...(document ? { document: toDoc<Record<string, unknown>>(document) } : {}),\n }));\n const expected = options.dryRun ? 'validated' : 'committed';\n if (result.status !== expected) {\n const failed = results.filter((item) => item.error);\n // A sync transaction should only fail whole; map the rest of the Outcome statuses anyway.\n const execution = result.status === 'unknown' ? 'unknown' : result.status === 'partiallyCommitted' ? 'committed' : 'rolledBack';\n throw new DataError({\n message: `Transaction ${result.status}: ${failed.map((item) => `${item.opId} ${JSON.stringify(item.error)}`).join('; ') || 'no step committed'}`,\n status: execution === 'unknown' ? 503 : 409,\n code: 'TRANSACTION_FAILED',\n execution,\n attempts: 1,\n details: { status: result.status, results },\n });\n }\n return { status: result.status, results };\n }\n\n async function sendTransaction(steps: readonly TransactStep[], options: { dryRun?: boolean; idempotencyKey?: string }) {\n const items = await Promise.all(\n steps.map(async ({ collection: target, sort, query, ...step }) => {\n const definition = lookup(target);\n const item: Record<string, unknown> = { ...step, collectionId: await bootstrap.resolve(definition) };\n if (sort) item.sort = toSortPairs(sort);\n if (query) item.query = { ...query, ...(query.sort ? { sort: toSortPairs(query.sort) } : {}) };\n if (step.data) {\n // Fields bound to earlier results are filled by UNS at commit; everything literal validates now.\n const bound = (step.bindings ?? []).flatMap((binding) => (binding.path.startsWith('data.') ? [binding.path.slice(5).split('.')[0]] : []));\n item.data = validateStepData(definition, step.data, bound);\n }\n if (step.update) item.update = validateUpdate(definition, step.update);\n return item;\n }),\n );\n return call<{ status: string; results?: (Omit<TransactResultItem, 'document'> & { document?: UnsRecord })[] }>(\n 'recordTransactionExecute',\n {\n items,\n mode: 'sync',\n ...(options.dryRun ? { dryRun: true } : {}),\n ...(options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}),\n },\n { dataSubject: currentDataSubject(), signal: currentSignal() },\n );\n }\n\n async function queryBatch<Q extends BatchQueries>(queries: Q, options: { settle?: boolean } = {}) {\n const names = Object.keys(queries);\n if (names.length < 1 || names.length > MAX_BATCH_QUERIES) {\n throw new TypeError(`data.queryBatch takes 1–${MAX_BATCH_QUERIES} queries; got ${names.length}.`);\n }\n const bad = names.find((name) => !QUERY_NAME.test(name));\n if (bad !== undefined) throw new TypeError(`data.queryBatch query name \"${bad}\" must match ${QUERY_NAME}.`);\n for (const name of names) lookup(queries[name].collection);\n // Items resolve their collection IDs inside the recovered call, so a retry uses fresh IDs.\n const { results } = await bootstrap.recover(async () => {\n const items = await Promise.all(names.map(async (name) => ({\n id: name,\n collectionId: await bootstrap.resolve(queries[name].collection),\n ...(await queries[name].prepare()),\n })));\n return call<{ results: Record<string, { ok: true; data: unknown } | { ok: false; error: unknown }> }>(\n 'recordQueryBatch', { queries: items }, { dataSubject: currentDataSubject(), signal: currentSignal() });\n });\n const settled = Object.fromEntries(names.map((name) => {\n const result = results[name];\n if (result?.ok === true) return [name, { ok: true, value: queries[name].parse(result.data) }];\n return [name, { ok: false, error: itemError(name, result?.error) }];\n })) as Record<string, { ok: true; value: unknown } | { ok: false; error: DataError }>;\n if (options.settle) return settled;\n const failed = names.find((name) => !settled[name].ok);\n if (failed !== undefined) throw (settled[failed] as { error: DataError }).error;\n return Object.fromEntries(names.map((name) => [name, (settled[name] as { value: unknown }).value]));\n }\n\n const subscribe = createSubscribe({\n openStream,\n ensure: bootstrap.ensure,\n rebuild: bootstrap.rebuild,\n lookup,\n declared,\n subject: currentDataSubject,\n });\n\n return {\n collection: collection as DataClient['collection'],\n transact,\n queryBatch: queryBatch as DataClient['queryBatch'],\n subscribe,\n ensure: bootstrap.ensure,\n };\n}\n\n/** Item errors carry no HTTP status of their own; it follows from the code. */\nconst ITEM_STATUS: Record<string, number> = { FORBIDDEN: 403, NOT_FOUND: 404 };\n\n/** A queryBatch item's error as a DataError; its `upstreamStatus` is the status its code stands for. */\nfunction itemError(name: string, value: unknown): DataError {\n const fault = parseUnsV2Fault(value);\n if (!fault) {\n return new DataError({\n message: `UNS answered query \"${name}\" of recordQueryBatch with an invalid result.`,\n status: 502,\n code: 'DATA_BAD_RESPONSE',\n upstreamStatus: 200,\n execution: 'rolledBack',\n attempts: 1,\n });\n }\n const error = new UnsV2Error(fault.reason ?? fault.code, {\n ...fault,\n // The items are reads: never an unknown outcome.\n execution: fault.execution === 'unknown' ? 'rolledBack' : fault.execution,\n queryId: fault.queryId ?? name,\n status: ITEM_STATUS[fault.code] ?? 400,\n operation: 'recordQueryBatch',\n data: { queryId: fault.queryId ?? name, details: fault.details ?? [] },\n });\n return toDataError(error);\n}\n\n"]}
@@ -12,6 +12,12 @@ export type WithMeta<T> = T & {
12
12
  _id: string;
13
13
  _meta: RecordMeta;
14
14
  };
15
+ /** A projection may omit business fields at any depth; array and tuple containers retain their shape. */
16
+ export type DeepPartial<T> = T extends readonly unknown[] ? {
17
+ [K in keyof T]: DeepPartial<T[K]>;
18
+ } : T extends object ? {
19
+ [K in keyof T]?: DeepPartial<T[K]>;
20
+ } : T;
15
21
  export type Projection = Record<string, 0 | 1 | boolean>;
16
22
  export type Update = Record<string, unknown>;
17
23
  export interface UnsRecord {
@@ -39,6 +45,8 @@ export interface CollectionRuntime {
39
45
  }): Promise<R>;
40
46
  /** Data subject of the request being served. */
41
47
  subject(): string | undefined;
48
+ /** Abort signal of the request being served. */
49
+ signal(): AbortSignal | undefined;
42
50
  }
43
51
  /** Rejects batches UNS would refuse, before anything is sent. */
44
52
  export declare function assertAtomicBatch(operation: string, count: number): void;
@@ -83,6 +91,64 @@ export interface FindPageOptions {
83
91
  /** At most 200. */
84
92
  pageSize?: number;
85
93
  projection?: Projection;
94
+ /** Count matches up to this many (1–100000); UNS defaults to 10000. */
95
+ maxCount?: number;
96
+ }
97
+ /** A match count UNS stopped at its limit: `capped` means "at least `count`". */
98
+ export interface CappedCount {
99
+ count: number;
100
+ capped: boolean;
101
+ }
102
+ /** UNS reads at most 100 records by ID per request. */
103
+ export declare const MAX_GET_IDS = 100;
104
+ export interface FoundPage<T> {
105
+ items: WithMeta<T>[];
106
+ total: CappedCount;
107
+ page: number;
108
+ pageSize: number;
109
+ }
110
+ export interface ValueCount {
111
+ value: unknown;
112
+ count: number;
113
+ }
114
+ /** One read of `data.queryBatch`, built by `collection.query.*`. */
115
+ export interface BatchQuery<R> {
116
+ readonly collection: CollectionDefinition;
117
+ /** The batch item without `id` and `collectionId`; resolves `$lookup` targets. */
118
+ prepare(): Promise<Record<string, unknown>>;
119
+ /** The item's result as the builder's caller sees it. */
120
+ parse(data: unknown): R;
121
+ }
122
+ export interface CollectionQueries<T> {
123
+ /** First page (≤200, default 20); `maxCount` adds the capped total of all matches. */
124
+ find(filter?: Filter, options?: {
125
+ sort?: Sort;
126
+ limit?: number;
127
+ projection?: Projection;
128
+ maxCount?: number;
129
+ }): BatchQuery<{
130
+ items: WithMeta<T>[];
131
+ hasMore: boolean;
132
+ total?: CappedCount;
133
+ }>;
134
+ /** Like `collection.findPage`. */
135
+ findPage(filter?: Filter, options?: FindPageOptions): BatchQuery<FoundPage<T>>;
136
+ count(filter?: Filter, options?: {
137
+ maxCount?: number;
138
+ }): BatchQuery<CappedCount>;
139
+ /** The 200 most frequent values. */
140
+ countBy(field: string, filter?: Filter): BatchQuery<ValueCount[]>;
141
+ /** The first 200 distinct values. */
142
+ distinct(field: string, filter?: Filter): BatchQuery<unknown[]>;
143
+ /** The first 200 output rows. */
144
+ aggregate<R = Record<string, unknown>>(pipeline: Record<string, unknown>[]): BatchQuery<R[]>;
145
+ /** 1–100 distinct records by ID, as `collection.get`. */
146
+ get(ids: readonly string[], options?: {
147
+ projection?: Projection;
148
+ }): BatchQuery<{
149
+ items: WithMeta<T>[];
150
+ missing: string[];
151
+ }>;
86
152
  }
87
153
  export interface UpdateOptions {
88
154
  upsert?: boolean;
@@ -96,6 +162,15 @@ export interface FindOneAndOptions extends UpdateOptions {
96
162
  returnDocument?: 'before' | 'after';
97
163
  projection?: Projection;
98
164
  }
165
+ export type InsertOneOptions = {
166
+ idempotencyKey?: string;
167
+ } & ({
168
+ returnDocument?: 'none';
169
+ projection?: never;
170
+ } | {
171
+ returnDocument: 'after';
172
+ projection?: Projection;
173
+ });
99
174
  export type BulkOperation = {
100
175
  insertOne: {
101
176
  document: Record<string, unknown>;
@@ -135,16 +210,43 @@ export declare class Collection<T extends object = Record<string, unknown>, TInp
135
210
  find(filter?: Filter, options?: FindOptions): Cursor<WithMeta<T>>;
136
211
  findOne(filter?: Filter, options?: Omit<FindOptions, 'limit'>): Promise<WithMeta<T> | null>;
137
212
  /**
138
- * One numbered page plus the total match count, for paged list screens.
139
- * Uses `recordAggregatePipeline` offset paging.
213
+ * Records by ID, in the order asked for (each ID once, at its first
214
+ * position); IDs that do not exist, are deleted or are hidden by employee
215
+ * data rules are listed in `missing` alike. More than 100 IDs are read in
216
+ * chunks of 100 (not one snapshot).
140
217
  */
141
- findPage(filter?: Filter, options?: FindPageOptions): Promise<{
218
+ get(ids: readonly string[], options?: {
219
+ projection?: Projection;
220
+ }): Promise<{
142
221
  items: WithMeta<T>[];
143
- total: number;
144
- page: number;
145
- pageSize: number;
222
+ missing: string[];
146
223
  }>;
147
- countDocuments(filter?: Filter): Promise<number>;
224
+ /**
225
+ * One numbered page plus the total match count, for paged list screens:
226
+ * one `recordAggregatePipeline` offset page whose `count` runs in the same
227
+ * snapshot. Show a capped total as "10000+".
228
+ */
229
+ findPage(filter?: Filter, options?: FindPageOptions): Promise<FoundPage<T>>;
230
+ /**
231
+ * Counts matches up to `maxCount` (1–100000, UNS default 100000). A
232
+ * `capped` result means "at least `count`".
233
+ */
234
+ countDocuments(filter?: Filter, options?: {
235
+ maxCount?: number;
236
+ }): Promise<CappedCount>;
237
+ /**
238
+ * Match counts per value of `field`, largest first: one `$group` pipeline
239
+ * instead of a count per value. Records without the field count under
240
+ * `value: null`.
241
+ */
242
+ countBy(field: string, filter?: Filter): Promise<ValueCount[]>;
243
+ /**
244
+ * Builders for `data.queryBatch`: each describes one read of this
245
+ * collection, run together with the others in one request and one
246
+ * snapshot. Each returns one page (at most 200 rows), unlike the cursor
247
+ * methods; `get` takes at most 100 IDs.
248
+ */
249
+ get query(): CollectionQueries<T>;
148
250
  /** Distinct values of a field (every page). */
149
251
  distinct(field: string, filter?: Filter): Promise<unknown[]>;
150
252
  /**
@@ -163,12 +265,37 @@ export declare class Collection<T extends object = Record<string, unknown>, TInp
163
265
  private aggregatePage;
164
266
  /** Converts Mongo-style `$sort` objects and resolves `$lookup.from`, recursively. */
165
267
  private prepareStages;
268
+ /** Returns the creation snapshot in the same write; replay preserves that snapshot and requires current read access. */
166
269
  insertOne(document: TInput & {
167
270
  _id?: string;
168
- }, options?: {
169
- idempotencyKey?: string;
271
+ }, options: Extract<InsertOneOptions, {
272
+ returnDocument: 'after';
273
+ }> & {
274
+ projection?: never;
170
275
  }): Promise<{
171
276
  insertedId: string;
277
+ value: WithMeta<T>;
278
+ }>;
279
+ insertOne(document: TInput & {
280
+ _id?: string;
281
+ }, options: Extract<InsertOneOptions, {
282
+ returnDocument: 'after';
283
+ }>): Promise<{
284
+ insertedId: string;
285
+ value: WithMeta<DeepPartial<T>>;
286
+ }>;
287
+ insertOne(document: TInput & {
288
+ _id?: string;
289
+ }, options?: Extract<InsertOneOptions, {
290
+ returnDocument?: 'none';
291
+ }>): Promise<{
292
+ insertedId: string;
293
+ }>;
294
+ insertOne(document: TInput & {
295
+ _id?: string;
296
+ }, options: InsertOneOptions): Promise<{
297
+ insertedId: string;
298
+ value?: WithMeta<DeepPartial<T>>;
172
299
  }>;
173
300
  /** Inserts up to 200 documents atomically (all or none). */
174
301
  insertMany(documents: readonly (TInput & {
@@ -187,13 +314,20 @@ export declare class Collection<T extends object = Record<string, unknown>, TInp
187
314
  /**
188
315
  * Updates every match like Mongo's updateMany: documents commit one by one
189
316
  * (not atomically), following `resumeAfter` until every match is processed.
190
- * A failure stops the run and reports the counts already committed. For
191
- * all-or-nothing multi-document changes use `data.transact` (≤200 items).
317
+ * A failure after some documents committed keeps its code but is
318
+ * `execution: "committed"`, with the cumulative counts and
319
+ * `details.resumeAfter`; pass that back as `resumeAfter` to continue
320
+ * instead of repeating the call. For all-or-nothing multi-document changes
321
+ * use `data.transact` (≤200 items).
192
322
  */
193
- updateMany(filter: Filter, update: Update, options?: Pick<UpdateOptions, 'arrayFilters'>): Promise<{
323
+ updateMany(filter: Filter, update: Update, options?: Pick<UpdateOptions, 'arrayFilters'> & {
324
+ resumeAfter?: string;
325
+ }): Promise<{
194
326
  matchedCount: number;
195
327
  modifiedCount: number;
196
328
  }>;
329
+ /** Runs a non-atomic many-write page by page; see updateMany for failures. */
330
+ private writeEach;
197
331
  /**
198
332
  * Replaces the whole body. Fields written by another App version and not in
199
333
  * this declaration are lost — prefer `updateOne` with `$set`.
@@ -209,9 +343,12 @@ export declare class Collection<T extends object = Record<string, unknown>, TInp
209
343
  }>;
210
344
  /**
211
345
  * Soft-deletes every match, one document at a time like Mongo's deleteMany
212
- * (not atomic), following `resumeAfter`; requires `uns.delete` on the App key.
346
+ * (not atomic), following `resumeAfter`; requires `uns.delete` on the App
347
+ * key. Failures after committed deletes report them as updateMany does.
213
348
  */
214
- deleteMany(filter: Filter): Promise<{
349
+ deleteMany(filter: Filter, options?: {
350
+ resumeAfter?: string;
351
+ }): Promise<{
215
352
  deletedCount: number;
216
353
  }>;
217
354
  /**