primitive-admin 1.2.0-alpha.4 → 1.2.0-alpha.5

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 (114) hide show
  1. package/assets/skill/skills/primitive-platform/SKILL.md +17 -0
  2. package/dist/src/commands/auth.js +7 -4
  3. package/dist/src/commands/auth.js.map +1 -1
  4. package/dist/src/commands/connections.js +11 -12
  5. package/dist/src/commands/connections.js.map +1 -1
  6. package/dist/src/commands/database-type-configs.js +2 -76
  7. package/dist/src/commands/database-type-configs.js.map +1 -1
  8. package/dist/src/commands/database-types.js +313 -13
  9. package/dist/src/commands/database-types.js.map +1 -1
  10. package/dist/src/commands/databases.d.ts +9 -0
  11. package/dist/src/commands/databases.js +67 -367
  12. package/dist/src/commands/databases.js.map +1 -1
  13. package/dist/src/commands/documents.d.ts +32 -2
  14. package/dist/src/commands/documents.js +281 -685
  15. package/dist/src/commands/documents.js.map +1 -1
  16. package/dist/src/commands/functions.js +2 -2
  17. package/dist/src/commands/functions.js.map +1 -1
  18. package/dist/src/commands/llm.js +6 -9
  19. package/dist/src/commands/llm.js.map +1 -1
  20. package/dist/src/commands/settings.d.ts +9 -12
  21. package/dist/src/commands/settings.js +79 -31
  22. package/dist/src/commands/settings.js.map +1 -1
  23. package/dist/src/commands/sync.d.ts +31 -81
  24. package/dist/src/commands/sync.js +228 -1113
  25. package/dist/src/commands/sync.js.map +1 -1
  26. package/dist/src/lib/api-client.d.ts +30 -199
  27. package/dist/src/lib/api-client.js +31 -297
  28. package/dist/src/lib/api-client.js.map +1 -1
  29. package/dist/src/lib/codegen-shared/generatedFiles.d.ts +6 -7
  30. package/dist/src/lib/codegen-shared/generatedFiles.js +2 -2
  31. package/dist/src/lib/codegen-shared/generatedFiles.js.map +1 -1
  32. package/dist/src/lib/codegen-shared/prettierStable.d.ts +1 -1
  33. package/dist/src/lib/codegen-shared/prettierStable.js +1 -1
  34. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.d.ts +2 -2
  35. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js +2 -2
  36. package/dist/src/lib/config-object-descriptor.js +1 -5
  37. package/dist/src/lib/config-object-descriptor.js.map +1 -1
  38. package/dist/src/lib/config-surface.d.ts +2 -1
  39. package/dist/src/lib/config-surface.js +4 -1
  40. package/dist/src/lib/config-surface.js.map +1 -1
  41. package/dist/src/lib/connections-list.d.ts +10 -0
  42. package/dist/src/lib/connections-list.js +18 -0
  43. package/dist/src/lib/connections-list.js.map +1 -0
  44. package/dist/src/lib/csv.d.ts +8 -9
  45. package/dist/src/lib/csv.js +3 -4
  46. package/dist/src/lib/csv.js.map +1 -1
  47. package/dist/src/lib/db-codegen/dbNaming.d.ts +2 -1
  48. package/dist/src/lib/db-codegen/dbNaming.js +2 -1
  49. package/dist/src/lib/db-codegen/dbNaming.js.map +1 -1
  50. package/dist/src/lib/db-codegen/dbTsTypes.d.ts +2 -2
  51. package/dist/src/lib/db-codegen/dbTsTypes.js +2 -2
  52. package/dist/src/lib/db-codegen/dbTypeIR.d.ts +59 -1
  53. package/dist/src/lib/db-codegen/dbTypeIR.js +12 -1
  54. package/dist/src/lib/db-codegen/dbTypeIR.js.map +1 -1
  55. package/dist/src/lib/document-export-record-log.d.ts +57 -0
  56. package/dist/src/lib/document-export-record-log.js +215 -0
  57. package/dist/src/lib/document-export-record-log.js.map +1 -0
  58. package/dist/src/lib/document-ingest-artifact.d.ts +13 -2
  59. package/dist/src/lib/document-ingest-artifact.js +7 -3
  60. package/dist/src/lib/document-ingest-artifact.js.map +1 -1
  61. package/dist/src/lib/document-ingest-rows.d.ts +16 -0
  62. package/dist/src/lib/document-ingest-rows.js +17 -0
  63. package/dist/src/lib/document-ingest-rows.js.map +1 -1
  64. package/dist/src/lib/document-ingest.d.ts +22 -0
  65. package/dist/src/lib/document-ingest.js +33 -0
  66. package/dist/src/lib/document-ingest.js.map +1 -1
  67. package/dist/src/lib/function-triggers.d.ts +3 -15
  68. package/dist/src/lib/function-triggers.js +10 -72
  69. package/dist/src/lib/function-triggers.js.map +1 -1
  70. package/dist/src/lib/generated-allowlist.js +0 -20
  71. package/dist/src/lib/generated-allowlist.js.map +1 -1
  72. package/dist/src/lib/generated-config-surfaces.d.ts +40 -2
  73. package/dist/src/lib/generated-config-surfaces.js +160 -94
  74. package/dist/src/lib/generated-config-surfaces.js.map +1 -1
  75. package/dist/src/lib/generated-sdk-types.d.ts +1 -1
  76. package/dist/src/lib/generated-sdk-types.js +1 -1
  77. package/dist/src/lib/generated-sdk-types.js.map +1 -1
  78. package/dist/src/lib/login-scope-lines.d.ts +15 -0
  79. package/dist/src/lib/login-scope-lines.js +22 -0
  80. package/dist/src/lib/login-scope-lines.js.map +1 -0
  81. package/dist/src/lib/paginate.d.ts +0 -5
  82. package/dist/src/lib/paginate.js +0 -5
  83. package/dist/src/lib/paginate.js.map +1 -1
  84. package/dist/src/lib/refresh-admin-credentials.d.ts +7 -0
  85. package/dist/src/lib/refresh-admin-credentials.js +16 -4
  86. package/dist/src/lib/refresh-admin-credentials.js.map +1 -1
  87. package/dist/src/lib/root-import-decision.d.ts +15 -14
  88. package/dist/src/lib/root-import-decision.js +44 -51
  89. package/dist/src/lib/root-import-decision.js.map +1 -1
  90. package/dist/src/lib/snapshot-build-rows.d.ts +21 -0
  91. package/dist/src/lib/snapshot-build-rows.js +22 -0
  92. package/dist/src/lib/snapshot-build-rows.js.map +1 -1
  93. package/dist/src/lib/snapshot-build.js +8 -3
  94. package/dist/src/lib/snapshot-build.js.map +1 -1
  95. package/dist/src/lib/swift-codegen/banners.d.ts +34 -7
  96. package/dist/src/lib/swift-codegen/banners.js +37 -7
  97. package/dist/src/lib/swift-codegen/banners.js.map +1 -1
  98. package/dist/src/lib/swift-codegen/functionGenerator.d.ts +3 -3
  99. package/dist/src/lib/swift-codegen/functionGenerator.js +22 -17
  100. package/dist/src/lib/swift-codegen/functionGenerator.js.map +1 -1
  101. package/dist/src/lib/swift-codegen/generator.js +27 -27
  102. package/dist/src/lib/swift-codegen/generator.js.map +1 -1
  103. package/dist/src/lib/toml-database-config.d.ts +14 -75
  104. package/dist/src/lib/toml-database-config.js +14 -305
  105. package/dist/src/lib/toml-database-config.js.map +1 -1
  106. package/dist/src/lib/workflow-toml-validator.js +15 -1
  107. package/dist/src/lib/workflow-toml-validator.js.map +1 -1
  108. package/package.json +3 -5
  109. package/dist/src/lib/generated-workflow-model-fields.d.ts +0 -20
  110. package/dist/src/lib/generated-workflow-model-fields.js +0 -53
  111. package/dist/src/lib/generated-workflow-model-fields.js.map +0 -1
  112. package/dist/src/lib/workflow-field-descriptor.d.ts +0 -91
  113. package/dist/src/lib/workflow-field-descriptor.js +0 -153
  114. package/dist/src/lib/workflow-field-descriptor.js.map +0 -1
