@happyvertical/smrt-core 0.49.2 → 0.49.4

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 (156) hide show
  1. package/README.md +4 -0
  2. package/agents/change-feed.md +19 -0
  3. package/agents/collection-reads.md +21 -0
  4. package/agents/generators.md +5 -0
  5. package/agents/memory.md +23 -3
  6. package/agents/null-equal-indexes.md +88 -0
  7. package/agents/object-runtime.md +85 -0
  8. package/agents/schema-paths.md +7 -7
  9. package/dist/browser.js +2 -2
  10. package/dist/cascade.d.ts.map +1 -1
  11. package/dist/cascade.js +13 -10
  12. package/dist/cascade.js.map +1 -1
  13. package/dist/change-feed.d.ts +22 -0
  14. package/dist/change-feed.d.ts.map +1 -1
  15. package/dist/change-feed.js +147 -6
  16. package/dist/change-feed.js.map +1 -1
  17. package/dist/class.js +1 -1
  18. package/dist/collection-cache.d.ts +10 -1
  19. package/dist/collection-cache.d.ts.map +1 -1
  20. package/dist/collection-cache.js +65 -10
  21. package/dist/collection-cache.js.map +1 -1
  22. package/dist/collection.d.ts +69 -2
  23. package/dist/collection.d.ts.map +1 -1
  24. package/dist/collection.js +142 -9
  25. package/dist/collection.js.map +1 -1
  26. package/dist/consumer-plugin/artifact-publication.d.ts +27 -0
  27. package/dist/consumer-plugin/artifact-publication.d.ts.map +1 -0
  28. package/dist/consumer-plugin/artifact-publication.js +80 -0
  29. package/dist/consumer-plugin/artifact-publication.js.map +1 -0
  30. package/dist/consumer-plugin/index.d.ts.map +1 -1
  31. package/dist/consumer-plugin/index.js +37 -4
  32. package/dist/consumer-plugin/index.js.map +1 -1
  33. package/dist/db-errors.js +1 -1
  34. package/dist/db-errors.js.map +1 -1
  35. package/dist/embedded-write-queue.d.ts +1 -1
  36. package/dist/embedded-write-queue.d.ts.map +1 -1
  37. package/dist/embedded-write-queue.js +16 -6
  38. package/dist/embedded-write-queue.js.map +1 -1
  39. package/dist/embeddings/storage.d.ts +16 -0
  40. package/dist/embeddings/storage.d.ts.map +1 -1
  41. package/dist/embeddings/storage.js +40 -0
  42. package/dist/embeddings/storage.js.map +1 -1
  43. package/dist/index.d.ts +1 -1
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +3 -3
  46. package/dist/interceptors.d.ts +22 -0
  47. package/dist/interceptors.d.ts.map +1 -1
  48. package/dist/interceptors.js +12 -0
  49. package/dist/interceptors.js.map +1 -1
  50. package/dist/junction.d.ts +12 -3
  51. package/dist/junction.d.ts.map +1 -1
  52. package/dist/junction.js +34 -4
  53. package/dist/junction.js.map +1 -1
  54. package/dist/knowledge-config.d.ts +6 -0
  55. package/dist/knowledge-config.d.ts.map +1 -0
  56. package/dist/knowledge-config.js +48 -0
  57. package/dist/knowledge-config.js.map +1 -0
  58. package/dist/manifest/static-manifest.d.ts.map +1 -1
  59. package/dist/manifest/static-manifest.js +91 -1
  60. package/dist/manifest/static-manifest.js.map +1 -1
  61. package/dist/manifest/store.js +1 -1
  62. package/dist/manifest/store.js.map +1 -1
  63. package/dist/manifest.json +104 -1
  64. package/dist/migrations/differ.d.ts.map +1 -1
  65. package/dist/migrations/differ.js +2 -0
  66. package/dist/migrations/differ.js.map +1 -1
  67. package/dist/migrations/generator.d.ts.map +1 -1
  68. package/dist/migrations/generator.js +2 -0
  69. package/dist/migrations/generator.js.map +1 -1
  70. package/dist/migrations/index.d.ts +1 -0
  71. package/dist/migrations/index.d.ts.map +1 -1
  72. package/dist/migrations/index.js +2 -1
  73. package/dist/migrations/null-equal-indexes.d.ts +37 -0
  74. package/dist/migrations/null-equal-indexes.d.ts.map +1 -0
  75. package/dist/migrations/null-equal-indexes.js +133 -0
  76. package/dist/migrations/null-equal-indexes.js.map +1 -0
  77. package/dist/migrations/tracker.d.ts.map +1 -1
  78. package/dist/migrations/tracker.js +6 -0
  79. package/dist/migrations/tracker.js.map +1 -1
  80. package/dist/migrations.js +2 -1
  81. package/dist/object.d.ts +33 -0
  82. package/dist/object.d.ts.map +1 -1
  83. package/dist/object.js +316 -46
  84. package/dist/object.js.map +1 -1
  85. package/dist/plain-json.d.ts +6 -0
  86. package/dist/plain-json.d.ts.map +1 -0
  87. package/dist/plain-json.js +41 -0
  88. package/dist/plain-json.js.map +1 -0
  89. package/dist/postgres-permissions.d.ts.map +1 -1
  90. package/dist/postgres-permissions.js +6 -2
  91. package/dist/postgres-permissions.js.map +1 -1
  92. package/dist/prebuild/index.d.ts.map +1 -1
  93. package/dist/prebuild/index.js +3 -3
  94. package/dist/prebuild/index.js.map +1 -1
  95. package/dist/registry/class-registration.d.ts.map +1 -1
  96. package/dist/registry/class-registration.js +4 -1
  97. package/dist/registry/class-registration.js.map +1 -1
  98. package/dist/registry/schema-builder.d.ts.map +1 -1
  99. package/dist/registry/schema-builder.js +4 -3
  100. package/dist/registry/schema-builder.js.map +1 -1
  101. package/dist/registry/validator.d.ts +1 -0
  102. package/dist/registry/validator.d.ts.map +1 -1
  103. package/dist/registry/validator.js +8 -1
  104. package/dist/registry/validator.js.map +1 -1
  105. package/dist/registry.js +1 -1
  106. package/dist/scanner/manifest-generator.d.ts.map +1 -1
  107. package/dist/scanner/manifest-generator.js +21 -0
  108. package/dist/scanner/manifest-generator.js.map +1 -1
  109. package/dist/scanner/types.d.ts +2 -0
  110. package/dist/scanner/types.d.ts.map +1 -1
  111. package/dist/scanner/types.js.map +1 -1
  112. package/dist/schema/conflict-target.d.ts +8 -2
  113. package/dist/schema/conflict-target.d.ts.map +1 -1
  114. package/dist/schema/conflict-target.js +11 -3
  115. package/dist/schema/conflict-target.js.map +1 -1
  116. package/dist/schema/ddl/null-equal-index.d.ts +5 -0
  117. package/dist/schema/ddl/null-equal-index.d.ts.map +1 -0
  118. package/dist/schema/ddl/null-equal-index.js +17 -0
  119. package/dist/schema/ddl/null-equal-index.js.map +1 -0
  120. package/dist/schema/ddl/postgres-strategy.d.ts +2 -1
  121. package/dist/schema/ddl/postgres-strategy.d.ts.map +1 -1
  122. package/dist/schema/ddl/postgres-strategy.js +7 -0
  123. package/dist/schema/ddl/postgres-strategy.js.map +1 -1
  124. package/dist/schema/generator.d.ts.map +1 -1
  125. package/dist/schema/generator.js +8 -3
  126. package/dist/schema/generator.js.map +1 -1
  127. package/dist/schema/manifest-schema.d.ts +2 -1
  128. package/dist/schema/manifest-schema.d.ts.map +1 -1
  129. package/dist/schema/manifest-schema.js +12 -4
  130. package/dist/schema/manifest-schema.js.map +1 -1
  131. package/dist/schema/schema-manager.d.ts.map +1 -1
  132. package/dist/schema/schema-manager.js +1 -1
  133. package/dist/schema/schema-manager.js.map +1 -1
  134. package/dist/schema/types.d.ts +2 -0
  135. package/dist/schema/types.d.ts.map +1 -1
  136. package/dist/scripts/browser-plain-json-regression.mjs +85 -0
  137. package/dist/smrt-knowledge.json +73 -15
  138. package/dist/system/schema.d.ts +14 -3
  139. package/dist/system/schema.d.ts.map +1 -1
  140. package/dist/system/schema.js +120 -2
  141. package/dist/system/schema.js.map +1 -1
  142. package/dist/vite-plugin/api-client-entries.d.ts.map +1 -1
  143. package/dist/vite-plugin/api-client-entries.js +9 -7
  144. package/dist/vite-plugin/api-client-entries.js.map +1 -1
  145. package/dist/vite-plugin/index.d.ts +24 -0
  146. package/dist/vite-plugin/index.d.ts.map +1 -1
  147. package/dist/vite-plugin/index.js +47 -64
  148. package/dist/vite-plugin/index.js.map +1 -1
  149. package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
  150. package/dist/vite-plugin/sveltekit-generator.js +2 -2
  151. package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
  152. package/dist/vite-plugin/web-collections.d.ts +10 -0
  153. package/dist/vite-plugin/web-collections.d.ts.map +1 -1
  154. package/dist/vite-plugin/web-collections.js +14 -2
  155. package/dist/vite-plugin/web-collections.js.map +1 -1
  156. package/package.json +5 -4
