@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.
- package/LICENSE +21 -21
- package/README.md +13 -11
- package/dist/cjs/app-identity-contract.generated.js.map +1 -1
- package/dist/cjs/capabilities.js.map +1 -1
- package/dist/cjs/cloud-server.js.map +1 -1
- package/dist/cjs/cloud-transport.d.ts +21 -0
- package/dist/cjs/cloud-transport.js +81 -1
- package/dist/cjs/cloud-transport.js.map +1 -1
- package/dist/cjs/controls.js.map +1 -1
- package/dist/cjs/data/bootstrap.d.ts +4 -0
- package/dist/cjs/data/bootstrap.js +15 -6
- package/dist/cjs/data/bootstrap.js.map +1 -1
- package/dist/cjs/data/call.d.ts +26 -9
- package/dist/cjs/data/call.js +179 -14
- package/dist/cjs/data/call.js.map +1 -1
- package/dist/cjs/data/client.d.ts +44 -2
- package/dist/cjs/data/client.js +87 -4
- package/dist/cjs/data/client.js.map +1 -1
- package/dist/cjs/data/collection.d.ts +151 -14
- package/dist/cjs/data/collection.js +213 -50
- package/dist/cjs/data/collection.js.map +1 -1
- package/dist/cjs/data/context.d.ts +4 -2
- package/dist/cjs/data/context.js +14 -3
- package/dist/cjs/data/context.js.map +1 -1
- package/dist/cjs/data/define.d.ts +9 -1
- package/dist/cjs/data/define.js.map +1 -1
- package/dist/cjs/data/error.d.ts +30 -12
- package/dist/cjs/data/error.js +61 -63
- package/dist/cjs/data/error.js.map +1 -1
- package/dist/cjs/data/index.d.ts +6 -3
- package/dist/cjs/data/index.js +7 -1
- package/dist/cjs/data/index.js.map +1 -1
- package/dist/cjs/data/schema.d.ts +7 -1
- package/dist/cjs/data/schema.js +54 -4
- package/dist/cjs/data/schema.js.map +1 -1
- package/dist/cjs/data/subscribe.d.ts +108 -0
- package/dist/cjs/data/subscribe.js +270 -0
- package/dist/cjs/data/subscribe.js.map +1 -0
- package/dist/cjs/mq/broker.js.map +1 -1
- package/dist/cjs/mq/client.js.map +1 -1
- package/dist/cjs/mq/index.js.map +1 -1
- package/dist/cjs/mq/types.js.map +1 -1
- package/dist/cjs/runtime-context.js.map +1 -1
- package/dist/cjs/runtime-env.js.map +1 -1
- package/dist/cjs/uns-v2/client.d.ts +90 -12
- package/dist/cjs/uns-v2/client.js +238 -63
- package/dist/cjs/uns-v2/client.js.map +1 -1
- package/dist/cjs/uns-v2/codec.js.map +1 -1
- package/dist/cjs/uns-v2/generated.d.ts +9619 -6923
- package/dist/cjs/uns-v2/generated.js +124 -119
- package/dist/cjs/uns-v2/generated.js.map +1 -1
- package/dist/cjs/uns-v2/index.js.map +1 -1
- package/dist/cjs/uns-v2/sse.d.ts +20 -0
- package/dist/cjs/uns-v2/sse.js +65 -0
- package/dist/cjs/uns-v2/sse.js.map +1 -0
- package/dist/esm/app-identity-contract.generated.js.map +1 -1
- package/dist/esm/capabilities.js.map +1 -1
- package/dist/esm/cloud-server.js.map +1 -1
- package/dist/esm/cloud-transport.d.ts +21 -0
- package/dist/esm/cloud-transport.js +81 -1
- package/dist/esm/cloud-transport.js.map +1 -1
- package/dist/esm/controls.js.map +1 -1
- package/dist/esm/data/bootstrap.d.ts +4 -0
- package/dist/esm/data/bootstrap.js +14 -5
- package/dist/esm/data/bootstrap.js.map +1 -1
- package/dist/esm/data/call.d.ts +26 -9
- package/dist/esm/data/call.js +177 -13
- package/dist/esm/data/call.js.map +1 -1
- package/dist/esm/data/client.d.ts +44 -2
- package/dist/esm/data/client.js +89 -7
- package/dist/esm/data/client.js.map +1 -1
- package/dist/esm/data/collection.d.ts +151 -14
- package/dist/esm/data/collection.js +212 -49
- package/dist/esm/data/collection.js.map +1 -1
- package/dist/esm/data/context.d.ts +4 -2
- package/dist/esm/data/context.js +13 -3
- package/dist/esm/data/context.js.map +1 -1
- package/dist/esm/data/define.d.ts +9 -1
- package/dist/esm/data/define.js.map +1 -1
- package/dist/esm/data/error.d.ts +30 -12
- package/dist/esm/data/error.js +61 -63
- package/dist/esm/data/error.js.map +1 -1
- package/dist/esm/data/index.d.ts +6 -3
- package/dist/esm/data/index.js +3 -1
- package/dist/esm/data/index.js.map +1 -1
- package/dist/esm/data/schema.d.ts +7 -1
- package/dist/esm/data/schema.js +54 -4
- package/dist/esm/data/schema.js.map +1 -1
- package/dist/esm/data/subscribe.d.ts +108 -0
- package/dist/esm/data/subscribe.js +265 -0
- package/dist/esm/data/subscribe.js.map +1 -0
- package/dist/esm/mq/broker.js.map +1 -1
- package/dist/esm/mq/client.js.map +1 -1
- package/dist/esm/mq/index.js.map +1 -1
- package/dist/esm/mq/types.js.map +1 -1
- package/dist/esm/runtime-context.js.map +1 -1
- package/dist/esm/runtime-env.js.map +1 -1
- package/dist/esm/uns-v2/client.d.ts +90 -12
- package/dist/esm/uns-v2/client.js +236 -63
- package/dist/esm/uns-v2/client.js.map +1 -1
- package/dist/esm/uns-v2/codec.js.map +1 -1
- package/dist/esm/uns-v2/generated.d.ts +9619 -6923
- package/dist/esm/uns-v2/generated.js +123 -118
- package/dist/esm/uns-v2/generated.js.map +1 -1
- package/dist/esm/uns-v2/index.js.map +1 -1
- package/dist/esm/uns-v2/sse.d.ts +20 -0
- package/dist/esm/uns-v2/sse.js +60 -0
- package/dist/esm/uns-v2/sse.js.map +1 -0
- package/package.json +29 -16
- package/skills/tier0-sdk-v2/SKILL.md +14 -14
- package/skills/{tier0-sdk-v2-data → tier0-sdk-v2/tier0-sdk-v2-data}/SKILL.md +3 -2
- package/skills/{tier0-sdk-v2-data → tier0-sdk-v2/tier0-sdk-v2-data}/references/consistency.md +3 -3
- package/skills/{tier0-sdk-v2-data → tier0-sdk-v2/tier0-sdk-v2-data}/references/errors.md +23 -10
- package/skills/{tier0-sdk-v2-data → tier0-sdk-v2/tier0-sdk-v2-data}/references/modeling.md +36 -0
- package/skills/tier0-sdk-v2/tier0-sdk-v2-data/references/read-write.md +172 -0
- package/skills/{tier0-sdk-v2-mq → tier0-sdk-v2/tier0-sdk-v2-mq}/references/quickstart.md +1 -1
- package/skills/{tier0-sdk-v2-source → tier0-sdk-v2/tier0-sdk-v2-source}/SKILL.md +3 -3
- package/skills/{tier0-sdk-v2-source → tier0-sdk-v2/tier0-sdk-v2-source}/references/create.md +3 -6
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/delete.md +39 -0
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/state.md +36 -0
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/update.md +80 -0
- package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/SKILL.md +6 -6
- package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/create.md +15 -14
- package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/deploy.md +11 -9
- package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/lifecycle.md +2 -1
- package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/update.md +5 -6
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/SKILL.md +55 -0
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/create.md +57 -0
- package/skills/{tier0-sdk-v2-uns → tier0-sdk-v2/tier0-sdk-v2-uns}/references/data-workflows.md +3 -7
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/delete.md +53 -0
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/namespace.md +38 -0
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/read.md +34 -0
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/restore.md +39 -0
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/update.md +62 -0
- package/skills/tier0-sdk-v2-data/references/read-write.md +0 -72
- package/skills/tier0-sdk-v2-source/references/delete.md +0 -47
- package/skills/tier0-sdk-v2-source/references/state.md +0 -37
- package/skills/tier0-sdk-v2-source/references/update.md +0 -61
- package/skills/tier0-sdk-v2-uns/SKILL.md +0 -53
- package/skills/tier0-sdk-v2-uns/references/create.md +0 -85
- package/skills/tier0-sdk-v2-uns/references/delete.md +0 -69
- package/skills/tier0-sdk-v2-uns/references/namespace.md +0 -75
- package/skills/tier0-sdk-v2-uns/references/read.md +0 -32
- package/skills/tier0-sdk-v2-uns/references/restore.md +0 -41
- package/skills/tier0-sdk-v2-uns/references/update.md +0 -86
- /package/skills/{tier0-sdk-v2-agent → tier0-sdk-v2/tier0-sdk-v2-agent}/SKILL.md +0 -0
- /package/skills/{tier0-sdk-v2-agent → tier0-sdk-v2/tier0-sdk-v2-agent}/references/results.md +0 -0
- /package/skills/{tier0-sdk-v2-controls → tier0-sdk-v2/tier0-sdk-v2-controls}/SKILL.md +0 -0
- /package/skills/{tier0-sdk-v2-data → tier0-sdk-v2/tier0-sdk-v2-data}/references/schema-evolution.md +0 -0
- /package/skills/{tier0-sdk-v2-files → tier0-sdk-v2/tier0-sdk-v2-files}/SKILL.md +0 -0
- /package/skills/{tier0-sdk-v2-files → tier0-sdk-v2/tier0-sdk-v2-files}/references/delete.md +0 -0
- /package/skills/{tier0-sdk-v2-files → tier0-sdk-v2/tier0-sdk-v2-files}/references/download.md +0 -0
- /package/skills/{tier0-sdk-v2-files → tier0-sdk-v2/tier0-sdk-v2-files}/references/multipart.md +0 -0
- /package/skills/{tier0-sdk-v2-files → tier0-sdk-v2/tier0-sdk-v2-files}/references/upload.md +0 -0
- /package/skills/{tier0-sdk-v2-files → tier0-sdk-v2/tier0-sdk-v2-files}/references/url.md +0 -0
- /package/skills/{tier0-sdk-v2-members → tier0-sdk-v2/tier0-sdk-v2-members}/SKILL.md +0 -0
- /package/skills/{tier0-sdk-v2-members → tier0-sdk-v2/tier0-sdk-v2-members}/references/app-members.md +0 -0
- /package/skills/{tier0-sdk-v2-members → tier0-sdk-v2/tier0-sdk-v2-members}/references/platform-members.md +0 -0
- /package/skills/{tier0-sdk-v2-mq → tier0-sdk-v2/tier0-sdk-v2-mq}/SKILL.md +0 -0
- /package/skills/{tier0-sdk-v2-notifications → tier0-sdk-v2/tier0-sdk-v2-notifications}/SKILL.md +0 -0
- /package/skills/{tier0-sdk-v2-notifications → tier0-sdk-v2/tier0-sdk-v2-notifications}/references/get.md +0 -0
- /package/skills/{tier0-sdk-v2-notifications → tier0-sdk-v2/tier0-sdk-v2-notifications}/references/send.md +0 -0
- /package/skills/{tier0-sdk-v2-source → tier0-sdk-v2/tier0-sdk-v2-source}/references/changes.md +0 -0
- /package/skills/{tier0-sdk-v2-source → tier0-sdk-v2/tier0-sdk-v2-source}/references/protocols.md +0 -0
- /package/skills/{tier0-sdk-v2-source → tier0-sdk-v2/tier0-sdk-v2-source}/references/read.md +0 -0
- /package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/delete.md +0 -0
- /package/skills/{tier0-sdk-v2-stream → tier0-sdk-v2/tier0-sdk-v2-stream}/references/read.md +0 -0
- /package/skills/{tier0-sdk-v2-system → tier0-sdk-v2/tier0-sdk-v2-system}/SKILL.md +0 -0
- /package/skills/{tier0-sdk-v2-system → tier0-sdk-v2/tier0-sdk-v2-system}/references/info.md +0 -0
- /package/skills/{tier0-sdk-v2-system → tier0-sdk-v2/tier0-sdk-v2-system}/references/whoami.md +0 -0
package/dist/esm/data/client.js
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { getEnvVar } from '../runtime-env.js';
|
|
2
2
|
import { createBootstrap } from './bootstrap.js';
|
|
3
|
-
import {
|
|
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 {
|
|
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 =
|
|
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
|
-
|
|
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
|
-
*
|
|
139
|
-
*
|
|
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
|
-
|
|
218
|
+
get(ids: readonly string[], options?: {
|
|
219
|
+
projection?: Projection;
|
|
220
|
+
}): Promise<{
|
|
142
221
|
items: WithMeta<T>[];
|
|
143
|
-
|
|
144
|
-
page: number;
|
|
145
|
-
pageSize: number;
|
|
222
|
+
missing: string[];
|
|
146
223
|
}>;
|
|
147
|
-
|
|
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
|
-
|
|
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
|
|
191
|
-
*
|
|
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'>
|
|
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
|
|
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
|
|
349
|
+
deleteMany(filter: Filter, options?: {
|
|
350
|
+
resumeAfter?: string;
|
|
351
|
+
}): Promise<{
|
|
215
352
|
deletedCount: number;
|
|
216
353
|
}>;
|
|
217
354
|
/**
|