@happyvertical/smrt-core 0.40.69 → 0.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/AGENTS.md +29 -4
  2. package/README.md +20 -1
  3. package/agents/change-feed.md +1 -1
  4. package/agents/query-bounds.md +45 -0
  5. package/agents/schema-paths.md +786 -0
  6. package/dist/browser.d.ts +1 -0
  7. package/dist/browser.d.ts.map +1 -1
  8. package/dist/browser.js +5 -3
  9. package/dist/cascade.d.ts +120 -0
  10. package/dist/cascade.d.ts.map +1 -0
  11. package/dist/cascade.js +430 -0
  12. package/dist/cascade.js.map +1 -0
  13. package/dist/change-feed.d.ts +34 -2
  14. package/dist/change-feed.d.ts.map +1 -1
  15. package/dist/change-feed.js +52 -11
  16. package/dist/change-feed.js.map +1 -1
  17. package/dist/class.d.ts +36 -3
  18. package/dist/class.d.ts.map +1 -1
  19. package/dist/class.js +87 -9
  20. package/dist/class.js.map +1 -1
  21. package/dist/collection-cache.js +0 -0
  22. package/dist/collection-cache.js.map +1 -1
  23. package/dist/collection.d.ts +130 -2
  24. package/dist/collection.d.ts.map +1 -1
  25. package/dist/collection.js +290 -57
  26. package/dist/collection.js.map +1 -1
  27. package/dist/config.d.ts +10 -0
  28. package/dist/config.d.ts.map +1 -1
  29. package/dist/config.js.map +1 -1
  30. package/dist/database.d.ts +8 -0
  31. package/dist/database.d.ts.map +1 -1
  32. package/dist/database.js +16 -8
  33. package/dist/database.js.map +1 -1
  34. package/dist/db-errors.d.ts +105 -0
  35. package/dist/db-errors.d.ts.map +1 -0
  36. package/dist/db-errors.js +382 -0
  37. package/dist/db-errors.js.map +1 -0
  38. package/dist/decorators/index.d.ts +80 -6
  39. package/dist/decorators/index.d.ts.map +1 -1
  40. package/dist/decorators/index.js +102 -12
  41. package/dist/decorators/index.js.map +1 -1
  42. package/dist/dispatch/bus.d.ts.map +1 -1
  43. package/dist/dispatch/bus.js +4 -3
  44. package/dist/dispatch/bus.js.map +1 -1
  45. package/dist/dispatch/collections/Dispatches.d.ts.map +1 -1
  46. package/dist/dispatch/collections/Dispatches.js +19 -4
  47. package/dist/dispatch/collections/Dispatches.js.map +1 -1
  48. package/dist/dispatch/types.d.ts +5 -0
  49. package/dist/dispatch/types.d.ts.map +1 -1
  50. package/dist/embedded-write-queue.d.ts +46 -0
  51. package/dist/embedded-write-queue.d.ts.map +1 -0
  52. package/dist/embedded-write-queue.js +66 -0
  53. package/dist/embedded-write-queue.js.map +1 -0
  54. package/dist/embeddings/storage.d.ts +7 -0
  55. package/dist/embeddings/storage.d.ts.map +1 -1
  56. package/dist/embeddings/storage.js +29 -12
  57. package/dist/embeddings/storage.js.map +1 -1
  58. package/dist/errors.d.ts +31 -3
  59. package/dist/errors.d.ts.map +1 -1
  60. package/dist/errors.js +34 -2
  61. package/dist/errors.js.map +1 -1
  62. package/dist/generators/changes-route.d.ts.map +1 -1
  63. package/dist/generators/changes-route.js +6 -3
  64. package/dist/generators/changes-route.js.map +1 -1
  65. package/dist/generators/mcp-runtime-template.d.ts +8 -0
  66. package/dist/generators/mcp-runtime-template.d.ts.map +1 -1
  67. package/dist/generators/mcp-runtime-template.js +38 -4
  68. package/dist/generators/mcp-runtime-template.js.map +1 -1
  69. package/dist/generators/mcp.d.ts +16 -0
  70. package/dist/generators/mcp.d.ts.map +1 -1
  71. package/dist/generators/mcp.js +41 -3
  72. package/dist/generators/mcp.js.map +1 -1
  73. package/dist/generators/rest.d.ts +22 -0
  74. package/dist/generators/rest.d.ts.map +1 -1
  75. package/dist/generators/rest.js +34 -3
  76. package/dist/generators/rest.js.map +1 -1
  77. package/dist/hierarchical.js +1 -1
  78. package/dist/index.d.ts +7 -1
  79. package/dist/index.d.ts.map +1 -1
  80. package/dist/index.js +12 -5
  81. package/dist/interceptors.d.ts +21 -0
  82. package/dist/interceptors.d.ts.map +1 -1
  83. package/dist/interceptors.js +27 -1
  84. package/dist/interceptors.js.map +1 -1
  85. package/dist/manifest/generator.d.ts.map +1 -1
  86. package/dist/manifest/generator.js +4 -7
  87. package/dist/manifest/generator.js.map +1 -1
  88. package/dist/manifest/static-manifest.js +10 -10
  89. package/dist/manifest/static-manifest.js.map +1 -1
  90. package/dist/manifest/store.js +1 -1
  91. package/dist/manifest/store.js.map +1 -1
  92. package/dist/manifest.json +19 -19
  93. package/dist/migrations/differ.d.ts +211 -9
  94. package/dist/migrations/differ.d.ts.map +1 -1
  95. package/dist/migrations/differ.js +613 -50
  96. package/dist/migrations/differ.js.map +1 -1
  97. package/dist/migrations/generator.d.ts +31 -4
  98. package/dist/migrations/generator.d.ts.map +1 -1
  99. package/dist/migrations/generator.js +49 -5
  100. package/dist/migrations/generator.js.map +1 -1
  101. package/dist/migrations/index.d.ts +4 -2
  102. package/dist/migrations/index.d.ts.map +1 -1
  103. package/dist/migrations/index.js +6 -3
  104. package/dist/migrations/minor-units.d.ts +162 -0
  105. package/dist/migrations/minor-units.d.ts.map +1 -0
  106. package/dist/migrations/minor-units.js +381 -0
  107. package/dist/migrations/minor-units.js.map +1 -0
  108. package/dist/migrations/orchestrate.js +35 -6
  109. package/dist/migrations/orchestrate.js.map +1 -1
  110. package/dist/migrations/sqlite-rebuild.d.ts +142 -0
  111. package/dist/migrations/sqlite-rebuild.d.ts.map +1 -0
  112. package/dist/migrations/sqlite-rebuild.js +514 -0
  113. package/dist/migrations/sqlite-rebuild.js.map +1 -0
  114. package/dist/migrations/tracker.d.ts +114 -1
  115. package/dist/migrations/tracker.d.ts.map +1 -1
  116. package/dist/migrations/tracker.js +331 -16
  117. package/dist/migrations/tracker.js.map +1 -1
  118. package/dist/migrations/types.d.ts +19 -4
  119. package/dist/migrations/types.d.ts.map +1 -1
  120. package/dist/migrations.js +6 -3
  121. package/dist/object.d.ts +142 -10
  122. package/dist/object.d.ts.map +1 -1
  123. package/dist/object.js +196 -41
  124. package/dist/object.js.map +1 -1
  125. package/dist/postgres-timeouts.d.ts +240 -0
  126. package/dist/postgres-timeouts.d.ts.map +1 -0
  127. package/dist/postgres-timeouts.js +204 -0
  128. package/dist/postgres-timeouts.js.map +1 -0
  129. package/dist/query-bounds.d.ts +101 -0
  130. package/dist/query-bounds.d.ts.map +1 -0
  131. package/dist/query-bounds.js +177 -0
  132. package/dist/query-bounds.js.map +1 -0
  133. package/dist/registry/class-registration.d.ts.map +1 -1
  134. package/dist/registry/class-registration.js +3 -1
  135. package/dist/registry/class-registration.js.map +1 -1
  136. package/dist/registry/manifest-field-merge.d.ts +12 -0
  137. package/dist/registry/manifest-field-merge.d.ts.map +1 -1
  138. package/dist/registry/manifest-field-merge.js +14 -2
  139. package/dist/registry/manifest-field-merge.js.map +1 -1
  140. package/dist/registry/schema-builder.d.ts +22 -1
  141. package/dist/registry/schema-builder.d.ts.map +1 -1
  142. package/dist/registry/schema-builder.js +205 -165
  143. package/dist/registry/schema-builder.js.map +1 -1
  144. package/dist/registry/types.d.ts +35 -3
  145. package/dist/registry/types.d.ts.map +1 -1
  146. package/dist/registry.d.ts +41 -46
  147. package/dist/registry.d.ts.map +1 -1
  148. package/dist/registry.js +61 -83
  149. package/dist/registry.js.map +1 -1
  150. package/dist/scanner/manifest-generator.d.ts +45 -0
  151. package/dist/scanner/manifest-generator.d.ts.map +1 -1
  152. package/dist/scanner/manifest-generator.js +92 -28
  153. package/dist/scanner/manifest-generator.js.map +1 -1
  154. package/dist/scanner/types.d.ts +5 -0
  155. package/dist/scanner/types.d.ts.map +1 -1
  156. package/dist/scanner/types.js.map +1 -1
  157. package/dist/schema/conflict-target.d.ts +104 -0
  158. package/dist/schema/conflict-target.d.ts.map +1 -0
  159. package/dist/schema/conflict-target.js +129 -0
  160. package/dist/schema/conflict-target.js.map +1 -0
  161. package/dist/schema/ddl/base-strategy.d.ts.map +1 -1
  162. package/dist/schema/ddl/base-strategy.js +2 -2
  163. package/dist/schema/ddl/base-strategy.js.map +1 -1
  164. package/dist/schema/ddl/duckdb-strategy.d.ts.map +1 -1
  165. package/dist/schema/ddl/duckdb-strategy.js +2 -1
  166. package/dist/schema/ddl/duckdb-strategy.js.map +1 -1
  167. package/dist/schema/ddl/postgres-strategy.d.ts.map +1 -1
  168. package/dist/schema/ddl/postgres-strategy.js +12 -1
  169. package/dist/schema/ddl/postgres-strategy.js.map +1 -1
  170. package/dist/schema/generator.d.ts +307 -6
  171. package/dist/schema/generator.d.ts.map +1 -1
  172. package/dist/schema/generator.js +510 -87
  173. package/dist/schema/generator.js.map +1 -1
  174. package/dist/schema/index-utils.d.ts +120 -0
  175. package/dist/schema/index-utils.d.ts.map +1 -1
  176. package/dist/schema/index-utils.js +242 -1
  177. package/dist/schema/index-utils.js.map +1 -1
  178. package/dist/schema/index.d.ts +3 -0
  179. package/dist/schema/index.d.ts.map +1 -1
  180. package/dist/schema/index.js +4 -1
  181. package/dist/schema/live-parity.d.ts +90 -0
  182. package/dist/schema/live-parity.d.ts.map +1 -0
  183. package/dist/schema/live-parity.js +602 -0
  184. package/dist/schema/live-parity.js.map +1 -0
  185. package/dist/schema/manifest-schema.d.ts +121 -0
  186. package/dist/schema/manifest-schema.d.ts.map +1 -0
  187. package/dist/schema/manifest-schema.js +267 -0
  188. package/dist/schema/manifest-schema.js.map +1 -0
  189. package/dist/schema/schema-aggregator.d.ts +24 -10
  190. package/dist/schema/schema-aggregator.d.ts.map +1 -1
  191. package/dist/schema/schema-aggregator.js +35 -90
  192. package/dist/schema/schema-aggregator.js.map +1 -1
  193. package/dist/schema/system-table-shapes.d.ts +65 -0
  194. package/dist/schema/system-table-shapes.d.ts.map +1 -0
  195. package/dist/schema/system-table-shapes.js +187 -0
  196. package/dist/schema/system-table-shapes.js.map +1 -0
  197. package/dist/schema/types.d.ts +103 -4
  198. package/dist/schema/types.d.ts.map +1 -1
  199. package/dist/schema/utils.d.ts +2 -1
  200. package/dist/schema/utils.d.ts.map +1 -1
  201. package/dist/schema/utils.js +5 -3
  202. package/dist/schema/utils.js.map +1 -1
  203. package/dist/schema.js +4 -1
  204. package/dist/smrt-knowledge.json +20 -8
  205. package/dist/sync/apply.d.ts.map +1 -1
  206. package/dist/sync/apply.js +9 -16
  207. package/dist/sync/apply.js.map +1 -1
  208. package/dist/system/compatibility.d.ts +42 -0
  209. package/dist/system/compatibility.d.ts.map +1 -1
  210. package/dist/system/compatibility.js +182 -9
  211. package/dist/system/compatibility.js.map +1 -1
  212. package/dist/system/index.d.ts +1 -0
  213. package/dist/system/index.d.ts.map +1 -1
  214. package/dist/system/index.js +3 -2
  215. package/dist/system/retention.d.ts +237 -0
  216. package/dist/system/retention.d.ts.map +1 -0
  217. package/dist/system/retention.js +497 -0
  218. package/dist/system/retention.js.map +1 -0
  219. package/dist/system/schema.d.ts +100 -15
  220. package/dist/system/schema.d.ts.map +1 -1
  221. package/dist/system/schema.js +81 -45
  222. package/dist/system/schema.js.map +1 -1
  223. package/dist/system/types.d.ts +0 -2
  224. package/dist/system/types.d.ts.map +1 -1
  225. package/dist/testing/database.d.ts.map +1 -1
  226. package/dist/testing/database.js +1 -0
  227. package/dist/testing/database.js.map +1 -1
  228. package/dist/vite-plugin/index.d.ts.map +1 -1
  229. package/dist/vite-plugin/index.js +4 -9
  230. package/dist/vite-plugin/index.js.map +1 -1
  231. package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
  232. package/dist/vite-plugin/sveltekit-generator.js +71 -5
  233. package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
  234. package/dist/vite-plugin/web-collections.d.ts.map +1 -1
  235. package/dist/vite-plugin/web-collections.js +6 -4
  236. package/dist/vite-plugin/web-collections.js.map +1 -1
  237. package/package.json +5 -5
