@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.
- package/README.md +4 -0
- package/agents/change-feed.md +19 -0
- package/agents/collection-reads.md +21 -0
- package/agents/generators.md +5 -0
- package/agents/memory.md +23 -3
- package/agents/null-equal-indexes.md +88 -0
- package/agents/object-runtime.md +85 -0
- package/agents/schema-paths.md +7 -7
- package/dist/browser.js +2 -2
- package/dist/cascade.d.ts.map +1 -1
- package/dist/cascade.js +13 -10
- package/dist/cascade.js.map +1 -1
- package/dist/change-feed.d.ts +22 -0
- package/dist/change-feed.d.ts.map +1 -1
- package/dist/change-feed.js +147 -6
- package/dist/change-feed.js.map +1 -1
- package/dist/class.js +1 -1
- package/dist/collection-cache.d.ts +10 -1
- package/dist/collection-cache.d.ts.map +1 -1
- package/dist/collection-cache.js +65 -10
- package/dist/collection-cache.js.map +1 -1
- package/dist/collection.d.ts +69 -2
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +142 -9
- package/dist/collection.js.map +1 -1
- package/dist/consumer-plugin/artifact-publication.d.ts +27 -0
- package/dist/consumer-plugin/artifact-publication.d.ts.map +1 -0
- package/dist/consumer-plugin/artifact-publication.js +80 -0
- package/dist/consumer-plugin/artifact-publication.js.map +1 -0
- package/dist/consumer-plugin/index.d.ts.map +1 -1
- package/dist/consumer-plugin/index.js +37 -4
- package/dist/consumer-plugin/index.js.map +1 -1
- package/dist/db-errors.js +1 -1
- package/dist/db-errors.js.map +1 -1
- package/dist/embedded-write-queue.d.ts +1 -1
- package/dist/embedded-write-queue.d.ts.map +1 -1
- package/dist/embedded-write-queue.js +16 -6
- package/dist/embedded-write-queue.js.map +1 -1
- package/dist/embeddings/storage.d.ts +16 -0
- package/dist/embeddings/storage.d.ts.map +1 -1
- package/dist/embeddings/storage.js +40 -0
- package/dist/embeddings/storage.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/interceptors.d.ts +22 -0
- package/dist/interceptors.d.ts.map +1 -1
- package/dist/interceptors.js +12 -0
- package/dist/interceptors.js.map +1 -1
- package/dist/junction.d.ts +12 -3
- package/dist/junction.d.ts.map +1 -1
- package/dist/junction.js +34 -4
- package/dist/junction.js.map +1 -1
- package/dist/knowledge-config.d.ts +6 -0
- package/dist/knowledge-config.d.ts.map +1 -0
- package/dist/knowledge-config.js +48 -0
- package/dist/knowledge-config.js.map +1 -0
- package/dist/manifest/static-manifest.d.ts.map +1 -1
- package/dist/manifest/static-manifest.js +91 -1
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.js +1 -1
- package/dist/manifest/store.js.map +1 -1
- package/dist/manifest.json +104 -1
- package/dist/migrations/differ.d.ts.map +1 -1
- package/dist/migrations/differ.js +2 -0
- package/dist/migrations/differ.js.map +1 -1
- package/dist/migrations/generator.d.ts.map +1 -1
- package/dist/migrations/generator.js +2 -0
- package/dist/migrations/generator.js.map +1 -1
- package/dist/migrations/index.d.ts +1 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +2 -1
- package/dist/migrations/null-equal-indexes.d.ts +37 -0
- package/dist/migrations/null-equal-indexes.d.ts.map +1 -0
- package/dist/migrations/null-equal-indexes.js +133 -0
- package/dist/migrations/null-equal-indexes.js.map +1 -0
- package/dist/migrations/tracker.d.ts.map +1 -1
- package/dist/migrations/tracker.js +6 -0
- package/dist/migrations/tracker.js.map +1 -1
- package/dist/migrations.js +2 -1
- package/dist/object.d.ts +33 -0
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +316 -46
- package/dist/object.js.map +1 -1
- package/dist/plain-json.d.ts +6 -0
- package/dist/plain-json.d.ts.map +1 -0
- package/dist/plain-json.js +41 -0
- package/dist/plain-json.js.map +1 -0
- package/dist/postgres-permissions.d.ts.map +1 -1
- package/dist/postgres-permissions.js +6 -2
- package/dist/postgres-permissions.js.map +1 -1
- package/dist/prebuild/index.d.ts.map +1 -1
- package/dist/prebuild/index.js +3 -3
- package/dist/prebuild/index.js.map +1 -1
- package/dist/registry/class-registration.d.ts.map +1 -1
- package/dist/registry/class-registration.js +4 -1
- package/dist/registry/class-registration.js.map +1 -1
- package/dist/registry/schema-builder.d.ts.map +1 -1
- package/dist/registry/schema-builder.js +4 -3
- package/dist/registry/schema-builder.js.map +1 -1
- package/dist/registry/validator.d.ts +1 -0
- package/dist/registry/validator.d.ts.map +1 -1
- package/dist/registry/validator.js +8 -1
- package/dist/registry/validator.js.map +1 -1
- package/dist/registry.js +1 -1
- package/dist/scanner/manifest-generator.d.ts.map +1 -1
- package/dist/scanner/manifest-generator.js +21 -0
- package/dist/scanner/manifest-generator.js.map +1 -1
- package/dist/scanner/types.d.ts +2 -0
- package/dist/scanner/types.d.ts.map +1 -1
- package/dist/scanner/types.js.map +1 -1
- package/dist/schema/conflict-target.d.ts +8 -2
- package/dist/schema/conflict-target.d.ts.map +1 -1
- package/dist/schema/conflict-target.js +11 -3
- package/dist/schema/conflict-target.js.map +1 -1
- package/dist/schema/ddl/null-equal-index.d.ts +5 -0
- package/dist/schema/ddl/null-equal-index.d.ts.map +1 -0
- package/dist/schema/ddl/null-equal-index.js +17 -0
- package/dist/schema/ddl/null-equal-index.js.map +1 -0
- package/dist/schema/ddl/postgres-strategy.d.ts +2 -1
- package/dist/schema/ddl/postgres-strategy.d.ts.map +1 -1
- package/dist/schema/ddl/postgres-strategy.js +7 -0
- package/dist/schema/ddl/postgres-strategy.js.map +1 -1
- package/dist/schema/generator.d.ts.map +1 -1
- package/dist/schema/generator.js +8 -3
- package/dist/schema/generator.js.map +1 -1
- package/dist/schema/manifest-schema.d.ts +2 -1
- package/dist/schema/manifest-schema.d.ts.map +1 -1
- package/dist/schema/manifest-schema.js +12 -4
- package/dist/schema/manifest-schema.js.map +1 -1
- package/dist/schema/schema-manager.d.ts.map +1 -1
- package/dist/schema/schema-manager.js +1 -1
- package/dist/schema/schema-manager.js.map +1 -1
- package/dist/schema/types.d.ts +2 -0
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/scripts/browser-plain-json-regression.mjs +85 -0
- package/dist/smrt-knowledge.json +73 -15
- package/dist/system/schema.d.ts +14 -3
- package/dist/system/schema.d.ts.map +1 -1
- package/dist/system/schema.js +120 -2
- package/dist/system/schema.js.map +1 -1
- package/dist/vite-plugin/api-client-entries.d.ts.map +1 -1
- package/dist/vite-plugin/api-client-entries.js +9 -7
- package/dist/vite-plugin/api-client-entries.js.map +1 -1
- package/dist/vite-plugin/index.d.ts +24 -0
- package/dist/vite-plugin/index.d.ts.map +1 -1
- package/dist/vite-plugin/index.js +47 -64
- package/dist/vite-plugin/index.js.map +1 -1
- package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
- package/dist/vite-plugin/sveltekit-generator.js +2 -2
- package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
- package/dist/vite-plugin/web-collections.d.ts +10 -0
- package/dist/vite-plugin/web-collections.d.ts.map +1 -1
- package/dist/vite-plugin/web-collections.js +14 -2
- package/dist/vite-plugin/web-collections.js.map +1 -1
- package/package.json +5 -4
package/dist/change-feed.d.ts
CHANGED
|
@@ -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":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkIG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAS5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"change-feed.d.ts","sourceRoot":"","sources":["../src/change-feed.ts"],"names":[],"mappings":"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"}
|
package/dist/change-feed.js
CHANGED
|
@@ -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
|
-
|
|
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
|