@@ -140,6 +140,8 @@ export interface AppendChangeInput {
140
140
  /** Tenant the change belongs to (default `null` = global). */
141
141
  tenantId?: string | null;
142
142
  }
143
+ /** A batch of mutations to append as one framework database statement. */
144
+ export type AppendChangeBatch = readonly AppendChangeInput[];
143
145
  /** Retention bounds for {@link pruneChangeFeed}. At least one is required. */
144
146
  export interface ChangeFeedRetention {
145
147
  /** Prune entries older than this many milliseconds. */
@@ -225,6 +227,16 @@ export declare function ensureChangeFeedTable(db: DatabaseInterface): Promise<vo
225
227
  * handle from interleaving inside a manual SAVEPOINT scope.
226
228
  */
227
229
  export declare function appendChange(db: DatabaseInterface, input: AppendChangeInput): Promise<number | null>;
230
+ /**
231
+ * Append a mutation batch with one feed row per input entry.
232
+ *
233
+ * The batch is deliberately one statement per supported dialect. In
234
+ * particular, PostgreSQL enters the existing append helper from one
235
+ * framework-owned function call, so a caller transaction still stages every
236
+ * entry, preserves rollback/failure isolation, and never waits on the feed
237
+ * head. Direct batches allocate contiguous sequences in input order.
238
+ */
239
+ export declare function appendChanges(db: DatabaseInterface, inputs: AppendChangeBatch): Promise<Array<number | null>>;
228
240
  /**
229
241
  * Sequence change-feed entries staged inside caller-managed transactions
230
242
  * (issue #2649). PostgreSQL only; a no-op on every other engine.
@@ -417,6 +429,16 @@ export declare const CHANGE_FEED_WAS_PERSISTED_KEY = "_smrtChangeFeedWasPersiste
417
429
  export declare function registerChangeFeedWriter(): void;
418
430
  /** Unregister the change-feed writer (test helper). */
419
431
  export declare function unregisterChangeFeedWriter(): boolean;
432
+ /**
433
+ * Record a collection mutation through one batch append and publish its direct
434
+ * entries in feed order. The collection lifecycle supplies one database
435
+ * context; malformed or framework-owned instances are skipped just as the
436
+ * single-instance writer does.
437
+ */
438
+ export declare function recordInstanceChanges(entries: readonly {
439
+ instance: SmrtObject;
440
+ operation: ChangeOperation;
441
+ }[]): Promise<void>;
420
442
  /** Record a framework-owned mutation that intentionally bypasses save hooks. */
421
443
  export declare function recordInstanceChange(instance: SmrtObject, operation?: ChangeOperation): Promise<void>;
422
444
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"change-feed.d.ts","sourceRoot":"","sources":["../src/change-feed.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"change-feed.d.ts","sourceRoot":"","sources":["../src/change-feed.ts"],"names":[],"mappings":"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"}
@@ -5,7 +5,7 @@ import { resolveDispatchTenantScope } from "./dispatch/tenant-resolver.js";
5
5
  import { isEmbeddedDatabase, withEmbeddedWriteQueue } from "./embedded-write-queue.js";
6
6
  import { GlobalInterceptors } from "./interceptors.js";
7
7
  import { detectEngine } from "./schema/ddl/index.js";
8
- import { CREATE_SMRT_CHANGES_TABLE, ENSURE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION, ENSURE_POSTGRES_CHANGE_FEED_SCHEMA, FRAMEWORK_OPERATIONAL_TABLES, POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY, POSTGRES_CHANGE_FEED_APPEND_FUNCTION_NAME, POSTGRES_CHANGE_FEED_DRAIN_FUNCTION_IDENTITY, POSTGRES_CHANGE_FEED_DRAIN_FUNCTION_NAME, POSTGRES_CHANGE_FEED_HELPER_MARKER, POSTGRES_CHANGE_FEED_PENDING_TABLE, REPLACE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION, RETIRED_SYSTEM_TABLES } from "./system/schema.js";
8
+ import { CREATE_SMRT_CHANGES_TABLE, ENSURE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION, ENSURE_POSTGRES_CHANGE_FEED_SCHEMA, FRAMEWORK_OPERATIONAL_TABLES, POSTGRES_CHANGE_FEED_APPEND_BATCH_FUNCTION_IDENTITY, POSTGRES_CHANGE_FEED_APPEND_BATCH_FUNCTION_NAME, POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY, POSTGRES_CHANGE_FEED_APPEND_FUNCTION_NAME, POSTGRES_CHANGE_FEED_DRAIN_FUNCTION_IDENTITY, POSTGRES_CHANGE_FEED_DRAIN_FUNCTION_NAME, POSTGRES_CHANGE_FEED_HELPER_MARKER, POSTGRES_CHANGE_FEED_PENDING_TABLE, REPLACE_POSTGRES_CHANGE_FEED_APPEND_FUNCTION, RETIRED_SYSTEM_TABLES } from "./system/schema.js";
9
9
  import { SYSTEM_TABLE_NAMES } from "./schema/system-table-shapes.js";
10
10
  import { createLogger } from "@happyvertical/logger";
11
11
  //#region src/change-feed.ts
@@ -269,7 +269,10 @@ var VALID_OPERATIONS = /* @__PURE__ */ new Set([
269
269
  ]);
270
270
  function getEngine(db, typeHint) {
271
271
  const withConfig = db;
272
- return detectEngine(db.url || withConfig.config?.url || "", typeHint || withConfig.type || withConfig.config?.type);
272
+ const engine = detectEngine(db.url || withConfig.config?.url || "", typeHint || withConfig.type || withConfig.config?.type);
273
+ const isDuckDbConnection = (withConfig.client?.constructor?.name?.toLowerCase() ?? "").includes("duckdb") || withConfig.client !== void 0 && "connection" in withConfig.client;
274
+ if (engine === "sqlite" && isDuckDbConnection) return "duckdb";
275
+ return engine;
273
276
  }
274
277
  /**
275
278
  * Positional placeholder factory: Postgres uses `$n`, SQLite/DuckDB use `?`.
@@ -338,14 +341,16 @@ function currentHelperProbe(identity) {
338
341
  async function postgresChangeFeedHelpersCurrent(db) {
339
342
  const rows = getQueryRows(await db.query(`SELECT
340
343
  ${currentHelperProbe(POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY)} AS function_name,
344
+ ${currentHelperProbe(POSTGRES_CHANGE_FEED_APPEND_BATCH_FUNCTION_IDENTITY)} AS batch_function_name,
341
345
  ${currentHelperProbe(POSTGRES_CHANGE_FEED_DRAIN_FUNCTION_IDENTITY)} AS drain_function_name,
342
346
  to_regclass('${POSTGRES_CHANGE_FEED_PENDING_TABLE}') AS pending_table_name`));
343
- return Boolean(rows[0]?.function_name && rows[0]?.drain_function_name && rows[0]?.pending_table_name);
347
+ return Boolean(rows[0]?.function_name && rows[0]?.batch_function_name && rows[0]?.drain_function_name && rows[0]?.pending_table_name);
344
348
  }
345
349
  async function getPostgresChangeFeedSchemaState(db) {
346
350
  const rows = getQueryRows(await db.query(`SELECT
347
351
  to_regclass('${CHANGE_FEED_TABLE}') AS table_name,
348
352
  ${currentHelperProbe(POSTGRES_CHANGE_FEED_APPEND_FUNCTION_IDENTITY)} AS function_name,
353
+ ${currentHelperProbe(POSTGRES_CHANGE_FEED_APPEND_BATCH_FUNCTION_IDENTITY)} AS batch_function_name,
349
354
  to_regclass('${POSTGRES_CHANGE_FEED_PENDING_TABLE}') AS pending_table_name,
350
355
  ${currentHelperProbe(POSTGRES_CHANGE_FEED_DRAIN_FUNCTION_IDENTITY)} AS drain_function_name,
351
356
  (
@@ -358,6 +363,7 @@ async function getPostgresChangeFeedSchemaState(db) {
358
363
  return {
359
364
  tableExists: Boolean(rows[0]?.table_name),
360
365
  functionExists: Boolean(rows[0]?.function_name),
366
+ batchFunctionExists: Boolean(rows[0]?.batch_function_name),
361
367
  pendingTableExists: Boolean(rows[0]?.pending_table_name),
362
368
  drainFunctionExists: Boolean(rows[0]?.drain_function_name),
363
369
  createdAtType: rows[0]?.created_at_type ? String(rows[0].created_at_type) : null
@@ -412,7 +418,7 @@ async function ensurePostgresChangeFeedHelpers(db, typeHint) {
412
418
  const state = await getPostgresChangeFeedSchemaState(db);
413
419
  if (!state.tableExists) return;
414
420
  if (state.createdAtType !== "timestamp with time zone") return;
415
- if (state.functionExists && state.pendingTableExists && state.drainFunctionExists) return;
421
+ if (state.functionExists && state.batchFunctionExists && state.pendingTableExists && state.drainFunctionExists) return;
416
422
  await db.query(ENSURE_POSTGRES_CHANGE_FEED_SCHEMA);
417
423
  }
418
424
  async function ensureChangeFeedTable(db) {
@@ -420,7 +426,7 @@ async function ensureChangeFeedTable(db) {
420
426
  if (getEngine(db) === "postgres") {
421
427
  const state = await getPostgresChangeFeedSchemaState(db);
422
428
  assertPostgresChangeFeedTimestampCurrent(state);
423
- if (state.tableExists && state.functionExists && state.pendingTableExists && state.drainFunctionExists && state.createdAtType === "timestamp with time zone") {
429
+ if (state.tableExists && state.functionExists && state.batchFunctionExists && state.pendingTableExists && state.drainFunctionExists && state.createdAtType === "timestamp with time zone") {
424
430
  ensuredHandles.add(db);
425
431
  return;
426
432
  }
@@ -522,6 +528,75 @@ async function appendChange(db, input) {
522
528
  throw new Error("appendChange exhausted retries without allocating a seq");
523
529
  }
524
530
  /**
531
+ * Append a mutation batch with one feed row per input entry.
532
+ *
533
+ * The batch is deliberately one statement per supported dialect. In
534
+ * particular, PostgreSQL enters the existing append helper from one
535
+ * framework-owned function call, so a caller transaction still stages every
536
+ * entry, preserves rollback/failure isolation, and never waits on the feed
537
+ * head. Direct batches allocate contiguous sequences in input order.
538
+ */
539
+ async function appendChanges(db, inputs) {
540
+ if (inputs.length === 0) return [];
541
+ const entries = inputs.map((input) => {
542
+ const table = input.table?.trim();
543
+ if (!table) throw new Error("appendChanges requires a non-empty table name");
544
+ const operation = input.operation ?? "update";
545
+ if (!VALID_OPERATIONS.has(operation)) throw new Error(`appendChanges operation must be one of create/update/delete, got '${String(input.operation)}'`);
546
+ return {
547
+ table,
548
+ rowId: input.rowId ?? null,
549
+ operation,
550
+ tenantId: input.tenantId ?? null,
551
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
552
+ };
553
+ });
554
+ const engine = getEngine(db);
555
+ const p = placeholders(db);
556
+ const payload = JSON.stringify(entries);
557
+ const sql = engine === "postgres" ? `SELECT entry_index, allocated_seq, error_code, error_message FROM ${POSTGRES_CHANGE_FEED_APPEND_BATCH_FUNCTION_NAME}(${p(1)}::jsonb)` : engine === "sqlite" ? `WITH input AS (SELECT CAST(key AS INTEGER) AS entry_index, json_extract(value, '$.table') AS table_name, json_extract(value, '$.rowId') AS row_id, json_extract(value, '$.operation') AS operation, json_extract(value, '$.tenantId') AS tenant_id, json_extract(value, '$.timestamp') AS created_at FROM json_each(${p(1)})), numbered AS (SELECT *, COALESCE((SELECT MAX(seq) FROM ${CHANGE_FEED_TABLE}), 0) + row_number() OVER (ORDER BY entry_index) AS seq FROM input) INSERT INTO ${CHANGE_FEED_TABLE} (seq, table_name, row_id, operation, tenant_id, created_at) SELECT seq, table_name, row_id, operation, tenant_id, created_at FROM numbered RETURNING seq` : `WITH input AS (SELECT CAST(key AS INTEGER) AS entry_index, json_extract_string(value, '$.table') AS table_name, json_extract_string(value, '$.rowId') AS row_id, json_extract_string(value, '$.operation') AS operation, json_extract_string(value, '$.tenantId') AS tenant_id, CAST(json_extract_string(value, '$.timestamp') AS TIMESTAMP) AS created_at FROM json_each(${p(1)})), numbered AS (SELECT *, COALESCE((SELECT MAX(seq) FROM ${CHANGE_FEED_TABLE}), 0) + row_number() OVER (ORDER BY entry_index) AS seq FROM input) INSERT INTO ${CHANGE_FEED_TABLE} (seq, table_name, row_id, operation, tenant_id, created_at) SELECT seq, table_name, row_id, operation, tenant_id, created_at FROM numbered RETURNING seq`;
558
+ const drainedSignals = await drainBeforeAppend(db);
559
+ const settleDrainedSignals = (sequenced) => {
560
+ if (drainedSignals.length === 0) return;
561
+ if (sequenced) publishSignals(db, drainedSignals);
562
+ else queueDeferredSignals(db, drainedSignals);
563
+ };
564
+ for (let attempt = 1; attempt <= MAX_APPEND_ATTEMPTS; attempt++) try {
565
+ const rows = getQueryRows(await withEmbeddedWriteQueue(db, isEmbeddedDatabase(db), () => db.query(sql, payload)));
566
+ if (engine === "postgres") {
567
+ const failure = rows.find((row) => row.error_code != null);
568
+ if (failure) {
569
+ const error = new Error(String(failure.error_message || "PostgreSQL change-feed batch append failed"));
570
+ error.code = String(failure.error_code);
571
+ throw error;
572
+ }
573
+ if (rows.length !== entries.length) throw new Error("PostgreSQL change-feed batch append returned an incomplete result");
574
+ const result = Array(entries.length).fill(null);
575
+ const seenIndexes = /* @__PURE__ */ new Set();
576
+ for (const row of rows) {
577
+ const index = Number(row.entry_index);
578
+ if (!Number.isInteger(index) || index < 0 || index >= result.length || seenIndexes.has(index)) throw new Error("PostgreSQL change-feed batch append returned invalid indexes");
579
+ seenIndexes.add(index);
580
+ result[index] = row.allocated_seq == null ? null : toSeqNumber(row.allocated_seq);
581
+ }
582
+ if (result.some((seq) => seq === null)) noteStagedAppend(db);
583
+ settleDrainedSignals(!result.some((seq) => seq === null));
584
+ for (const seq of result) if (seq != null) recordUncommittedDrainMark(db, seq);
585
+ return result;
586
+ }
587
+ if (rows.length !== entries.length) throw new Error("Change feed batch append returned an incomplete result");
588
+ const result = rows.map((row) => toSeqNumber(row.seq)).sort((a, b) => a - b);
589
+ settleDrainedSignals(true);
590
+ return result;
591
+ } catch (error) {
592
+ if (!isUniqueViolation(error) || attempt === MAX_APPEND_ATTEMPTS) {
593
+ queueDeferredSignals(db, drainedSignals);
594
+ throw error;
595
+ }
596
+ }
597
+ throw new Error("appendChanges exhausted retries without allocating a sequence");
598
+ }
599
+ /**
525
600
  * Sequence change-feed entries staged inside caller-managed transactions
526
601
  * (issue #2649). PostgreSQL only; a no-op on every other engine.
527
602
  *
@@ -1037,6 +1112,21 @@ function registerChangeFeedWriter() {
1037
1112
  },
1038
1113
  async afterDelete(instance) {
1039
1114
  await appendForInstance(instance, "delete");
1115
+ },
1116
+ bulkMutation: {
1117
+ compatible: () => true,
1118
+ async afterSave(entries) {
1119
+ await recordInstanceChanges(entries.map(({ instance, context }) => ({
1120
+ instance,
1121
+ operation: context.metadata?.["_smrtChangeFeedWasPersisted"] === true ? "update" : "create"
1122
+ })));
1123
+ },
1124
+ async afterDelete(entries) {
1125
+ await recordInstanceChanges(entries.map(({ instance }) => ({
1126
+ instance,
1127
+ operation: "delete"
1128
+ })));
1129
+ }
1040
1130
  }
1041
1131
  });
1042
1132
  }
@@ -1081,6 +1171,57 @@ async function appendForInstance(instance, operation) {
1081
1171
  warnAppendFailureOnce(db, table, error);
1082
1172
  }
1083
1173
  }
1174
+ /**
1175
+ * Record a collection mutation through one batch append and publish its direct
1176
+ * entries in feed order. The collection lifecycle supplies one database
1177
+ * context; malformed or framework-owned instances are skipped just as the
1178
+ * single-instance writer does.
1179
+ */
1180
+ async function recordInstanceChanges(entries) {
1181
+ const prepared = [];
1182
+ for (const entry of entries) try {
1183
+ const table = entry.instance.tableName;
1184
+ if (!isChangeFeedObservableTable(table)) continue;
1185
+ const id = entry.instance.id;
1186
+ const tenantId = entry.instance.tenantId;
1187
+ prepared.push({
1188
+ db: entry.instance.db,
1189
+ input: {
1190
+ table,
1191
+ rowId: typeof id === "string" && id ? id : null,
1192
+ operation: entry.operation,
1193
+ tenantId: typeof tenantId === "string" && tenantId ? tenantId : null
1194
+ }
1195
+ });
1196
+ } catch {}
1197
+ if (prepared.length === 0) return;
1198
+ const db = prepared[0].db;
1199
+ if (prepared.some((entry) => entry.db !== db)) {
1200
+ await Promise.all(prepared.map(({ input, db: entryDb }) => appendChange(entryDb, input)));
1201
+ return;
1202
+ }
1203
+ try {
1204
+ const sequences = await appendChanges(db, prepared.map(({ input }) => input));
1205
+ for (let index = 0; index < prepared.length; index++) {
1206
+ const seq = sequences[index];
1207
+ if (seq == null) continue;
1208
+ const input = prepared[index].input;
1209
+ try {
1210
+ publishChangeSignal(db, {
1211
+ table: input.table,
1212
+ operation: input.operation ?? "update",
1213
+ rowId: input.rowId ?? null,
1214
+ tenantId: input.tenantId ?? null,
1215
+ seq
1216
+ });
1217
+ } catch (error) {
1218
+ warnSignalPublishFailureOnce(db, input.table, error);
1219
+ }
1220
+ }
1221
+ } catch (error) {
1222
+ warnAppendFailureOnce(db, prepared[0].input.table, error);
1223
+ }
1224
+ }
1084
1225
  /** Record a framework-owned mutation that intentionally bypasses save hooks. */
1085
1226
  async function recordInstanceChange(instance, operation = "update") {
1086
1227
  await appendForInstance(instance, operation);
@@ -1121,6 +1262,6 @@ function resetChangeFeedWarnings() {
1121
1262
  deferredSignals.clear();
1122
1263
  }
1123
1264
  //#endregion
1124
- export { CHANGE_FEED_EXCLUDED_TABLES, CHANGE_FEED_INTERCEPTOR_NAME, CHANGE_FEED_TABLE, CHANGE_FEED_WAS_PERSISTED_KEY, DEFAULT_CHANGES_LIMIT, MAX_CHANGES_LIMIT, appendChange, bumpChangeFeed, drainChangeFeed, ensureChangeFeedTable, ensurePostgresChangeFeedAppendFunction, ensurePostgresChangeFeedHelpers, getChangesSince, getTableVersion, getTenantScopedChangesSince, isChangeFeedObservableTable, pruneChangeFeed, recordInstanceChange, registerChangeFeedWriter, resetChangeFeedWarnings, unregisterChangeFeedWriter };
1265
+ export { CHANGE_FEED_EXCLUDED_TABLES, CHANGE_FEED_INTERCEPTOR_NAME, CHANGE_FEED_TABLE, CHANGE_FEED_WAS_PERSISTED_KEY, DEFAULT_CHANGES_LIMIT, MAX_CHANGES_LIMIT, appendChange, appendChanges, bumpChangeFeed, drainChangeFeed, ensureChangeFeedTable, ensurePostgresChangeFeedAppendFunction, ensurePostgresChangeFeedHelpers, getChangesSince, getTableVersion, getTenantScopedChangesSince, isChangeFeedObservableTable, pruneChangeFeed, recordInstanceChange, recordInstanceChanges, registerChangeFeedWriter, resetChangeFeedWarnings, unregisterChangeFeedWriter };
1125
1266
 
1126
1267
  //# sourceMappingURL=change-feed.js.map