@@ -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}\u0000${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}\u0000${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 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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"}
@@ -124,6 +124,23 @@ interface CollectionFieldDefinition {
124
124
  * Configuration options for SmrtCollection
125
125
  */
126
126
  export interface SmrtCollectionOptions extends SmrtClassOptions {
127
+ /**
128
+ * Page size `list()` applies when the caller passes no `limit` (#2367).
129
+ *
130
+ * Unset by default — `list()` is the framework's bulk-read primitive and its
131
+ * internal callers expect every matching row, so an implicit default would
132
+ * truncate correct queries. Set this on collections whose reads are always
133
+ * paged.
134
+ */
135
+ defaultListLimit?: number;
136
+ /**
137
+ * Ceiling `list()` clamps every `limit` down to (#2367).
138
+ *
139
+ * Unset by default. The generated REST/SvelteKit/MCP surfaces enforce
140
+ * `MAX_LIST_LIMIT` on their own untrusted input regardless of this option;
141
+ * set it to extend the same ceiling to direct programmatic reads.
142
+ */
143
+ maxListLimit?: number;
127
144
  }
128
145
  export type SmrtCollectionItemClass<ModelType extends SmrtObject> = (new (options: any) => ModelType) & {
129
146
  create(options: any): ModelType | Promise<ModelType>;
@@ -171,9 +188,71 @@ export declare class SmrtCollection<ModelType extends SmrtObject> extends SmrtCl
171
188
  * @private
172
189
  */
173
190
  private _cachedFields;
191
+ /**
192
+ * Opt-in list bounds (#2367). See {@link SmrtCollectionOptions.defaultListLimit}
193
+ * and {@link SmrtCollectionOptions.maxListLimit}.
194
+ * @private
195
+ */
196
+ private _defaultListLimit;
197
+ private _maxListLimit;
174
198
  private getRegisteredItemClass;
175
199
  private getResolvedItemClassName;
176
200
  private getResolvedItemQualifiedName;
201
+ /**
202
+ * Resolve the identifier whitelist and the sensitive-column blocklist that
203
+ * every caller-supplied identifier on this collection is checked against.
204
+ *
205
+ * Extracted from `convertWhereKeys()` so `orderBy` validation enforces the
206
+ * *same* sets (#2367). `where` (#1540) and `select` (#1902) both rejected
207
+ * sensitive columns while `orderBy` accepted any identifier, which made
208
+ * `?orderBy=api_secret&limit=1` an ordering oracle over a column the same
209
+ * request is forbidden to filter on or project.
210
+ *
211
+ * @returns the snake_case identifiers this collection accepts, the sensitive
212
+ * names it must refuse, and whether whitelist enforcement applies at all —
213
+ * classes with no registered fields (manifest-less inline test classes,
214
+ * #869) have nothing to validate against and fall through to SQL.
215
+ * @private
216
+ */
217
+ private collectQueryableFieldNames;
218
+ /**
219
+ * Build the `ORDER BY` clause for `list()`, validating every term against the
220
+ * same whitelist and sensitive-column blocklist as `where` and `select`
221
+ * (#2367).
222
+ *
223
+ * `orderBy` is interpolated UNPARAMETERIZED into the identifier position, and
224
+ * it arrives from the generated REST/MCP/SvelteKit surfaces as caller input.
225
+ * The identifier regex here has always blocked injection; what it did not
226
+ * block was ordering by a column the caller may not read. Sorting is a
227
+ * comparison, and a comparison against a secret is an oracle: repeatedly
228
+ * requesting `?orderBy=api_secret&limit=1` (with a shrinking `where`) walks
229
+ * the secret's value ordering without ever serializing the column.
230
+ *
231
+ * @param orderBy - one `'<field> [ASC|DESC]'` term or an array of them
232
+ * @param fields - the collection's cached field definitions
233
+ * @returns the `' ORDER BY ...'` fragment, or `''` when no ordering was asked
234
+ * for
235
+ * @throws {QueryOrderByError} on a malformed term, an unknown column, or a
236
+ * sensitive column — all rendered as a 400 by the generated surfaces
237
+ * @private
238
+ */
239
+ private buildOrderBySql;
240
+ /**
241
+ * Apply this collection's configured list bounds (#2367).
242
+ *
243
+ * Both bounds are opt-in (`defaultListLimit` / `maxListLimit` collection
244
+ * options) and default to unset. `list()` is the framework's bulk-read
245
+ * primitive — relationship loaders, junction hydration and `listByIds()` all
246
+ * go through it expecting every matching row — so a framework-wide implicit
247
+ * default would silently truncate correct queries instead of bounding an
248
+ * attack surface. The generated surfaces, where untrusted input actually
249
+ * arrives, apply {@link DEFAULT_LIST_LIMIT}/{@link MAX_LIST_LIMIT}
250
+ * unconditionally; an application that wants the same ceiling on its own
251
+ * programmatic reads sets `maxListLimit` when constructing the collection.
252
+ *
253
+ * @private
254
+ */
255
+ private applyListBounds;
177
256
  /**
178
257
  * Convert WHERE clause field names from camelCase to snake_case while preserving operators.
179
258
  * Validates operators and field names to prevent SQL injection and invalid queries.
@@ -197,6 +276,16 @@ export declare class SmrtCollection<ModelType extends SmrtObject> extends SmrtCl
197
276
  private isSensitiveFieldDefinition;
198
277
  private getReadPermissionFieldDefinition;
199
278
  private collectSensitiveFieldNames;
279
+ /**
280
+ * The `collectSensitiveFieldNames` sibling for `@field({ readPermission })`
281
+ * columns (#2367).
282
+ *
283
+ * These are per-caller redacted by `toPublicJSON()` and refused outright by
284
+ * `select`, so an ordering over one leaks comparisons about a value the
285
+ * caller may not read. `list()` has no permission context, so — exactly as
286
+ * `select` does — they are refused unconditionally rather than conditionally.
287
+ */
288
+ private collectReadPermissionFieldNames;
200
289
  private hasCustomPrimaryKey;
201
290
  private isOmittedCustomPrimaryKeySystemField;
202
291
  private resolveProjectionSelect;
@@ -260,7 +349,7 @@ export declare class SmrtCollection<ModelType extends SmrtObject> extends SmrtCl
260
349
  * });
261
350
  * ```
262
351
  */
263
- static create<T extends SmrtCollection<any>>(this: new (options?: SmrtCollectionOptions) => T, options?: SmrtClassOptions): Promise<T>;
352
+ static create<T extends SmrtCollection<any>>(this: new (options?: SmrtCollectionOptions) => T, options?: SmrtCollectionOptions): Promise<T>;
264
353
  /**
265
354
  * Async initialization hook for the collection.
266
355
  *
@@ -337,8 +426,18 @@ export declare class SmrtCollection<ModelType extends SmrtObject> extends SmrtCl
337
426
  * This is a convenience method that avoids N+1 queries when you have
338
427
  * a list of IDs and need to fetch the corresponding records.
339
428
  *
429
+ * The id list is chunked at `IN_LIST_CHUNK_SIZE` (#2367). `collection.list()`
430
+ * expands an array-valued WHERE into one `id IN (?, ?, ...)` clause and does
431
+ * not chunk it, so an unbounded caller-sized array hits the backend's
432
+ * bind-variable ceiling — `SQLITE_MAX_VARIABLE_NUMBER` (999 pre-3.32, 32766
433
+ * after) or PostgreSQL's 65535 — and the whole query fails before it runs.
434
+ * The relationship, junction and hierarchy loaders have chunked at this value
435
+ * for exactly this reason; `listByIds()` is the remaining path that took ids
436
+ * straight from the caller and did not.
437
+ *
340
438
  * @param ids - Array of UUIDs to fetch
341
- * @returns Promise resolving to array of objects (order not guaranteed)
439
+ * @returns Promise resolving to array of objects (order not guaranteed;
440
+ * results are concatenated in chunk order)
342
441
  *
343
442
  * @example
344
443
  * ```typescript
@@ -747,12 +846,36 @@ export declare class SmrtCollection<ModelType extends SmrtObject> extends SmrtCl
747
846
  * model hook without relying on driver queuing and preserves row order.
748
847
  */
749
848
  private hydrateResultRows;
849
+ /**
850
+ * Resolve the `_smrt_contexts.owner_id` key for collection-level memory
851
+ * (#2365).
852
+ *
853
+ * `_smrt_contexts` has no tenant column, so before this hook a tenant-scoped
854
+ * collection's learned memory (`remember()`/`recall()`) was filed under one
855
+ * shared `__collection__` key and leaked across tenants. For a tenant-scoped
856
+ * item class under an active tenant context the key becomes
857
+ * `__collection__:<tenantId>`; everything else (tenancy disabled, no active
858
+ * tenant, system context, non-tenant-scoped class) keeps the shared
859
+ * `__collection__` key. Tenant-keyed memory is strictly isolated — a tenant
860
+ * recall does NOT fall back to the shared key, so memory learned outside a
861
+ * tenant context is invisible inside one (and vice versa).
862
+ *
863
+ * Tenant resolution uses the same core-side trust anchors as the generated
864
+ * REST read scope (#1782): `resolveDispatchTenantScope()` for the active
865
+ * tenant and the triple tenant-scoped-class check covering `@smrt()` config,
866
+ * manifest config, and the tenancy-filled `@TenantScoped()` resolver.
867
+ */
868
+ private resolveCollectionMemoryOwnerId;
750
869
  /**
751
870
  * Remember collection-level context
752
871
  *
753
872
  * Stores context applicable to all instances of this collection type.
754
873
  * Use for patterns that apply to the entire collection (e.g., default parsing strategies).
755
874
  *
875
+ * Under an active tenant context on a tenant-scoped class, memory is keyed
876
+ * per tenant and never shared across tenants (#2365) — see
877
+ * {@link resolveCollectionMemoryOwnerId}.
878
+ *
756
879
  * `expiresAt` is stored on the `_smrt_contexts` row as caller-managed metadata.
757
880
  * {@link recall} and {@link recallAll} do **not** filter on it, so an expired
758
881
  * entry is still returned; filter or delete expired entries yourself.
@@ -879,6 +1002,11 @@ export declare class SmrtCollection<ModelType extends SmrtObject> extends SmrtCl
879
1002
  * Generates an embedding for the query text and finds similar objects
880
1003
  * based on cosine similarity of stored embeddings.
881
1004
  *
1005
+ * Under an active tenant context on a tenant-scoped class, candidates are
1006
+ * restricted to the tenant's rows BEFORE top-K ranking (#2365), so results
1007
+ * are never starved by — and similarity ranks never leak — other tenants'
1008
+ * content.
1009
+ *
882
1010
  * @param query - Text to search for
883
1011
  * @param options - Search options
884
1012
  * @param options.field - Specific field to search (defaults to first embedding
@@ -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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"}