@happyvertical/smrt-core 0.49.3 → 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/collection-cache.js
CHANGED
|
@@ -14,8 +14,11 @@ import { createLogger } from "@happyvertical/logger";
|
|
|
14
14
|
* - SMRT owns every mutation path (`save()`/`delete()` back
|
|
15
15
|
* `collection.create()`, `getOrUpsert()`, junction attach/detach), so all
|
|
16
16
|
* writes invalidate the affected table's entries in-process automatically.
|
|
17
|
-
* - Entries
|
|
18
|
-
*
|
|
17
|
+
* - Entries and flights are partitioned by the concrete DatabaseInterface
|
|
18
|
+
* object, final SQL and parameters. Distinct transaction/connection handles
|
|
19
|
+
* never share rows or failures, even when their database URL is identical.
|
|
20
|
+
* - Invalidation remains URL/table scoped, so a write clears every handle's
|
|
21
|
+
* entries and STI siblings (which share a table) stay coherent.
|
|
19
22
|
* - Caches are per-process. With multiple replicas, a local invalidation
|
|
20
23
|
* leaves peers stale until TTL unless cross-process invalidation is opted
|
|
21
24
|
* into (`crossProcess: true`), which broadcasts over the database
|
|
@@ -67,6 +70,51 @@ var PROCESS_ID = crypto.randomUUID();
|
|
|
67
70
|
*/
|
|
68
71
|
var store = /* @__PURE__ */ new Map();
|
|
69
72
|
/**
|
|
73
|
+
* Reads currently populating a cache entry, scoped the same way as cached
|
|
74
|
+
* rows. A shared promise prevents concurrent identical cache misses from
|
|
75
|
+
* exhausting the connection pool with duplicate SELECTs.
|
|
76
|
+
*/
|
|
77
|
+
var inFlightReads = /* @__PURE__ */ new Map();
|
|
78
|
+
/**
|
|
79
|
+
* Share an in-progress cache miss with callers for the same database, table,
|
|
80
|
+
* and final query key. Rejected reads are removed too, so a later caller can
|
|
81
|
+
* retry rather than inheriting a permanently failed promise.
|
|
82
|
+
*/
|
|
83
|
+
function getOrCreateInFlightRead(dbKey, tableName, queryKey, generation, read) {
|
|
84
|
+
let tables = inFlightReads.get(dbKey);
|
|
85
|
+
if (!tables) {
|
|
86
|
+
tables = /* @__PURE__ */ new Map();
|
|
87
|
+
inFlightReads.set(dbKey, tables);
|
|
88
|
+
}
|
|
89
|
+
let entries = tables.get(tableName);
|
|
90
|
+
if (!entries) {
|
|
91
|
+
entries = /* @__PURE__ */ new Map();
|
|
92
|
+
tables.set(tableName, entries);
|
|
93
|
+
}
|
|
94
|
+
let generations = entries.get(queryKey);
|
|
95
|
+
if (!generations) {
|
|
96
|
+
generations = /* @__PURE__ */ new Map();
|
|
97
|
+
entries.set(queryKey, generations);
|
|
98
|
+
}
|
|
99
|
+
const existing = generations.get(generation);
|
|
100
|
+
if (existing) return existing;
|
|
101
|
+
let inFlight;
|
|
102
|
+
inFlight = read().finally(() => {
|
|
103
|
+
if (generations.get(generation) === inFlight) {
|
|
104
|
+
generations.delete(generation);
|
|
105
|
+
if (generations.size === 0 && entries.get(queryKey) === generations) {
|
|
106
|
+
entries.delete(queryKey);
|
|
107
|
+
if (entries.size === 0 && tables.get(tableName) === entries) {
|
|
108
|
+
tables.delete(tableName);
|
|
109
|
+
if (tables.size === 0 && inFlightReads.get(dbKey) === tables) inFlightReads.delete(dbKey);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
generations.set(generation, inFlight);
|
|
115
|
+
return inFlight;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
70
118
|
* Monotonic invalidation generation per `dbKey\0tableName`, bumped on every
|
|
71
119
|
* invalidation. A read captures the generation *before* its DB round-trip and
|
|
72
120
|
* passes it to `setCachedRows`; if an invalidating write landed during the
|
|
@@ -87,10 +135,10 @@ function getCacheGeneration(dbKey, tableName) {
|
|
|
87
135
|
return generations.get(generationKey(dbKey, tableName)) ?? 0;
|
|
88
136
|
}
|
|
89
137
|
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
138
|
+
* Concrete executor identities. URL equality does not imply transaction or
|
|
139
|
+
* snapshot equality; each public database-interface object has its own scope.
|
|
92
140
|
*/
|
|
93
|
-
var
|
|
141
|
+
var dbInstanceKeys = /* @__PURE__ */ new WeakMap();
|
|
94
142
|
/**
|
|
95
143
|
* Resolve a stable cache scope for a database instance.
|
|
96
144
|
*
|
|
@@ -104,10 +152,13 @@ function resolveDbCacheKey(db) {
|
|
|
104
152
|
const dbWithConfig = db;
|
|
105
153
|
const url = db.url || dbWithConfig.config?.url;
|
|
106
154
|
if (url && url !== ":memory:") return url;
|
|
107
|
-
|
|
155
|
+
return resolveDbInstanceKey(db);
|
|
156
|
+
}
|
|
157
|
+
function resolveDbInstanceKey(db) {
|
|
158
|
+
let key = dbInstanceKeys.get(db);
|
|
108
159
|
if (!key) {
|
|
109
160
|
key = `smrt-db:${crypto.randomUUID()}`;
|
|
110
|
-
|
|
161
|
+
dbInstanceKeys.set(db, key);
|
|
111
162
|
}
|
|
112
163
|
return key;
|
|
113
164
|
}
|
|
@@ -115,9 +166,12 @@ function resolveDbCacheKey(db) {
|
|
|
115
166
|
* Build the cache key for a query. The final SQL and bound parameters fully
|
|
116
167
|
* normalize the query shape — they already include STI discriminator
|
|
117
168
|
* filters, interceptor-injected tenant filters, ORDER BY, LIMIT and OFFSET.
|
|
169
|
+
* Collection reads also supply their concrete executor so neither pending
|
|
170
|
+
* reads nor completed rows cross connection/transaction boundaries. The
|
|
171
|
+
* surrounding URL/table scope remains shared for invalidation generations.
|
|
118
172
|
*/
|
|
119
|
-
function buildQueryCacheKey(sql, params) {
|
|
120
|
-
return `${sql}\0${JSON.stringify(params)}`;
|
|
173
|
+
function buildQueryCacheKey(sql, params, db) {
|
|
174
|
+
return `${db ? `${resolveDbInstanceKey(db)}\0` : ""}${sql}\0${JSON.stringify(params)}`;
|
|
121
175
|
}
|
|
122
176
|
/**
|
|
123
177
|
* Read cached rows for a query, or undefined on miss/expiry.
|
|
@@ -180,6 +234,7 @@ function invalidateCollectionCache(dbKey, tableName) {
|
|
|
180
234
|
*/
|
|
181
235
|
function resetCollectionCache() {
|
|
182
236
|
store.clear();
|
|
237
|
+
inFlightReads.clear();
|
|
183
238
|
generations.clear();
|
|
184
239
|
crossProcessInterest.clear();
|
|
185
240
|
stopCacheInvalidationListeners();
|
|
@@ -308,6 +363,6 @@ function safeParse(value) {
|
|
|
308
363
|
}
|
|
309
364
|
}
|
|
310
365
|
//#endregion
|
|
311
|
-
export { CACHE_INVALIDATION_CHANNEL, PROCESS_ID, broadcastCacheInvalidation, buildQueryCacheKey, ensureCacheInvalidationListener, getCacheGeneration, getCachedRows, getNotifications, hasCrossProcessCacheInterest, invalidateCollectionCache, registerCrossProcessCacheInterest, resetCollectionCache, resolveDbCacheKey, setCachedRows, stopCacheInvalidationListeners };
|
|
366
|
+
export { CACHE_INVALIDATION_CHANNEL, PROCESS_ID, broadcastCacheInvalidation, buildQueryCacheKey, ensureCacheInvalidationListener, getCacheGeneration, getCachedRows, getNotifications, getOrCreateInFlightRead, hasCrossProcessCacheInterest, invalidateCollectionCache, registerCrossProcessCacheInterest, resetCollectionCache, resolveDbCacheKey, setCachedRows, stopCacheInvalidationListeners };
|
|
312
367
|
|
|
313
368
|
//# sourceMappingURL=collection-cache.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collection-cache.js","names":[],"sources":["../src/collection-cache.ts"],"sourcesContent":["/**\n * Opt-in read-through cache for collection reads (issue #1498).\n *\n * SSR-rendered SMRT apps re-query read-heavy / write-rare collections on\n * every request, and the per-query round-trip dominates wall time when a\n * page needs many collections. This module memoizes `list()`/`get()` row\n * sets keyed by the final SQL + parameters, for an opt-in TTL.\n *\n * Correctness model:\n * - Caching is OFF by default. It is enabled per call\n * (`list({ cache: { ttl } })`) or per model (`@smrt({ cache: { ttl } })`).\n * - SMRT owns every mutation path (`save()`/`delete()` back\n * `collection.create()`, `getOrUpsert()`, junction attach/detach), so all\n * writes invalidate the affected table's entries in-process automatically.\n * - Entries are scoped per database identity (`db.url`) and per table, so\n * multi-DB processes and STI siblings (which share a table) stay coherent.\n * - Caches are per-process. With multiple replicas, a local invalidation\n * leaves peers stale until TTL unless cross-process invalidation is opted\n * into (`crossProcess: true`), which broadcasts over the database\n * adapter's notification capability (e.g. Postgres LISTEN/NOTIFY) when\n * the adapter provides one.\n *\n * Cached values are raw result rows, not hydrated instances — hydration and\n * read interceptors (tenancy, audit) run on every call, cached or not, and\n * each caller receives isolated row copies.\n *\n * Known limitations:\n * - Invalidation fires when a mutation's SQL executes, not when its\n * surrounding transaction commits. A write inside an uncommitted\n * transaction invalidates (and may broadcast) immediately; a concurrent\n * reader could repopulate the cache from the pre-commit snapshot, and a\n * rollback leaves the cache invalidated for a write that never landed.\n * Caching targets read-heavy / write-rare data where this is rare; for\n * models mutated inside multi-statement transactions, coherence is still\n * bounded by TTL. Per-call `cache: false` forces a fresh read where it\n * matters.\n * - Writes that bypass the framework's mutation paths (raw `db.query`\n * issued outside `collection.query()`, external processes without\n * `crossProcess`) are only bounded by TTL.\n *\n * @see https://github.com/happyvertical/smrt/issues/1498\n * @packageDocumentation\n */\n\nimport { createLogger } from '@happyvertical/logger';\nimport type { DatabaseInterface } from '@happyvertical/sql';\n\nconst logger = createLogger({ level: 'info' });\n\n/**\n * Read-through cache configuration for collection reads.\n *\n * Used both per call (`collection.list({ cache: { ttl: 60_000 } })`) and per\n * model (`@smrt({ cache: { ttl: 60_000 } })`).\n */\nexport interface CollectionCacheConfig {\n /**\n * Time-to-live for cached query results, in milliseconds. Must be > 0.\n */\n ttl: number;\n\n /**\n * Broadcast invalidations to peer processes through the database\n * adapter's notification capability (`db.notifications`, e.g. Postgres\n * LISTEN/NOTIFY). Without this, peer replicas serve stale rows until TTL.\n *\n * Model-level config (`@smrt({ cache })`) is the reliable opt-in: every\n * process that writes the model knows to broadcast. As a per-call option,\n * writes broadcast only from processes that have already performed a\n * `crossProcess` cached read of the same table (typical for homogeneous\n * replicas running the same routes); a process that only writes never\n * learns about the per-call opt-in, so its peers fall back to TTL expiry.\n *\n * Ignored (with a one-time warning) when the adapter does not expose\n * notifications.\n */\n crossProcess?: boolean;\n}\n\ninterface CacheEntry {\n expiresAt: number;\n rows: Record<string, unknown>[];\n}\n\n/**\n * Per-table entry cap. A table accumulating more distinct query shapes than\n * this within one TTL window evicts its oldest entries (insertion order).\n * Guards against unbounded growth from high-cardinality WHERE values.\n */\nconst MAX_ENTRIES_PER_TABLE = 500;\n\n/**\n * Notification channel used for cross-process invalidation broadcasts.\n */\nexport const CACHE_INVALIDATION_CHANNEL = 'smrt_collection_cache';\n\n/**\n * Identifies this process in broadcast payloads so a replica can skip\n * notifications it published itself (it already invalidated locally).\n *\n * Exported so sibling cross-replica buses (the #1763 change-signal bus) share\n * one per-process identity, keeping echo-avoidance consistent across channels.\n */\nexport const PROCESS_ID = crypto.randomUUID();\n\n/**\n * dbKey → tableName → (queryKey → entry)\n */\nconst store = new Map<string, Map<string, Map<string, CacheEntry>>>();\n\n/**\n * Monotonic invalidation generation per `dbKey\\0tableName`, bumped on every\n * invalidation. A read captures the generation *before* its DB round-trip and\n * passes it to `setCachedRows`; if an invalidating write landed during the\n * round-trip, the generation no longer matches and the (now-stale) result is\n * dropped instead of cached. This closes the read-miss/concurrent-write race\n * where an in-flight SELECT would otherwise repopulate the cache with\n * pre-write rows for the full TTL.\n */\nconst generations = new Map<string, number>();\n\nfunction generationKey(dbKey: string, tableName: string): string {\n return `${dbKey}\\0${tableName}`;\n}\n\n/**\n * Current invalidation generation for a table (0 if never invalidated).\n * Capture this before a DB read and pass it to `setCachedRows`.\n */\nexport function getCacheGeneration(dbKey: string, tableName: string): number {\n return generations.get(generationKey(dbKey, tableName)) ?? 0;\n}\n\n/**\n * Fallback identities for database instances that expose no URL\n * (each such instance gets its own scope, never shared).\n */\nconst fallbackDbKeys = new WeakMap<object, string>();\n\n/**\n * Resolve a stable cache scope for a database instance.\n *\n * Mirrors table-verifier's identity resolution (`db.url || config.url`).\n * `:memory:` databases share that URL string while being entirely separate\n * databases, so they (and URL-less instances) are scoped per instance —\n * serving one in-memory database's rows for another would be a correctness\n * bug, not just a stale read.\n */\nexport function resolveDbCacheKey(db: DatabaseInterface): string {\n const dbWithConfig = db as DatabaseInterface & {\n config?: { url?: string };\n };\n const url = db.url || dbWithConfig.config?.url;\n if (url && url !== ':memory:') return url;\n\n let key = fallbackDbKeys.get(db);\n if (!key) {\n key = `smrt-db:${crypto.randomUUID()}`;\n fallbackDbKeys.set(db, key);\n }\n return key;\n}\n\n/**\n * Build the cache key for a query. The final SQL and bound parameters fully\n * normalize the query shape — they already include STI discriminator\n * filters, interceptor-injected tenant filters, ORDER BY, LIMIT and OFFSET.\n */\nexport function buildQueryCacheKey(sql: string, params: unknown[]): string {\n return `${sql}\\0${JSON.stringify(params)}`;\n}\n\n/**\n * Read cached rows for a query, or undefined on miss/expiry.\n *\n * Returns a structured clone so callers can never mutate the cached copy\n * (hydration writes into row objects).\n */\nexport function getCachedRows(\n dbKey: string,\n tableName: string,\n queryKey: string,\n): Record<string, unknown>[] | undefined {\n const entry = store.get(dbKey)?.get(tableName)?.get(queryKey);\n if (!entry) return undefined;\n if (entry.expiresAt <= Date.now()) {\n store.get(dbKey)?.get(tableName)?.delete(queryKey);\n return undefined;\n }\n return structuredClone(entry.rows);\n}\n\n/**\n * Store rows for a query under the table's cache scope.\n *\n * `expectedGeneration` is the value {@link getCacheGeneration} returned before\n * the DB read. If an invalidation bumped the table's generation while the read\n * was in flight, the result is stale and is dropped rather than cached.\n */\nexport function setCachedRows(\n dbKey: string,\n tableName: string,\n queryKey: string,\n rows: Record<string, unknown>[],\n ttl: number,\n expectedGeneration?: number,\n): void {\n if (!(ttl > 0)) return;\n\n // Drop a result that a concurrent write invalidated mid-flight.\n if (\n expectedGeneration !== undefined &&\n getCacheGeneration(dbKey, tableName) !== expectedGeneration\n ) {\n return;\n }\n\n let tables = store.get(dbKey);\n if (!tables) {\n tables = new Map();\n store.set(dbKey, tables);\n }\n let entries = tables.get(tableName);\n if (!entries) {\n entries = new Map();\n tables.set(tableName, entries);\n }\n\n // Evict oldest entries (Map preserves insertion order) at the cap. Skip when\n // refreshing an existing key — `set` updates it in place without growing the\n // map, so evicting first would drop a live entry and shrink the effective cap.\n if (!entries.has(queryKey)) {\n while (entries.size >= MAX_ENTRIES_PER_TABLE) {\n const oldest = entries.keys().next().value;\n if (oldest === undefined) break;\n entries.delete(oldest);\n }\n }\n\n entries.set(queryKey, {\n expiresAt: Date.now() + ttl,\n rows: structuredClone(rows),\n });\n}\n\n/**\n * Drop every cached entry for a table. Called by the framework after any\n * successful mutation against that table — this is the write-invalidation\n * guarantee an app-side cache can't make.\n */\nexport function invalidateCollectionCache(\n dbKey: string,\n tableName: string,\n): void {\n store.get(dbKey)?.delete(tableName);\n // Bump the generation so any read whose DB round-trip is still in flight\n // discards its (now-stale) result instead of repopulating the table.\n const gkey = generationKey(dbKey, tableName);\n generations.set(gkey, (generations.get(gkey) ?? 0) + 1);\n}\n\n/**\n * Clear all cached collection reads and stop cross-process listeners.\n * Call in test setup to ensure isolation between test files.\n */\nexport function resetCollectionCache(): void {\n store.clear();\n generations.clear();\n crossProcessInterest.clear();\n stopCacheInvalidationListeners();\n}\n\n// ============================================================================\n// Per-call crossProcess interest (write-side broadcast decision)\n// ============================================================================\n\n/**\n * dbKey → tables this process has cached with a per-call `crossProcess`\n * opt-in. Writes consult this so per-call usage broadcasts too — model-level\n * config can't be the only trigger when the opt-in lives at the call site.\n */\nconst crossProcessInterest = new Map<string, Set<string>>();\n\n/**\n * Record that a per-call `crossProcess` cached read happened for a table.\n */\nexport function registerCrossProcessCacheInterest(\n dbKey: string,\n tableName: string,\n): void {\n let tables = crossProcessInterest.get(dbKey);\n if (!tables) {\n tables = new Set();\n crossProcessInterest.set(dbKey, tables);\n }\n tables.add(tableName);\n}\n\n/**\n * Whether any per-call `crossProcess` cached read has touched this table.\n */\nexport function hasCrossProcessCacheInterest(\n dbKey: string,\n tableName: string,\n): boolean {\n return crossProcessInterest.get(dbKey)?.has(tableName) ?? false;\n}\n\n// ============================================================================\n// Cross-process invalidation (opt-in, via db.notifications)\n// ============================================================================\n\ninterface ListenerHandle {\n iterator: AsyncIterator<unknown> | null;\n stopped: boolean;\n}\n\nconst listeners = new Map<string, ListenerHandle>();\nconst warnedNoNotifications = new Set<string>();\n\n/**\n * The database adapter's optional notification capability (e.g. Postgres\n * LISTEN/NOTIFY), or `undefined` when the adapter exposes none. Duck-typed so\n * core never depends on a concrete adapter shape. Exported so sibling\n * cross-replica buses (the #1763 change-signal bus) resolve it identically.\n */\nexport function getNotifications(db: DatabaseInterface) {\n return (db as DatabaseInterface & { notifications?: unknown })\n .notifications as\n | {\n notify(channel: string, payload: unknown): Promise<number>;\n listen(\n channel: string,\n options?: Record<string, unknown>,\n ): AsyncIterable<{ channel: string; payload: unknown }>;\n }\n | undefined;\n}\n\nfunction warnOnceNoNotifications(dbKey: string, context: string): void {\n if (warnedNoNotifications.has(dbKey)) return;\n warnedNoNotifications.add(dbKey);\n logger.warn(\n `Collection cache: crossProcess invalidation requested but the database ` +\n `adapter exposes no notification capability (${context}). Peer ` +\n `replicas will serve stale rows until TTL.`,\n );\n}\n\n/**\n * Broadcast a table invalidation to peer processes.\n *\n * Fire-and-forget from mutation paths: a broadcast failure must never fail\n * the write that triggered it.\n */\nexport async function broadcastCacheInvalidation(\n db: DatabaseInterface,\n tableName: string,\n): Promise<void> {\n const notifications = getNotifications(db);\n const dbKey = resolveDbCacheKey(db);\n if (!notifications) {\n warnOnceNoNotifications(dbKey, 'broadcast');\n return;\n }\n try {\n await notifications.notify(CACHE_INVALIDATION_CHANNEL, {\n table: tableName,\n source: PROCESS_ID,\n });\n } catch (error) {\n logger.warn(\n `Collection cache: failed to broadcast invalidation for ${tableName}`,\n { error: error instanceof Error ? error.message : error },\n );\n }\n}\n\n/**\n * Ensure a background listener consumes invalidation broadcasts for this\n * database and drops matching local cache entries.\n *\n * Started lazily by the first cached read that opted into `crossProcess`.\n * Notifications published by this process are skipped — the local\n * invalidation already happened synchronously on the write path.\n */\nexport function ensureCacheInvalidationListener(db: DatabaseInterface): void {\n const dbKey = resolveDbCacheKey(db);\n if (listeners.has(dbKey)) return;\n\n const notifications = getNotifications(db);\n if (!notifications) {\n warnOnceNoNotifications(dbKey, 'listen');\n return;\n }\n\n const handle: ListenerHandle = { iterator: null, stopped: false };\n listeners.set(dbKey, handle);\n\n void (async () => {\n try {\n const iterable = notifications.listen(CACHE_INVALIDATION_CHANNEL);\n const iterator = iterable[Symbol.asyncIterator]();\n handle.iterator = iterator;\n\n while (!handle.stopped) {\n const { value, done } = await iterator.next();\n if (done || handle.stopped) break;\n\n const notification = value as { payload?: unknown };\n const payload: unknown =\n typeof notification?.payload === 'string'\n ? safeParse(notification.payload)\n : notification?.payload;\n\n if (!payload || typeof payload !== 'object') continue;\n const record = payload as { table?: unknown; source?: unknown };\n if (typeof record.table !== 'string') continue;\n if (record.source === PROCESS_ID) continue;\n\n invalidateCollectionCache(dbKey, record.table);\n }\n } catch (error) {\n if (!handle.stopped) {\n logger.warn(\n 'Collection cache: invalidation listener terminated unexpectedly; ' +\n 'cross-process invalidation is inactive for this database ' +\n '(local TTL still bounds staleness)',\n { error: error instanceof Error ? error.message : error },\n );\n }\n } finally {\n // Only retract our own handle. A concurrent stop+restart for the same\n // dbKey may have already installed a replacement; deleting unconditionally\n // would orphan it and leak/duplicate the live listener.\n if (listeners.get(dbKey) === handle) {\n listeners.delete(dbKey);\n }\n }\n })();\n}\n\n/**\n * Stop all cross-process invalidation listeners. Used by tests and during\n * shutdown; safe to call when none are active.\n */\nexport function stopCacheInvalidationListeners(): void {\n for (const handle of listeners.values()) {\n handle.stopped = true;\n void handle.iterator?.return?.(undefined);\n }\n listeners.clear();\n warnedNoNotifications.clear();\n}\n\nfunction safeParse(value: string): unknown {\n try {\n return JSON.parse(value);\n } catch {\n return undefined;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"collection-cache.js","names":[],"sources":["../src/collection-cache.ts"],"sourcesContent":["/**\n * Opt-in read-through cache for collection reads (issue #1498).\n *\n * SSR-rendered SMRT apps re-query read-heavy / write-rare collections on\n * every request, and the per-query round-trip dominates wall time when a\n * page needs many collections. This module memoizes `list()`/`get()` row\n * sets keyed by the final SQL + parameters, for an opt-in TTL.\n *\n * Correctness model:\n * - Caching is OFF by default. It is enabled per call\n * (`list({ cache: { ttl } })`) or per model (`@smrt({ cache: { ttl } })`).\n * - SMRT owns every mutation path (`save()`/`delete()` back\n * `collection.create()`, `getOrUpsert()`, junction attach/detach), so all\n * writes invalidate the affected table's entries in-process automatically.\n * - Entries and flights are partitioned by the concrete DatabaseInterface\n * object, final SQL and parameters. Distinct transaction/connection handles\n * never share rows or failures, even when their database URL is identical.\n * - Invalidation remains URL/table scoped, so a write clears every handle's\n * entries and STI siblings (which share a table) stay coherent.\n * - Caches are per-process. With multiple replicas, a local invalidation\n * leaves peers stale until TTL unless cross-process invalidation is opted\n * into (`crossProcess: true`), which broadcasts over the database\n * adapter's notification capability (e.g. Postgres LISTEN/NOTIFY) when\n * the adapter provides one.\n *\n * Cached values are raw result rows, not hydrated instances — hydration and\n * read interceptors (tenancy, audit) run on every call, cached or not, and\n * each caller receives isolated row copies.\n *\n * Known limitations:\n * - Invalidation fires when a mutation's SQL executes, not when its\n * surrounding transaction commits. A write inside an uncommitted\n * transaction invalidates (and may broadcast) immediately; a concurrent\n * reader could repopulate the cache from the pre-commit snapshot, and a\n * rollback leaves the cache invalidated for a write that never landed.\n * Caching targets read-heavy / write-rare data where this is rare; for\n * models mutated inside multi-statement transactions, coherence is still\n * bounded by TTL. Per-call `cache: false` forces a fresh read where it\n * matters.\n * - Writes that bypass the framework's mutation paths (raw `db.query`\n * issued outside `collection.query()`, external processes without\n * `crossProcess`) are only bounded by TTL.\n *\n * @see https://github.com/happyvertical/smrt/issues/1498\n * @packageDocumentation\n */\n\nimport { createLogger } from '@happyvertical/logger';\nimport type { DatabaseInterface } from '@happyvertical/sql';\n\nconst logger = createLogger({ level: 'info' });\n\n/**\n * Read-through cache configuration for collection reads.\n *\n * Used both per call (`collection.list({ cache: { ttl: 60_000 } })`) and per\n * model (`@smrt({ cache: { ttl: 60_000 } })`).\n */\nexport interface CollectionCacheConfig {\n /**\n * Time-to-live for cached query results, in milliseconds. Must be > 0.\n */\n ttl: number;\n\n /**\n * Broadcast invalidations to peer processes through the database\n * adapter's notification capability (`db.notifications`, e.g. Postgres\n * LISTEN/NOTIFY). Without this, peer replicas serve stale rows until TTL.\n *\n * Model-level config (`@smrt({ cache })`) is the reliable opt-in: every\n * process that writes the model knows to broadcast. As a per-call option,\n * writes broadcast only from processes that have already performed a\n * `crossProcess` cached read of the same table (typical for homogeneous\n * replicas running the same routes); a process that only writes never\n * learns about the per-call opt-in, so its peers fall back to TTL expiry.\n *\n * Ignored (with a one-time warning) when the adapter does not expose\n * notifications.\n */\n crossProcess?: boolean;\n}\n\ninterface CacheEntry {\n expiresAt: number;\n rows: Record<string, unknown>[];\n}\n\n/**\n * Per-table entry cap. A table accumulating more distinct query shapes than\n * this within one TTL window evicts its oldest entries (insertion order).\n * Guards against unbounded growth from high-cardinality WHERE values.\n */\nconst MAX_ENTRIES_PER_TABLE = 500;\n\n/**\n * Notification channel used for cross-process invalidation broadcasts.\n */\nexport const CACHE_INVALIDATION_CHANNEL = 'smrt_collection_cache';\n\n/**\n * Identifies this process in broadcast payloads so a replica can skip\n * notifications it published itself (it already invalidated locally).\n *\n * Exported so sibling cross-replica buses (the #1763 change-signal bus) share\n * one per-process identity, keeping echo-avoidance consistent across channels.\n */\nexport const PROCESS_ID = crypto.randomUUID();\n\n/**\n * dbKey → tableName → (queryKey → entry)\n */\nconst store = new Map<string, Map<string, Map<string, CacheEntry>>>();\n\n/**\n * Reads currently populating a cache entry, scoped the same way as cached\n * rows. A shared promise prevents concurrent identical cache misses from\n * exhausting the connection pool with duplicate SELECTs.\n */\nconst inFlightReads = new Map<\n string,\n Map<string, Map<string, Map<number, Promise<Record<string, unknown>[]>>>>\n>();\n\n/**\n * Share an in-progress cache miss with callers for the same database, table,\n * and final query key. Rejected reads are removed too, so a later caller can\n * retry rather than inheriting a permanently failed promise.\n */\nexport function getOrCreateInFlightRead(\n dbKey: string,\n tableName: string,\n queryKey: string,\n generation: number,\n read: () => Promise<Record<string, unknown>[]>,\n): Promise<Record<string, unknown>[]> {\n let tables = inFlightReads.get(dbKey);\n if (!tables) {\n tables = new Map();\n inFlightReads.set(dbKey, tables);\n }\n let entries = tables.get(tableName);\n if (!entries) {\n entries = new Map();\n tables.set(tableName, entries);\n }\n\n let generations = entries.get(queryKey);\n if (!generations) {\n generations = new Map();\n entries.set(queryKey, generations);\n }\n const existing = generations.get(generation);\n if (existing) return existing;\n\n let inFlight: Promise<Record<string, unknown>[]>;\n inFlight = read().finally(() => {\n if (generations.get(generation) === inFlight) {\n generations.delete(generation);\n if (generations.size === 0 && entries.get(queryKey) === generations) {\n entries.delete(queryKey);\n if (entries.size === 0 && tables.get(tableName) === entries) {\n tables.delete(tableName);\n if (tables.size === 0 && inFlightReads.get(dbKey) === tables) {\n inFlightReads.delete(dbKey);\n }\n }\n }\n }\n });\n generations.set(generation, inFlight);\n return inFlight;\n}\n\n/**\n * Monotonic invalidation generation per `dbKey\\0tableName`, bumped on every\n * invalidation. A read captures the generation *before* its DB round-trip and\n * passes it to `setCachedRows`; if an invalidating write landed during the\n * round-trip, the generation no longer matches and the (now-stale) result is\n * dropped instead of cached. This closes the read-miss/concurrent-write race\n * where an in-flight SELECT would otherwise repopulate the cache with\n * pre-write rows for the full TTL.\n */\nconst generations = new Map<string, number>();\n\nfunction generationKey(dbKey: string, tableName: string): string {\n return `${dbKey}\\0${tableName}`;\n}\n\n/**\n * Current invalidation generation for a table (0 if never invalidated).\n * Capture this before a DB read and pass it to `setCachedRows`.\n */\nexport function getCacheGeneration(dbKey: string, tableName: string): number {\n return generations.get(generationKey(dbKey, tableName)) ?? 0;\n}\n\n/**\n * Concrete executor identities. URL equality does not imply transaction or\n * snapshot equality; each public database-interface object has its own scope.\n */\nconst dbInstanceKeys = new WeakMap<object, string>();\n\n/**\n * Resolve a stable cache scope for a database instance.\n *\n * Mirrors table-verifier's identity resolution (`db.url || config.url`).\n * `:memory:` databases share that URL string while being entirely separate\n * databases, so they (and URL-less instances) are scoped per instance —\n * serving one in-memory database's rows for another would be a correctness\n * bug, not just a stale read.\n */\nexport function resolveDbCacheKey(db: DatabaseInterface): string {\n const dbWithConfig = db as DatabaseInterface & {\n config?: { url?: string };\n };\n const url = db.url || dbWithConfig.config?.url;\n if (url && url !== ':memory:') return url;\n\n return resolveDbInstanceKey(db);\n}\n\nfunction resolveDbInstanceKey(db: DatabaseInterface): string {\n let key = dbInstanceKeys.get(db);\n if (!key) {\n key = `smrt-db:${crypto.randomUUID()}`;\n dbInstanceKeys.set(db, key);\n }\n return key;\n}\n\n/**\n * Build the cache key for a query. The final SQL and bound parameters fully\n * normalize the query shape — they already include STI discriminator\n * filters, interceptor-injected tenant filters, ORDER BY, LIMIT and OFFSET.\n * Collection reads also supply their concrete executor so neither pending\n * reads nor completed rows cross connection/transaction boundaries. The\n * surrounding URL/table scope remains shared for invalidation generations.\n */\nexport function buildQueryCacheKey(\n sql: string,\n params: unknown[],\n db?: DatabaseInterface,\n): string {\n const executor = db ? `${resolveDbInstanceKey(db)}\\0` : '';\n return `${executor}${sql}\\0${JSON.stringify(params)}`;\n}\n\n/**\n * Read cached rows for a query, or undefined on miss/expiry.\n *\n * Returns a structured clone so callers can never mutate the cached copy\n * (hydration writes into row objects).\n */\nexport function getCachedRows(\n dbKey: string,\n tableName: string,\n queryKey: string,\n): Record<string, unknown>[] | undefined {\n const entry = store.get(dbKey)?.get(tableName)?.get(queryKey);\n if (!entry) return undefined;\n if (entry.expiresAt <= Date.now()) {\n store.get(dbKey)?.get(tableName)?.delete(queryKey);\n return undefined;\n }\n return structuredClone(entry.rows);\n}\n\n/**\n * Store rows for a query under the table's cache scope.\n *\n * `expectedGeneration` is the value {@link getCacheGeneration} returned before\n * the DB read. If an invalidation bumped the table's generation while the read\n * was in flight, the result is stale and is dropped rather than cached.\n */\nexport function setCachedRows(\n dbKey: string,\n tableName: string,\n queryKey: string,\n rows: Record<string, unknown>[],\n ttl: number,\n expectedGeneration?: number,\n): void {\n if (!(ttl > 0)) return;\n\n // Drop a result that a concurrent write invalidated mid-flight.\n if (\n expectedGeneration !== undefined &&\n getCacheGeneration(dbKey, tableName) !== expectedGeneration\n ) {\n return;\n }\n\n let tables = store.get(dbKey);\n if (!tables) {\n tables = new Map();\n store.set(dbKey, tables);\n }\n let entries = tables.get(tableName);\n if (!entries) {\n entries = new Map();\n tables.set(tableName, entries);\n }\n\n // Evict oldest entries (Map preserves insertion order) at the cap. Skip when\n // refreshing an existing key — `set` updates it in place without growing the\n // map, so evicting first would drop a live entry and shrink the effective cap.\n if (!entries.has(queryKey)) {\n while (entries.size >= MAX_ENTRIES_PER_TABLE) {\n const oldest = entries.keys().next().value;\n if (oldest === undefined) break;\n entries.delete(oldest);\n }\n }\n\n entries.set(queryKey, {\n expiresAt: Date.now() + ttl,\n rows: structuredClone(rows),\n });\n}\n\n/**\n * Drop every cached entry for a table. Called by the framework after any\n * successful mutation against that table — this is the write-invalidation\n * guarantee an app-side cache can't make.\n */\nexport function invalidateCollectionCache(\n dbKey: string,\n tableName: string,\n): void {\n store.get(dbKey)?.delete(tableName);\n // Bump the generation so any read whose DB round-trip is still in flight\n // discards its (now-stale) result instead of repopulating the table.\n const gkey = generationKey(dbKey, tableName);\n generations.set(gkey, (generations.get(gkey) ?? 0) + 1);\n}\n\n/**\n * Clear all cached collection reads and stop cross-process listeners.\n * Call in test setup to ensure isolation between test files.\n */\nexport function resetCollectionCache(): void {\n store.clear();\n inFlightReads.clear();\n generations.clear();\n crossProcessInterest.clear();\n stopCacheInvalidationListeners();\n}\n\n// ============================================================================\n// Per-call crossProcess interest (write-side broadcast decision)\n// ============================================================================\n\n/**\n * dbKey → tables this process has cached with a per-call `crossProcess`\n * opt-in. Writes consult this so per-call usage broadcasts too — model-level\n * config can't be the only trigger when the opt-in lives at the call site.\n */\nconst crossProcessInterest = new Map<string, Set<string>>();\n\n/**\n * Record that a per-call `crossProcess` cached read happened for a table.\n */\nexport function registerCrossProcessCacheInterest(\n dbKey: string,\n tableName: string,\n): void {\n let tables = crossProcessInterest.get(dbKey);\n if (!tables) {\n tables = new Set();\n crossProcessInterest.set(dbKey, tables);\n }\n tables.add(tableName);\n}\n\n/**\n * Whether any per-call `crossProcess` cached read has touched this table.\n */\nexport function hasCrossProcessCacheInterest(\n dbKey: string,\n tableName: string,\n): boolean {\n return crossProcessInterest.get(dbKey)?.has(tableName) ?? false;\n}\n\n// ============================================================================\n// Cross-process invalidation (opt-in, via db.notifications)\n// ============================================================================\n\ninterface ListenerHandle {\n iterator: AsyncIterator<unknown> | null;\n stopped: boolean;\n}\n\nconst listeners = new Map<string, ListenerHandle>();\nconst warnedNoNotifications = new Set<string>();\n\n/**\n * The database adapter's optional notification capability (e.g. Postgres\n * LISTEN/NOTIFY), or `undefined` when the adapter exposes none. Duck-typed so\n * core never depends on a concrete adapter shape. Exported so sibling\n * cross-replica buses (the #1763 change-signal bus) resolve it identically.\n */\nexport function getNotifications(db: DatabaseInterface) {\n return (db as DatabaseInterface & { notifications?: unknown })\n .notifications as\n | {\n notify(channel: string, payload: unknown): Promise<number>;\n listen(\n channel: string,\n options?: Record<string, unknown>,\n ): AsyncIterable<{ channel: string; payload: unknown }>;\n }\n | undefined;\n}\n\nfunction warnOnceNoNotifications(dbKey: string, context: string): void {\n if (warnedNoNotifications.has(dbKey)) return;\n warnedNoNotifications.add(dbKey);\n logger.warn(\n `Collection cache: crossProcess invalidation requested but the database ` +\n `adapter exposes no notification capability (${context}). Peer ` +\n `replicas will serve stale rows until TTL.`,\n );\n}\n\n/**\n * Broadcast a table invalidation to peer processes.\n *\n * Fire-and-forget from mutation paths: a broadcast failure must never fail\n * the write that triggered it.\n */\nexport async function broadcastCacheInvalidation(\n db: DatabaseInterface,\n tableName: string,\n): Promise<void> {\n const notifications = getNotifications(db);\n const dbKey = resolveDbCacheKey(db);\n if (!notifications) {\n warnOnceNoNotifications(dbKey, 'broadcast');\n return;\n }\n try {\n await notifications.notify(CACHE_INVALIDATION_CHANNEL, {\n table: tableName,\n source: PROCESS_ID,\n });\n } catch (error) {\n logger.warn(\n `Collection cache: failed to broadcast invalidation for ${tableName}`,\n { error: error instanceof Error ? error.message : error },\n );\n }\n}\n\n/**\n * Ensure a background listener consumes invalidation broadcasts for this\n * database and drops matching local cache entries.\n *\n * Started lazily by the first cached read that opted into `crossProcess`.\n * Notifications published by this process are skipped — the local\n * invalidation already happened synchronously on the write path.\n */\nexport function ensureCacheInvalidationListener(db: DatabaseInterface): void {\n const dbKey = resolveDbCacheKey(db);\n if (listeners.has(dbKey)) return;\n\n const notifications = getNotifications(db);\n if (!notifications) {\n warnOnceNoNotifications(dbKey, 'listen');\n return;\n }\n\n const handle: ListenerHandle = { iterator: null, stopped: false };\n listeners.set(dbKey, handle);\n\n void (async () => {\n try {\n const iterable = notifications.listen(CACHE_INVALIDATION_CHANNEL);\n const iterator = iterable[Symbol.asyncIterator]();\n handle.iterator = iterator;\n\n while (!handle.stopped) {\n const { value, done } = await iterator.next();\n if (done || handle.stopped) break;\n\n const notification = value as { payload?: unknown };\n const payload: unknown =\n typeof notification?.payload === 'string'\n ? safeParse(notification.payload)\n : notification?.payload;\n\n if (!payload || typeof payload !== 'object') continue;\n const record = payload as { table?: unknown; source?: unknown };\n if (typeof record.table !== 'string') continue;\n if (record.source === PROCESS_ID) continue;\n\n invalidateCollectionCache(dbKey, record.table);\n }\n } catch (error) {\n if (!handle.stopped) {\n logger.warn(\n 'Collection cache: invalidation listener terminated unexpectedly; ' +\n 'cross-process invalidation is inactive for this database ' +\n '(local TTL still bounds staleness)',\n { error: error instanceof Error ? error.message : error },\n );\n }\n } finally {\n // Only retract our own handle. A concurrent stop+restart for the same\n // dbKey may have already installed a replacement; deleting unconditionally\n // would orphan it and leak/duplicate the live listener.\n if (listeners.get(dbKey) === handle) {\n listeners.delete(dbKey);\n }\n }\n })();\n}\n\n/**\n * Stop all cross-process invalidation listeners. Used by tests and during\n * shutdown; safe to call when none are active.\n */\nexport function stopCacheInvalidationListeners(): void {\n for (const handle of listeners.values()) {\n handle.stopped = true;\n void handle.iterator?.return?.(undefined);\n }\n listeners.clear();\n warnedNoNotifications.clear();\n}\n\nfunction safeParse(value: string): unknown {\n try {\n return JSON.parse(value);\n } catch {\n return undefined;\n }\n}\n"],"mappings":"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"}
|
package/dist/collection.d.ts
CHANGED
|
@@ -236,6 +236,9 @@ export interface SmrtCollectionOptions extends SmrtClassOptions {
|
|
|
236
236
|
*/
|
|
237
237
|
maxListLimit?: number;
|
|
238
238
|
}
|
|
239
|
+
/** Only provider/configuration unavailability permits a caller's text fallback. */
|
|
240
|
+
export declare class EmbeddingUnavailableError extends Error {
|
|
241
|
+
}
|
|
239
242
|
export type SmrtCollectionItemClass<ModelType extends SmrtObject> = (new (options: any) => ModelType) & {
|
|
240
243
|
create(options: any): ModelType | Promise<ModelType>;
|
|
241
244
|
};
|
|
@@ -290,7 +293,7 @@ export declare class SmrtCollection<ModelType extends SmrtObject> extends SmrtCl
|
|
|
290
293
|
private _defaultListLimit;
|
|
291
294
|
private _maxListLimit;
|
|
292
295
|
private getRegisteredItemClass;
|
|
293
|
-
|
|
296
|
+
protected getResolvedItemClassName(): string;
|
|
294
297
|
private getResolvedItemQualifiedName;
|
|
295
298
|
/** Apply and validate this collection's owned STI discriminator predicate. */
|
|
296
299
|
private applyStiReadScope;
|
|
@@ -600,7 +603,8 @@ export declare class SmrtCollection<ModelType extends SmrtObject> extends SmrtCl
|
|
|
600
603
|
/**
|
|
601
604
|
* Execute a SELECT, optionally through the collection read cache.
|
|
602
605
|
*
|
|
603
|
-
* The cache key
|
|
606
|
+
* The cache key includes the concrete database-interface object and final
|
|
607
|
+
* SQL + bound parameters — computed after
|
|
604
608
|
* interceptors (tenancy filters) and STI discriminators are applied, so
|
|
605
609
|
* differently-scoped queries can never share an entry. Cached values are
|
|
606
610
|
* raw rows; callers still run their normal post-query formatting path.
|
|
@@ -802,6 +806,10 @@ export declare class SmrtCollection<ModelType extends SmrtObject> extends SmrtCl
|
|
|
802
806
|
* @see {@link getOrUpsert} to avoid duplicates by finding-or-creating
|
|
803
807
|
*/
|
|
804
808
|
create(options: SmrtCreateInput<ModelType>): Promise<ModelType>;
|
|
809
|
+
/** @internal Test creation overrides inside the owning protected boundary. */
|
|
810
|
+
static hasBaseCreateLifecycle<T extends SmrtObject>(collection: SmrtCollection<T>): boolean;
|
|
811
|
+
/** Initialize using the same owning path as create(), without persistence. */
|
|
812
|
+
protected createUnsaved(options: SmrtCreateInput<ModelType>): Promise<ModelType>;
|
|
805
813
|
/**
|
|
806
814
|
* Creates an instance of the correct subclass for STI polymorphic queries
|
|
807
815
|
*
|
|
@@ -1265,6 +1273,65 @@ export declare class SmrtCollection<ModelType extends SmrtObject> extends SmrtCl
|
|
|
1265
1273
|
}): Promise<Array<ModelType & {
|
|
1266
1274
|
_similarity: number;
|
|
1267
1275
|
}>>;
|
|
1276
|
+
/**
|
|
1277
|
+
* Prepare normal list authorization for a subclass's bounded hydrated SQL read.
|
|
1278
|
+
* Call finish exactly once on the final page, after query hooks/annotations,
|
|
1279
|
+
* outside provider fallback catches. It preserves the beforeList context and
|
|
1280
|
+
* applies the normal afterList pipeline without fetching replacement rows.
|
|
1281
|
+
*/
|
|
1282
|
+
protected resolveListReadPredicate(where?: SmrtListWhereClause<ModelType>): Promise<{
|
|
1283
|
+
sql: string;
|
|
1284
|
+
values: unknown[];
|
|
1285
|
+
finish(instances: ModelType[]): Promise<ModelType[]>;
|
|
1286
|
+
}>;
|
|
1287
|
+
/**
|
|
1288
|
+
* Search text without hydrating objects. All read predicates (including
|
|
1289
|
+
* `where`, tenancy and STI) apply BEFORE exact cosine ranking. Unlike the
|
|
1290
|
+
* legacy semanticSearch API, a nonmatching high score cannot consume limit.
|
|
1291
|
+
*/
|
|
1292
|
+
semanticSearchIds(query: string, options?: {
|
|
1293
|
+
field?: string;
|
|
1294
|
+
limit?: number;
|
|
1295
|
+
minSimilarity?: number;
|
|
1296
|
+
where?: SmrtListWhereClause<ModelType>;
|
|
1297
|
+
}): Promise<Array<{
|
|
1298
|
+
id: string;
|
|
1299
|
+
similarity: number;
|
|
1300
|
+
}>>;
|
|
1301
|
+
/**
|
|
1302
|
+
* Provider-origin availability for bounded subclass reads. Only missing
|
|
1303
|
+
* configuration or provider.embed failure returns unavailable; all option,
|
|
1304
|
+
* authorization, ranking and database failures propagate unchanged.
|
|
1305
|
+
*/
|
|
1306
|
+
protected semanticSearchIdsWithAvailability(query: string, options?: {
|
|
1307
|
+
field?: string;
|
|
1308
|
+
limit?: number;
|
|
1309
|
+
minSimilarity?: number;
|
|
1310
|
+
where?: SmrtListWhereClause<ModelType>;
|
|
1311
|
+
}): Promise<{
|
|
1312
|
+
available: true;
|
|
1313
|
+
matches: Array<{
|
|
1314
|
+
id: string;
|
|
1315
|
+
similarity: number;
|
|
1316
|
+
}>;
|
|
1317
|
+
} | {
|
|
1318
|
+
available: false;
|
|
1319
|
+
error: EmbeddingUnavailableError;
|
|
1320
|
+
}>;
|
|
1321
|
+
/**
|
|
1322
|
+
* Return scored IDs using bounded embedding batches and scalar SQL scope
|
|
1323
|
+
* masks. Application rows are never fetched; separate system/app databases
|
|
1324
|
+
* are supported. Equal scores sort by object ID for stable pagination.
|
|
1325
|
+
*/
|
|
1326
|
+
findSimilarIdsToEmbedding(embedding: number[], options?: {
|
|
1327
|
+
field?: string;
|
|
1328
|
+
limit?: number;
|
|
1329
|
+
minSimilarity?: number;
|
|
1330
|
+
where?: SmrtListWhereClause<ModelType>;
|
|
1331
|
+
}): Promise<Array<{
|
|
1332
|
+
id: string;
|
|
1333
|
+
similarity: number;
|
|
1334
|
+
}>>;
|
|
1268
1335
|
/**
|
|
1269
1336
|
* Find objects similar to a given object
|
|
1270
1337
|
*
|
package/dist/collection.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"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"}
|
package/dist/collection.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { chunkArray } from "./utils/chunk.js";
|
|
2
2
|
import { classnameToTablename, toSnakeCase } from "./utils/naming.js";
|
|
3
|
-
import { buildQueryCacheKey, ensureCacheInvalidationListener, getCacheGeneration, getCachedRows, invalidateCollectionCache, registerCrossProcessCacheInterest, resolveDbCacheKey, setCachedRows } from "./collection-cache.js";
|
|
3
|
+
import { buildQueryCacheKey, ensureCacheInvalidationListener, getCacheGeneration, getCachedRows, getOrCreateInFlightRead, invalidateCollectionCache, registerCrossProcessCacheInterest, resolveDbCacheKey, setCachedRows } from "./collection-cache.js";
|
|
4
4
|
import { toSafeInteger } from "./utils/safe-integer.js";
|
|
5
5
|
import { isTenantScopedClassResolved, resolveDispatchTenantScope } from "./dispatch/tenant-resolver.js";
|
|
6
6
|
import { GlobalInterceptors, createInterceptorContext, resolveGetStringFilter } from "./interceptors.js";
|
|
@@ -183,6 +183,8 @@ function getSearchableEmbeddingFields(config) {
|
|
|
183
183
|
if (!combinedName || config.fields.includes(combinedName)) return config.fields;
|
|
184
184
|
return [...config.fields, combinedName];
|
|
185
185
|
}
|
|
186
|
+
/** Only provider/configuration unavailability permits a caller's text fallback. */
|
|
187
|
+
var EmbeddingUnavailableError = class extends Error {};
|
|
186
188
|
/**
|
|
187
189
|
* Typed CRUD collection for a specific `SmrtObject` subclass.
|
|
188
190
|
*
|
|
@@ -1033,7 +1035,8 @@ var SmrtCollection = class SmrtCollection extends SmrtClass {
|
|
|
1033
1035
|
/**
|
|
1034
1036
|
* Execute a SELECT, optionally through the collection read cache.
|
|
1035
1037
|
*
|
|
1036
|
-
* The cache key
|
|
1038
|
+
* The cache key includes the concrete database-interface object and final
|
|
1039
|
+
* SQL + bound parameters — computed after
|
|
1037
1040
|
* interceptors (tenancy filters) and STI discriminators are applied, so
|
|
1038
1041
|
* differently-scoped queries can never share an entry. Cached values are
|
|
1039
1042
|
* raw rows; callers still run their normal post-query formatting path.
|
|
@@ -1041,7 +1044,7 @@ var SmrtCollection = class SmrtCollection extends SmrtClass {
|
|
|
1041
1044
|
async queryRowsWithCache(sql, params, cacheConfig, describeUuidOutputs) {
|
|
1042
1045
|
if (!cacheConfig) return await this.queryDuckDbCanonicalSelectRows(sql, params, describeUuidOutputs);
|
|
1043
1046
|
const dbKey = resolveDbCacheKey(this.db);
|
|
1044
|
-
const queryKey = buildQueryCacheKey(sql, params);
|
|
1047
|
+
const queryKey = buildQueryCacheKey(sql, params, this.db);
|
|
1045
1048
|
if (cacheConfig.crossProcess) {
|
|
1046
1049
|
ensureCacheInvalidationListener(this.db);
|
|
1047
1050
|
registerCrossProcessCacheInterest(dbKey, this.tableName);
|
|
@@ -1049,9 +1052,12 @@ var SmrtCollection = class SmrtCollection extends SmrtClass {
|
|
|
1049
1052
|
const cached = getCachedRows(dbKey, this.tableName, queryKey);
|
|
1050
1053
|
if (cached) return cached;
|
|
1051
1054
|
const generation = getCacheGeneration(dbKey, this.tableName);
|
|
1052
|
-
const rows = await this.
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
+
const rows = await getOrCreateInFlightRead(dbKey, this.tableName, queryKey, generation, async () => {
|
|
1056
|
+
const rows = await this.queryDuckDbCanonicalSelectRows(sql, params, describeUuidOutputs);
|
|
1057
|
+
setCachedRows(dbKey, this.tableName, queryKey, rows, cacheConfig.ttl, generation);
|
|
1058
|
+
return rows;
|
|
1059
|
+
});
|
|
1060
|
+
return structuredClone(rows);
|
|
1055
1061
|
}
|
|
1056
1062
|
/**
|
|
1057
1063
|
* Describe a native DuckDB SELECT before executing it, then cast UUID output
|
|
@@ -1371,6 +1377,16 @@ var SmrtCollection = class SmrtCollection extends SmrtClass {
|
|
|
1371
1377
|
* @see {@link getOrUpsert} to avoid duplicates by finding-or-creating
|
|
1372
1378
|
*/
|
|
1373
1379
|
async create(options) {
|
|
1380
|
+
const instance = await this.createUnsaved(options);
|
|
1381
|
+
await instance.save();
|
|
1382
|
+
return instance;
|
|
1383
|
+
}
|
|
1384
|
+
/** @internal Test creation overrides inside the owning protected boundary. */
|
|
1385
|
+
static hasBaseCreateLifecycle(collection) {
|
|
1386
|
+
return collection.create === SmrtCollection.prototype.create && collection.createUnsaved === SmrtCollection.prototype.createUnsaved;
|
|
1387
|
+
}
|
|
1388
|
+
/** Initialize using the same owning path as create(), without persistence. */
|
|
1389
|
+
async createUnsaved(options) {
|
|
1374
1390
|
let itemClassName = this.getResolvedItemClassName();
|
|
1375
1391
|
await ObjectRegistry.ensureManifestLoaded(itemClassName);
|
|
1376
1392
|
itemClassName = this.getResolvedItemClassName();
|
|
@@ -1380,7 +1396,6 @@ var SmrtCollection = class SmrtCollection extends SmrtClass {
|
|
|
1380
1396
|
const instance = await this.createPolymorphic(options._meta_type, options);
|
|
1381
1397
|
if (!instance.id) instance._id = crypto.randomUUID();
|
|
1382
1398
|
if (options._insertOnly === true) instance.requireInsertOnSave();
|
|
1383
|
-
await instance.save();
|
|
1384
1399
|
return instance;
|
|
1385
1400
|
}
|
|
1386
1401
|
const params = {
|
|
@@ -1394,7 +1409,6 @@ var SmrtCollection = class SmrtCollection extends SmrtClass {
|
|
|
1394
1409
|
if (tableStrategy === "sti") instance._meta_type = itemQualifiedName;
|
|
1395
1410
|
if (!instance.id) instance._id = crypto.randomUUID();
|
|
1396
1411
|
if (options._insertOnly === true) instance.requireInsertOnSave();
|
|
1397
|
-
await instance.save();
|
|
1398
1412
|
return instance;
|
|
1399
1413
|
}
|
|
1400
1414
|
/**
|
|
@@ -2252,6 +2266,125 @@ var SmrtCollection = class SmrtCollection extends SmrtClass {
|
|
|
2252
2266
|
});
|
|
2253
2267
|
}
|
|
2254
2268
|
/**
|
|
2269
|
+
* Prepare normal list authorization for a subclass's bounded hydrated SQL read.
|
|
2270
|
+
* Call finish exactly once on the final page, after query hooks/annotations,
|
|
2271
|
+
* outside provider fallback catches. It preserves the beforeList context and
|
|
2272
|
+
* applies the normal afterList pipeline without fetching replacement rows.
|
|
2273
|
+
*/
|
|
2274
|
+
async resolveListReadPredicate(where = {}) {
|
|
2275
|
+
await this.ensureStorageReady();
|
|
2276
|
+
const className = this.getResolvedItemClassName();
|
|
2277
|
+
const context = createInterceptorContext(className, "list", this.constructor.name, void 0, this.getResolvedItemQualifiedName());
|
|
2278
|
+
const options = await GlobalInterceptors.executeBeforeList(className, { where }, context);
|
|
2279
|
+
const scoped = resolveMetaTypeInWhere(this.applyStiReadScope(options.where, void 0));
|
|
2280
|
+
const predicate = buildWhere(this.convertWhereKeys(scoped || {}));
|
|
2281
|
+
return {
|
|
2282
|
+
sql: predicate.sql.trim().replace(/^WHERE\s+/i, "").replace(/\$\d+/g, "?") || "1 = 1",
|
|
2283
|
+
values: predicate.values,
|
|
2284
|
+
finish: (instances) => GlobalInterceptors.executeAfterList(className, instances, context)
|
|
2285
|
+
};
|
|
2286
|
+
}
|
|
2287
|
+
/**
|
|
2288
|
+
* Search text without hydrating objects. All read predicates (including
|
|
2289
|
+
* `where`, tenancy and STI) apply BEFORE exact cosine ranking. Unlike the
|
|
2290
|
+
* legacy semanticSearch API, a nonmatching high score cannot consume limit.
|
|
2291
|
+
*/
|
|
2292
|
+
async semanticSearchIds(query, options = {}) {
|
|
2293
|
+
const result = await this.semanticSearchIdsWithAvailability(query, options);
|
|
2294
|
+
if (!result.available) throw result.error;
|
|
2295
|
+
return result.matches;
|
|
2296
|
+
}
|
|
2297
|
+
/**
|
|
2298
|
+
* Provider-origin availability for bounded subclass reads. Only missing
|
|
2299
|
+
* configuration or provider.embed failure returns unavailable; all option,
|
|
2300
|
+
* authorization, ranking and database failures propagate unchanged.
|
|
2301
|
+
*/
|
|
2302
|
+
async semanticSearchIdsWithAvailability(query, options = {}) {
|
|
2303
|
+
const config = ObjectRegistry.resolveEmbeddingConfig(this._itemClass.name);
|
|
2304
|
+
if (!config) return {
|
|
2305
|
+
available: false,
|
|
2306
|
+
error: new EmbeddingUnavailableError(`No embedding configuration found for ${this._itemClass.name}.`)
|
|
2307
|
+
};
|
|
2308
|
+
const field = options.field || config.fields[0];
|
|
2309
|
+
if (!getSearchableEmbeddingFields(config).includes(field)) throw new Error(`Field '${field}' is not configured for embeddings on ${this._itemClass.name}.`);
|
|
2310
|
+
const provider = new EmbeddingProvider({
|
|
2311
|
+
dimensions: config.dimensions,
|
|
2312
|
+
provider: config.provider,
|
|
2313
|
+
localModel: config.localModel,
|
|
2314
|
+
aiModel: config.aiModel,
|
|
2315
|
+
fallbackToAI: config.fallbackToAI
|
|
2316
|
+
}, this.ai);
|
|
2317
|
+
let embedding;
|
|
2318
|
+
try {
|
|
2319
|
+
[embedding] = await provider.embed(query);
|
|
2320
|
+
} catch (cause) {
|
|
2321
|
+
return {
|
|
2322
|
+
available: false,
|
|
2323
|
+
error: new EmbeddingUnavailableError("Query embedding is unavailable", { cause })
|
|
2324
|
+
};
|
|
2325
|
+
}
|
|
2326
|
+
return {
|
|
2327
|
+
available: true,
|
|
2328
|
+
matches: await this.findSimilarIdsToEmbedding(embedding, {
|
|
2329
|
+
...options,
|
|
2330
|
+
field
|
|
2331
|
+
})
|
|
2332
|
+
};
|
|
2333
|
+
}
|
|
2334
|
+
/**
|
|
2335
|
+
* Return scored IDs using bounded embedding batches and scalar SQL scope
|
|
2336
|
+
* masks. Application rows are never fetched; separate system/app databases
|
|
2337
|
+
* are supported. Equal scores sort by object ID for stable pagination.
|
|
2338
|
+
*/
|
|
2339
|
+
async findSimilarIdsToEmbedding(embedding, options = {}) {
|
|
2340
|
+
const { limit = 10, minSimilarity = 0 } = options;
|
|
2341
|
+
if (!Number.isSafeInteger(limit) || limit < 0) throw new Error("Semantic ID search limit must be a nonnegative safe integer");
|
|
2342
|
+
if (!Number.isFinite(minSimilarity) || minSimilarity < -1 || minSimilarity > 1) throw new Error("Semantic ID search minSimilarity must be between -1 and 1");
|
|
2343
|
+
await this.ensureStorageReady();
|
|
2344
|
+
const itemClassName = this.getResolvedItemClassName();
|
|
2345
|
+
const intercepted = await GlobalInterceptors.executeBeforeList(itemClassName, { where: options.where || {} }, createInterceptorContext(itemClassName, "list", this.constructor.name, void 0, this.getResolvedItemQualifiedName()));
|
|
2346
|
+
const scopedWhere = resolveMetaTypeInWhere(this.applyStiReadScope(intercepted.where, void 0));
|
|
2347
|
+
const { sql: whereSql, values: whereValues } = buildWhere(this.convertWhereKeys(scopedWhere || {}));
|
|
2348
|
+
const config = ObjectRegistry.resolveEmbeddingConfig(this._itemClass.name);
|
|
2349
|
+
if (!config) throw new Error(`No embedding configuration found for ${this._itemClass.name}.`);
|
|
2350
|
+
const field = options.field || config.fields[0];
|
|
2351
|
+
if (!getSearchableEmbeddingFields(config).includes(field)) throw new Error(`Field '${field}' is not configured for embeddings on ${this._itemClass.name}.`);
|
|
2352
|
+
const provider = new EmbeddingProvider({
|
|
2353
|
+
dimensions: config.dimensions,
|
|
2354
|
+
provider: config.provider,
|
|
2355
|
+
localModel: config.localModel,
|
|
2356
|
+
aiModel: config.aiModel,
|
|
2357
|
+
fallbackToAI: config.fallbackToAI
|
|
2358
|
+
}, this.ai);
|
|
2359
|
+
if (limit === 0) return [];
|
|
2360
|
+
return (await EmbeddingStorage.searchSimilarBatched(this.systemDb, this._itemClass.name, embedding, {
|
|
2361
|
+
field,
|
|
2362
|
+
model: provider.getModelName(),
|
|
2363
|
+
limit,
|
|
2364
|
+
minSimilarity,
|
|
2365
|
+
eligible: async (ids) => {
|
|
2366
|
+
const chunkSize = Math.max(1, 999 - whereValues.length);
|
|
2367
|
+
const eligible = [];
|
|
2368
|
+
for (let offset = 0; offset < ids.length; offset += chunkSize) {
|
|
2369
|
+
const chunk = ids.slice(offset, offset + chunkSize);
|
|
2370
|
+
const cases = chunk.map((_, index) => `CASE WHEN EXISTS (SELECT 1 FROM eligible_scope
|
|
2371
|
+
WHERE eligible_id = $${whereValues.length + index + 1})
|
|
2372
|
+
THEN '1' ELSE '0' END`);
|
|
2373
|
+
const { rows } = await this.db.query(`WITH eligible_scope AS (
|
|
2374
|
+
SELECT id AS eligible_id FROM ${this.tableName} ${whereSql}
|
|
2375
|
+
) SELECT ${cases.join(" || ")} AS eligibility`, ...whereValues, ...chunk);
|
|
2376
|
+
const mask = rows[0]?.eligibility;
|
|
2377
|
+
if (typeof mask !== "string" || !/^[01]+$/.test(mask) || mask.length !== chunk.length) throw new Error("Invalid semantic search eligibility result");
|
|
2378
|
+
eligible.push(...[...mask].map((bit) => bit === "1"));
|
|
2379
|
+
}
|
|
2380
|
+
return eligible;
|
|
2381
|
+
}
|
|
2382
|
+
})).map(({ objectId, similarity }) => ({
|
|
2383
|
+
id: objectId,
|
|
2384
|
+
similarity
|
|
2385
|
+
}));
|
|
2386
|
+
}
|
|
2387
|
+
/**
|
|
2255
2388
|
* Find objects similar to a given object
|
|
2256
2389
|
*
|
|
2257
2390
|
* Uses stored embeddings to find objects most similar to the provided object.
|
|
@@ -2428,6 +2561,6 @@ var SmrtCollection = class SmrtCollection extends SmrtClass {
|
|
|
2428
2561
|
}
|
|
2429
2562
|
};
|
|
2430
2563
|
//#endregion
|
|
2431
|
-
export { DEFAULT_FACET_LIMIT, MAX_FACET_FIELDS, MAX_FACET_LIMIT, MAX_STI_READ_SCOPE_TYPES, SmrtCollection };
|
|
2564
|
+
export { DEFAULT_FACET_LIMIT, EmbeddingUnavailableError, MAX_FACET_FIELDS, MAX_FACET_LIMIT, MAX_STI_READ_SCOPE_TYPES, SmrtCollection };
|
|
2432
2565
|
|
|
2433
2566
|
//# sourceMappingURL=collection.js.map
|