@@ -134,55 +134,15 @@ export function parseErrorResponse(response, text, path) {
134
134
  return { message, code, details, raw: errorData };
135
135
  }
136
136
  /**
137
- * Typed exception classes for the database-schema feature (issue #666).
138
- * Each maps 1:1 to a server `code` value emitted from the op-edit or
139
- * schema-edit gate. They all extend ApiError so existing catch-all paths
140
- * continue to work; specialized catch blocks can branch on `instanceof`.
141
- *
142
- * Per round-2 addendum A1, `details` is always preserved so callers can
143
- * extract structured offender lists (refs[], operations[], etc.).
137
+ * Typed exception for the database-schema feature (issue #666): a stored
138
+ * schema that does not parse. `details` carries the line and column.
139
+ * (#3189 — the operation gates' exceptions retired with operations.)
144
140
  */
145
- export class SchemaRequiredError extends ApiError {
146
- constructor(message, details) {
147
- super(message, 422, "SCHEMA_REQUIRED", details);
148
- this.name = "SchemaRequiredError";
149
- }
150
- }
151
141
  function detailsRecord(details) {
152
142
  return details && !Array.isArray(details) && typeof details === "object"
153
143
  ? details
154
144
  : undefined;
155
145
  }
156
- export class OperationRefError extends ApiError {
157
- constructor(message, details) {
158
- super(message, 422, "OPERATION_REFERENCES_UNDEFINED", details);
159
- this.name = "OperationRefError";
160
- }
161
- get refs() {
162
- const d = detailsRecord(this.details);
163
- return Array.isArray(d?.refs) ? d.refs : [];
164
- }
165
- }
166
- export class SchemaBreaksOpsError extends ApiError {
167
- constructor(message, details) {
168
- super(message, 422, "SCHEMA_BREAKS_OPERATIONS", details);
169
- this.name = "SchemaBreaksOpsError";
170
- }
171
- get operations() {
172
- const d = detailsRecord(this.details);
173
- return Array.isArray(d?.operations) ? d.operations : [];
174
- }
175
- }
176
- export class SchemaHasUncheckableOpsError extends ApiError {
177
- constructor(message, details) {
178
- super(message, 422, "SCHEMA_HAS_UNCHECKABLE_OPS", details);
179
- this.name = "SchemaHasUncheckableOpsError";
180
- }
181
- get operations() {
182
- const d = detailsRecord(this.details);
183
- return Array.isArray(d?.operations) ? d.operations : [];
184
- }
185
- }
186
146
  export class TomlParseError extends ApiError {
187
147
  constructor(message, details) {
188
148
  super(message, 400, "TOML_PARSE_ERROR", details);
@@ -197,55 +157,6 @@ export class TomlParseError extends ApiError {
197
157
  return typeof d?.column === "number" ? d.column : undefined;
198
158
  }
199
159
  }
200
- export class OpsExistError extends ApiError {
201
- constructor(message, details) {
202
- super(message, 409, "OPS_EXIST", details);
203
- this.name = "OpsExistError";
204
- }
205
- get opCount() {
206
- const d = detailsRecord(this.details);
207
- return typeof d?.opCount === "number" ? d.opCount : 0;
208
- }
209
- }
210
- /**
211
- * Normalize a `databases operations execute` result to the CLI's one list
212
- * envelope — but only when the result is a Durable Object query page
213
- * (issue #2440).
214
- *
215
- * A registered operation is a list only sometimes. Of the shapes the server's
216
- * operation dispatch can return, exactly two carry a top-level `data` array: a
217
- * bare `query` and a `pipeline` whose `returnField` names a query step. A
218
- * `count` (`{ count }`), an `aggregate` (`{ result }`), a mutation
219
- * (`{ results }`), an `applyToQuery` (`{ matched, affected, failed, … }`) and a
220
- * `returnField: "all"` pipeline (`{ steps: { … } }`) do not, so blanket
221
- * normalization would corrupt them. This helper therefore tests the shape
222
- * first and passes everything else through byte-for-byte — including the
223
- * nested steps of a `returnField: "all"` pipeline, which are operation-defined
224
- * objects the CLI must not reach into.
225
- *
226
- * Deliberately narrow, not a general-purpose predicate: the contract is
227
- * specifically "an operations-execute result may be a DO query page".
228
- * `executeDatabaseOperation()` is the only caller.
229
- *
230
- * The conversion itself is delegated to `normalizeCliListEnvelope()` so cursor
231
- * handling, `prevCursor` dropping and `hasMore` derivation have exactly one
232
- * implementation. That helper throws on an unrecognized shape — correct when
233
- * the payload is supposed to be a list, wrong here, where the payload is the
234
- * caller's own result — which is why the shape test has to run first. `_timing`
235
- * (`--timing`) is re-attached afterwards, since it rides alongside the envelope
236
- * rather than inside it.
237
- */
238
- export function normalizeDatabaseOperationResult(raw) {
239
- if (raw === null || typeof raw !== "object" || Array.isArray(raw))
240
- return raw;
241
- const page = raw;
242
- if (!Array.isArray(page.data))
243
- return raw;
244
- const envelope = normalizeCliListEnvelope(page);
245
- if (page._timing !== undefined)
246
- envelope._timing = page._timing;
247
- return envelope;
248
- }
249
160
  export class ApiClient {
250
161
  /**
251
162
  * Loaded on first use (`ensureAuthenticated`), never in a constructor.
@@ -302,8 +213,9 @@ export class ApiClient {
302
213
  // error or a server-side rejection. Callers that need a finer
303
214
  // distinction (e.g. `primitive token`) consume RefreshError directly
304
215
  // from refresh-admin-credentials.ts instead of going through here.
305
- // A revoked session (#3885) says so.
306
- throw new ApiError(err.code === "SESSION_REVOKED"
216
+ // A refusal naming the session says why: revoked (#3885), or a login
217
+ // made before scoped sessions (#3891).
218
+ throw new ApiError(err.code === "SESSION_REVOKED" || err.code === "SESSION_UNSCOPED"
307
219
  ? err.message
308
220
  : "Token refresh failed. Please login again.", 401, err.code);
309
221
  }
@@ -392,24 +304,9 @@ export class ApiClient {
392
304
  if (response.status === 409 && parsed.code === "CONFLICT") {
393
305
  return new ConflictError(parsed.message, serverModifiedAt, expectedModifiedAt, detailsRecord);
394
306
  }
395
- if (response.status === 409 && parsed.code === "OPS_EXIST") {
396
- return new OpsExistError(parsed.message, detailsRecord);
397
- }
398
307
  if (response.status === 400 && parsed.code === "TOML_PARSE_ERROR") {
399
308
  return new TomlParseError(parsed.message, detailsRecord);
400
309
  }
401
- if (response.status === 422 && parsed.code === "SCHEMA_REQUIRED") {
402
- return new SchemaRequiredError(parsed.message, detailsRecord);
403
- }
404
- if (response.status === 422 && parsed.code === "OPERATION_REFERENCES_UNDEFINED") {
405
- return new OperationRefError(parsed.message, detailsRecord);
406
- }
407
- if (response.status === 422 && parsed.code === "SCHEMA_BREAKS_OPERATIONS") {
408
- return new SchemaBreaksOpsError(parsed.message, detailsRecord);
409
- }
410
- if (response.status === 422 && parsed.code === "SCHEMA_HAS_UNCHECKABLE_OPS") {
411
- return new SchemaHasUncheckableOpsError(parsed.message, detailsRecord);
412
- }
413
310
  return new ApiError(parsed.message, response.status, parsed.code, parsed.details);
414
311
  }
415
312
  /**
@@ -1149,10 +1046,9 @@ export class ApiClient {
1149
1046
  // ============================================
1150
1047
  /**
1151
1048
  * List the live connections the server believes exist for exactly one of a
1152
- * document, user, or database. Read-only inspection over admin-api. Rows are
1049
+ * document or user. Read-only inspection over admin-api. Rows are
1153
1050
  * per-connection-×-subscription bindings, so `connectionId` is not unique
1154
- * across rows. `params` on a database-subscription row is caller-defined
1155
- * bound CEL input — typed `unknown`.
1051
+ * across rows.
1156
1052
  */
1157
1053
  async listConnections(appId, selector, params) {
1158
1054
  const result = await this.get(`/admin/api/apps/${appId}/connections`, {
@@ -2147,45 +2043,6 @@ export class ApiClient {
2147
2043
  });
2148
2044
  }
2149
2045
  // ============================================
2150
- // DATABASE OPERATIONS (registered operations)
2151
- // ============================================
2152
- async listDatabaseOperations(appId, databaseId) {
2153
- return this.get(`/app/${appId}/api/databases/${databaseId}/operations`);
2154
- }
2155
- /**
2156
- * Execute a registered operation and, when the result is a query page,
2157
- * normalize it to the CLI's one list envelope (issue #2440).
2158
- *
2159
- * The registered operation decides the response shape, so the normalization
2160
- * is conditional — see {@link normalizeDatabaseOperationResult}. Everything
2161
- * else (a `count`, an `aggregate`, a mutation, a `returnField: "all"`
2162
- * pipeline) is returned exactly as the server sent it.
2163
- */
2164
- async executeDatabaseOperation(appId, databaseId, operationName, data, options) {
2165
- const path = `/app/${appId}/api/databases/${databaseId}/operations/${encodeURIComponent(operationName)}/execute`;
2166
- const extraHeaders = {};
2167
- if (options?.timing) {
2168
- extraHeaders["X-Timing"] = "true";
2169
- }
2170
- return normalizeDatabaseOperationResult(await this.request(path, {
2171
- method: "POST",
2172
- body: JSON.stringify(data || {}),
2173
- headers: extraHeaders,
2174
- }));
2175
- }
2176
- /**
2177
- * Execute a registered batch (bulk) database operation. Posts a chunk of
2178
- * items to the canonical `operations/:name/batch` endpoint (the same one the
2179
- * client library's `executeBatch` uses — NOT the deprecated `import-bulk`
2180
- * alias). Used by `databases import-csv`.
2181
- *
2182
- * @returns `{ imported, failed }` — DO-level write outcome counts. Per-item
2183
- * validation/access failures abort the whole chunk with a 4xx (thrown).
2184
- */
2185
- async executeBatch(appId, databaseId, operationName, batch) {
2186
- return this.post(`/app/${appId}/api/databases/${databaseId}/operations/${encodeURIComponent(operationName)}/batch`, { batch });
2187
- }
2188
- // ============================================
2189
2046
  // DATABASE INDEXES
2190
2047
  // ============================================
2191
2048
  async listDatabaseIndexes(appId, databaseId, modelName) {
@@ -2218,7 +2075,7 @@ export class ApiClient {
2218
2075
  // Issue #1336 (codex round-1 P2): `dryRun` runs every create-time
2219
2076
  // validation (inline CEL rule lint, manifest shape, schema TOML parse)
2220
2077
  // WITHOUT persisting, so a fresh-type `config push --dry-run` surfaces a
2221
- // malformed `defaultAccess`/manifest the same way the real POST would.
2078
+ // malformed rule or manifest the same way the real POST would.
2222
2079
  options) {
2223
2080
  const query = options?.dryRun ? "?dryRun=true" : "";
2224
2081
  return this.post(`/app/${appId}/api/databases/types${query}`, data);
@@ -2239,105 +2096,14 @@ export class ApiClient {
2239
2096
  return this.delete(`/app/${appId}/api/databases/types/${encodeURIComponent(databaseType)}${query}`);
2240
2097
  }
2241
2098
  /**
2242
- * Issue #666 Phase 3: ask the server to scaffold a TOML schema from
2243
- * existing ops + DO field introspection. Read-only — does NOT persist.
2099
+ * Issue #666 Phase 3: ask the server to scaffold a TOML schema from the
2100
+ * type's databases (the models each holds, and their fields). Read-only —
2101
+ * does NOT persist.
2244
2102
  */
2245
2103
  async scaffoldDatabaseTypeSchema(appId, databaseType) {
2246
2104
  return this.post(`/app/${appId}/api/databases/types/${encodeURIComponent(databaseType)}/schema:scaffold`, {});
2247
2105
  }
2248
2106
  // ============================================
2249
- // DATABASE TYPE OPERATIONS
2250
- // ============================================
2251
- async listDatabaseTypeOperations(appId, databaseType) {
2252
- return this.get(`/app/${appId}/api/databases/types/${encodeURIComponent(databaseType)}/operations`);
2253
- }
2254
- async getDatabaseTypeOperation(appId, databaseType, name) {
2255
- return this.get(`/app/${appId}/api/databases/types/${encodeURIComponent(databaseType)}/operations/${encodeURIComponent(name)}`);
2256
- }
2257
- async createDatabaseTypeOperation(appId, databaseType, data,
2258
- // Issue #813 (1A): when `dryRun` is set the server runs the op-edit gate
2259
- // without persisting — used by `config push --dry-run` to surface gate
2260
- // failures the real push would hit. `schemaOverride` lets the gate run
2261
- // against the schema the SAME push is about to land (so an op that depends
2262
- // on a new field isn't falsely rejected against the stale stored schema).
2263
- //
2264
- // Issue #915 (defect a, follow-up): for a FRESH db-type the parent config
2265
- // does not exist on the server yet, so the server's "access required"
2266
- // check has no `defaultAccess` to fall back on. Thread the TOML-declared
2267
- // type-level `defaultAccess` so an op that omits `access` (intending to
2268
- // inherit it after the type is created) isn't falsely rejected during the
2269
- // dry-run. Server only trusts this on the fresh-type dry-run path.
2270
- options) {
2271
- const query = options?.dryRun ? "?dryRun=true" : "";
2272
- let body = data;
2273
- if (options?.dryRun && options.schemaOverride !== undefined) {
2274
- body = { ...body, schemaOverride: options.schemaOverride };
2275
- }
2276
- if (options?.dryRun && options.defaultAccess !== undefined) {
2277
- body = { ...body, defaultAccess: options.defaultAccess };
2278
- }
2279
- if (options?.dryRun && options.metadataManifestOverride !== undefined) {
2280
- body = { ...body, metadataManifestOverride: options.metadataManifestOverride };
2281
- }
2282
- return this.post(`/app/${appId}/api/databases/types/${encodeURIComponent(databaseType)}/operations${query}`, body);
2283
- }
2284
- async updateDatabaseTypeOperation(appId, databaseType, name, data, expectedModifiedAt,
2285
- // Issue #813 (1A): dry-run runs the op-edit gate without persisting.
2286
- // `schemaOverride` gates against the schema the same push is landing.
2287
- // Issue #1336: `metadataManifestOverride` gates the op-access lint against
2288
- // the manifest the same push is landing (see `createDatabaseTypeOperation`).
2289
- options) {
2290
- let body = expectedModifiedAt ? { ...data, expectedModifiedAt } : { ...data };
2291
- if (options?.dryRun && options.schemaOverride !== undefined) {
2292
- body = { ...body, schemaOverride: options.schemaOverride };
2293
- }
2294
- if (options?.dryRun && options.metadataManifestOverride !== undefined) {
2295
- body = { ...body, metadataManifestOverride: options.metadataManifestOverride };
2296
- }
2297
- const query = options?.dryRun ? "?dryRun=true" : "";
2298
- return this.patch(`/app/${appId}/api/databases/types/${encodeURIComponent(databaseType)}/operations/${encodeURIComponent(name)}${query}`, body);
2299
- }
2300
- async deleteDatabaseTypeOperation(appId, databaseType, name) {
2301
- return this.delete(`/app/${appId}/api/databases/types/${encodeURIComponent(databaseType)}/operations/${encodeURIComponent(name)}`);
2302
- }
2303
- // ============================================
2304
- // DATABASE TYPE SUBSCRIPTIONS (#740 / #803)
2305
- // ============================================
2306
- //
2307
- // Transparent passthroughs against the server's type-scoped subscription
2308
- // routes (GET/POST/PUT/DELETE `/databases/types/:type/subscriptions[/:key]`).
2309
- // The wire format is authoritative in the controller
2310
- // (`database-type-subscriptions-controller.ts`): create requires
2311
- // `subscriptionKey`, `displayName`, `modelName`, `filter` (CEL), `access`
2312
- // (CEL); optional `description`, `select` (string[]), `emit`
2313
- // (enter/update/leave subset), `params` (object, ≤5 entries), `status`.
2314
- //
2315
- // The list endpoint wraps results in `{ items: [...] }` (unlike operations,
2316
- // which returns a bare array). We unwrap here so callers get an array,
2317
- // matching `listDatabaseTypeOperations`.
2318
- async listDatabaseTypeSubscriptions(appId, databaseType) {
2319
- const result = await this.get(`/app/${appId}/api/databases/types/${encodeURIComponent(databaseType)}/subscriptions`);
2320
- if (Array.isArray(result))
2321
- return result;
2322
- return Array.isArray(result?.items) ? result.items : [];
2323
- }
2324
- async getDatabaseTypeSubscription(appId, databaseType, subscriptionKey) {
2325
- return this.get(`/app/${appId}/api/databases/types/${encodeURIComponent(databaseType)}/subscriptions/${encodeURIComponent(subscriptionKey)}`);
2326
- }
2327
- async createDatabaseTypeSubscription(appId, databaseType, data) {
2328
- return this.post(`/app/${appId}/api/databases/types/${encodeURIComponent(databaseType)}/subscriptions`, data);
2329
- }
2330
- async updateDatabaseTypeSubscription(appId, databaseType, subscriptionKey, data,
2331
- // #2731 B10 — optimistic concurrency, as the sibling operation update has
2332
- // had. Omitted (by `--force`, and by every caller predating it) means the
2333
- // write stays unconditional.
2334
- expectedModifiedAt) {
2335
- return this.put(`/app/${appId}/api/databases/types/${encodeURIComponent(databaseType)}/subscriptions/${encodeURIComponent(subscriptionKey)}`, expectedModifiedAt ? { ...data, expectedModifiedAt } : data);
2336
- }
2337
- async deleteDatabaseTypeSubscription(appId, databaseType, subscriptionKey) {
2338
- return this.delete(`/app/${appId}/api/databases/types/${encodeURIComponent(databaseType)}/subscriptions/${encodeURIComponent(subscriptionKey)}`);
2339
- }
2340
- // ============================================
2341
2107
  // GROUPS
2342
2108
  // ============================================
2343
2109
  async listGroups(appId, params) {
@@ -2655,51 +2421,6 @@ export class ApiClient {
2655
2421
  async importDocumentState(appId, documentId, stateBase64) {
2656
2422
  return this.post(`/app/${appId}/api/documents/${documentId}/import/state`, { state: stateBase64 });
2657
2423
  }
2658
- /**
2659
- * Upload one artifact of a large document's import chain (#2816,
2660
- * behavior 20).
2661
- *
2662
- * The bytes travel raw and unmodified: the manifest's digest is over the
2663
- * STORED bytes, so re-encoding a chunk here would fail its own verification
2664
- * on install. The query says which artifact it is — the server derives the
2665
- * key from the document it belongs to, so nothing here composes one.
2666
- */
2667
- async uploadDocumentChainArtifact(appId, documentId, query, body) {
2668
- const credentials = await this.ensureAuthenticated();
2669
- const search = new URLSearchParams();
2670
- for (const [key, value] of Object.entries(query)) {
2671
- search.set(key, String(value));
2672
- }
2673
- const url = `${credentials.serverUrl}/app/${appId}/api/documents/${documentId}` +
2674
- `/import/chain/artifact?${search.toString()}`;
2675
- const headers = {
2676
- Authorization: `Bearer ${credentials.accessToken}`,
2677
- "Content-Type": "application/octet-stream",
2678
- };
2679
- if (credentials.globalAdminAppId) {
2680
- headers["X-Global-Admin-App-Id"] = credentials.globalAdminAppId;
2681
- }
2682
- const response = await fetchWithTLS(url, {
2683
- method: "POST",
2684
- headers,
2685
- body: body,
2686
- });
2687
- if (!response.ok) {
2688
- // #3403 — through the shared parser: reading `error` alone dropped the
2689
- // envelope's `code` and `details`, so the refusal reached the operator
2690
- // (and `primitive documents import`) with nothing to branch on.
2691
- throw await blobFailure(response, "Failed to upload import artifact: ");
2692
- }
2693
- return await response.json().catch(() => ({}));
2694
- }
2695
- /** Start installing an uploaded chain (#2816, behavior 20). */
2696
- async installDocumentChain(appId, documentId, plan) {
2697
- return this.post(`/app/${appId}/api/documents/${documentId}/import/chain`, plan);
2698
- }
2699
- /** How far a document's chain install has got (#2816, behavior 20). */
2700
- async getDocumentChainInstall(appId, documentId) {
2701
- return this.get(`/app/${appId}/api/documents/${documentId}/import/chain`);
2702
- }
2703
2424
  async getDocument(appId, documentId) {
2704
2425
  return this.get(`/app/${appId}/api/documents/${documentId}`);
2705
2426
  }
@@ -2793,10 +2514,19 @@ export class ApiClient {
2793
2514
  /**
2794
2515
  * A record-log document's room (#4306): `GET documents/:id/room` →
2795
2516
  * `{ ok, appId, documentId, documentFormat, room }`, `room` null for a
2796
- * format that has no record room. App admins only.
2517
+ * format that has no record room. With `refused` (#4310), the answer also
2518
+ * carries a page of that bulk load's refused rows. App admins only.
2797
2519
  */
2798
- async getDocumentRoom(appId, documentId) {
2799
- return this.get(`/app/${appId}/api/documents/${documentId}/room`);
2520
+ async getDocumentRoom(appId, documentId, query = {}) {
2521
+ const params = new URLSearchParams();
2522
+ if (query.refused !== undefined)
2523
+ params.set("refused", query.refused);
2524
+ if (query.limit !== undefined)
2525
+ params.set("limit", String(query.limit));
2526
+ if (query.cursor !== undefined)
2527
+ params.set("cursor", query.cursor);
2528
+ const search = params.toString();
2529
+ return this.get(`/app/${appId}/api/documents/${documentId}/room${search ? `?${search}` : ""}`);
2800
2530
  }
2801
2531
  /**
2802
2532
  * A large document's snapshot builds, newest first, one page (#3432).
@@ -2831,9 +2561,13 @@ export class ApiClient {
2831
2561
  async getDocumentIngest(appId, documentId, sessionId) {
2832
2562
  return this.get(`/app/${appId}/api/documents/${documentId}/ingest/${encodeURIComponent(sessionId)}`);
2833
2563
  }
2834
- /** Open a bulk-load session on a large document (#3435, criterion 9). */
2835
- async createDocumentIngest(appId, documentId) {
2836
- return this.post(`/app/${appId}/api/documents/${documentId}/ingest`, {});
2564
+ /**
2565
+ * Open a bulk-load session on a large document (#3435, criterion 9). A
2566
+ * record-log (format 3) document's load takes `reserveEpoch`, `mode` and a
2567
+ * refresh's `models` (#4310); `{}` keeps every format's defaults.
2568
+ */
2569
+ async createDocumentIngest(appId, documentId, options = {}) {
2570
+ return this.post(`/app/${appId}/api/documents/${documentId}/ingest`, options);
2837
2571
  }
2838
2572
  /**
2839
2573
  * Upload one chunk of a bulk-load session's artifact (#3435).