@happyvertical/smrt-core 0.40.62 → 0.40.64
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/AGENTS.md +29 -3
- package/README.md +50 -2
- package/agents/generators.md +22 -0
- package/dist/__typechecks__/collection-read-plan.d.ts +50 -0
- package/dist/__typechecks__/collection-read-plan.d.ts.map +1 -0
- package/dist/collection-read-plan.d.ts +77 -0
- package/dist/collection-read-plan.d.ts.map +1 -0
- package/dist/collection-read-plan.js +55 -0
- package/dist/collection-read-plan.js.map +1 -0
- package/dist/collection.d.ts +12 -1
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +37 -5
- package/dist/collection.js.map +1 -1
- package/dist/consumer-plugin/index.d.ts.map +1 -1
- package/dist/consumer-plugin/index.js +44 -5
- package/dist/consumer-plugin/index.js.map +1 -1
- package/dist/decorators/index.d.ts +8 -2
- package/dist/decorators/index.d.ts.map +1 -1
- package/dist/decorators/index.js.map +1 -1
- package/dist/embeddings/types.d.ts +4 -1
- package/dist/embeddings/types.d.ts.map +1 -1
- package/dist/generators/mcp-emit.d.ts +72 -0
- package/dist/generators/mcp-emit.d.ts.map +1 -0
- package/dist/generators/mcp-emit.js +104 -0
- package/dist/generators/mcp-emit.js.map +1 -0
- package/dist/generators/mcp-runtime-template.d.ts.map +1 -1
- package/dist/generators/mcp-runtime-template.js +7 -1
- package/dist/generators/mcp-runtime-template.js.map +1 -1
- package/dist/generators/mcp.d.ts +17 -3
- package/dist/generators/mcp.d.ts.map +1 -1
- package/dist/generators/mcp.js +43 -21
- package/dist/generators/mcp.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/manifest/generator.d.ts +5 -0
- package/dist/manifest/generator.d.ts.map +1 -1
- package/dist/manifest/generator.js +10 -7
- package/dist/manifest/generator.js.map +1 -1
- package/dist/manifest/static-manifest.js +1 -1
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.js +1 -1
- package/dist/manifest.json +1 -1
- package/dist/object.d.ts +11 -1
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +12 -2
- package/dist/object.js.map +1 -1
- package/dist/registry/class-registration.d.ts.map +1 -1
- package/dist/registry/class-registration.js +53 -3
- package/dist/registry/class-registration.js.map +1 -1
- package/dist/registry/types.d.ts +7 -0
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +5 -4
- package/dist/registry.js.map +1 -1
- package/dist/smrt-knowledge.json +7 -7
- package/dist/system/types.d.ts +4 -1
- package/dist/system/types.d.ts.map +1 -1
- package/dist/utils/scanner-module.d.ts +7 -0
- package/dist/utils/scanner-module.d.ts.map +1 -1
- package/dist/vite-plugin/index.d.ts +7 -0
- package/dist/vite-plugin/index.d.ts.map +1 -1
- package/dist/vite-plugin/index.js +3 -2
- 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 +92 -26
- package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
- package/package.json +11 -13
package/AGENTS.md
CHANGED
|
@@ -41,7 +41,7 @@ refreshes `last_used_at`. Keep semantic search behind the
|
|
|
41
41
|
|
|
42
42
|
```typescript
|
|
43
43
|
await collection.list({
|
|
44
|
-
where: { status: 'active', price
|
|
44
|
+
where: { status: 'active', 'price >': 10 },
|
|
45
45
|
limit: 50, offset: 0, orderBy: 'created_at DESC'
|
|
46
46
|
});
|
|
47
47
|
```
|
|
@@ -59,15 +59,40 @@ an `initialize()` hook may query through the same transaction-bound PostgreSQL
|
|
|
59
59
|
client. Keep this serialization invariant; use `select` when callers need plain
|
|
60
60
|
rows without model hydration.
|
|
61
61
|
|
|
62
|
-
**WHERE operators**: `=`, `>`, `<`, `>=`, `<=`, `!=`, `in`, `not in`, `like
|
|
62
|
+
**WHERE operators**: `=`, `>`, `<`, `>=`, `<=`, `!=`, `in`, `not in`, `like`.
|
|
63
|
+
Arrays auto-detect `IN`. NULL is a value, not an operator: `{ deletedAt: null }`
|
|
64
|
+
renders `IS NULL` and `{ 'deletedAt !=': null }` renders `IS NOT NULL`.
|
|
65
|
+
|
|
66
|
+
This list is the set `@happyvertical/sql`'s `buildWhere` can execute, and
|
|
67
|
+
`convertWhereKeys` accepts nothing outside it — an operator accepted here but
|
|
68
|
+
unknown there fails inside the query builder, after the API said the query was
|
|
69
|
+
valid (#2276). Two entries were removed for that reason and now reject at the
|
|
70
|
+
API boundary: `contains` (never existed in the SQL layer; use `like` with
|
|
71
|
+
explicit wildcards) and dot-notation JSON paths such as `metadata.userId` (never
|
|
72
|
+
rewritten into an extraction expression, so they reached SQL as qualified column
|
|
73
|
+
references). Re-adding either requires the query builder to support it first;
|
|
74
|
+
`src/__tests__/issue-2276-where-contract.test.ts` executes every accepted
|
|
75
|
+
operator against a database to keep the two in step.
|
|
63
76
|
|
|
64
77
|
STI child collections auto-filter by `_meta_type`.
|
|
65
78
|
|
|
79
|
+
## Bounded Collection Read Plans
|
|
80
|
+
|
|
81
|
+
Use `executeCollectionReadPlan()` when one operation needs several independent
|
|
82
|
+
collections. It bounds top-level `collection.list()` concurrency while keeping
|
|
83
|
+
all reads on the normal registry/collection path. Callers must choose an
|
|
84
|
+
explicit positive `maxConcurrency` and pass their normal shared
|
|
85
|
+
`collectionOptions` when database or tenant context matters.
|
|
86
|
+
|
|
87
|
+
The executor deliberately does not compose SQL, cache the plan, or change pool
|
|
88
|
+
defaults. On failure it stops starting queued entries, drains operations already
|
|
89
|
+
in flight, and rethrows the first error.
|
|
90
|
+
|
|
66
91
|
## Object Memory & Semantic Search
|
|
67
92
|
|
|
68
93
|
Two persistence primitives every `SmrtObject`/`SmrtCollection` inherits — load-bearing for learning agents, usable by any object. Full guide: `docs/content/core.md` → "Context Memory System".
|
|
69
94
|
|
|
70
|
-
- **Context memory** (`remember`/`recall`/`recallAll`/`forget`/`forgetScope`, table `_smrt_contexts`): stores any JSON value keyed by `(owner_class, owner_id, scope, key, version)` with a `confidence` score (0–1) and a stored `expiresAt` (metadata — `recall()` does **not** filter expired rows; expiry is caller-managed). `recall()` returns the highest-confidence match with an optional `minConfidence` floor and **opt-in** hierarchical scope fallback (`includeAncestors: true` → `'a/b/c' → 'a/b' → 'a' → 'global'`; default off); `recallAll()` returns a `Map`. Typical use: cache a learned strategy (e.g. a working selector per host) and reuse it across sessions. `success_count`/`failure_count` columns exist for outcome-weighting
|
|
95
|
+
- **Context memory** (`remember`/`recall`/`recallAll`/`forget`/`forgetScope`, table `_smrt_contexts`): stores any JSON value keyed by `(owner_class, owner_id, scope, key, version)` with a `confidence` score (0–1) and a stored `expiresAt` (metadata — `recall()` does **not** filter expired rows; expiry is caller-managed). `recall()` returns the highest-confidence match with an optional `minConfidence` floor and **opt-in** hierarchical scope fallback (`includeAncestors: true` → `'a/b/c' → 'a/b' → 'a' → 'global'`; default off); `recallAll()` returns a `Map`. Typical use: cache a learned strategy (e.g. a working selector per host) and reuse it across sessions. `success_count`/`failure_count` columns exist for outcome-weighting: `SmrtObject.remember()` leaves them untouched, `SmrtCollection.remember()` resets them to zero, and neither recall path updates them. `LearningMemory` is the layer that maintains them (and that does filter expired rows).
|
|
71
96
|
- **Semantic search** (on `SmrtCollection`, table `_smrt_embeddings`): `semanticSearch(query)`, `findSimilar(object)`, `findSimilarToEmbedding(vector)` — cosine ranking over embeddings of the fields declared in `@smrt({ embeddings })`. Native pgvector/HNSW when configured, in-memory `CosineSimilarity` fallback otherwise; default local model `Xenova/bge-base-en-v1.5` (768-dim) or AI `text-embedding-3-small`. Hits hydrate via `list({ 'id in': … })`, so `@TenantScoped` isolation applies to results.
|
|
72
97
|
|
|
73
98
|
## @smrt() Decorator Options
|
|
@@ -173,3 +198,4 @@ emitDecoratorMetadata: true`.
|
|
|
173
198
|
must abort before adapting partial scan results. A syntax error or unresolved
|
|
174
199
|
`@smrt()` config spread cannot be allowed to emit a default-open manifest.
|
|
175
200
|
- **Vite plugin loads scanner from `dist/` first**: `src/vite-plugin/import-build-aware.ts` prefers `dist/` when it exists on disk; it only falls back to `src/` on fresh clones. So if you edit `src/scanner/*.ts` or `src/schema/generator.ts` and want those edits reflected in consumer manifest generation, you must rebuild (`pnpm build` or have `pnpm dev` / `pnpm build:watch` running in core). This is intentional — sniffing `.ts` vs `.js` via `import.meta.url` was non-deterministic under tsx and broke 12–13 publishes (#1139).
|
|
201
|
+
- **Bundled registry ownership**: flattened production bundles can rewrite constructor names and make decorator-time stack inference attribute provider code to the consumer. Generated registration repairs identity only from the exact imported constructor plus an explicit package and isolated one-object manifest; never infer ownership from output paths, simple names, or table names. Distinct packages may export the same simple name under qualified keys. The production-consumer gate lives in `packages/bundle-gate/src/__tests__/registry-identity.spec.ts` (#2308).
|
package/README.md
CHANGED
|
@@ -67,6 +67,44 @@ const results = await products.list({
|
|
|
67
67
|
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
+
### Bounded multi-collection reads
|
|
71
|
+
|
|
72
|
+
When one request needs several independent collections, use a keyed read plan
|
|
73
|
+
instead of an unbounded `Promise.all`. Every entry still uses the normal
|
|
74
|
+
collection `list()` path, but only the requested number of operations run at
|
|
75
|
+
once:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import {
|
|
79
|
+
executeCollectionReadPlan,
|
|
80
|
+
type SmrtCollectionReadPlanEntry,
|
|
81
|
+
} from '@happyvertical/smrt-core';
|
|
82
|
+
|
|
83
|
+
const categories: SmrtCollectionReadPlanEntry<Category> = {
|
|
84
|
+
className: 'Category',
|
|
85
|
+
options: { orderBy: 'name ASC' },
|
|
86
|
+
};
|
|
87
|
+
const products: SmrtCollectionReadPlanEntry<Product> = {
|
|
88
|
+
className: 'Product',
|
|
89
|
+
options: { where: { isPublished: true }, orderBy: 'name ASC' },
|
|
90
|
+
};
|
|
91
|
+
const records = await executeCollectionReadPlan(
|
|
92
|
+
{
|
|
93
|
+
categories,
|
|
94
|
+
products,
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
collectionOptions: { db: 'file:products.db' },
|
|
98
|
+
maxConcurrency: 2,
|
|
99
|
+
},
|
|
100
|
+
);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`maxConcurrency` is required and must be a positive integer. If an entry
|
|
104
|
+
fails, the executor starts no further queued entries, waits for already-running
|
|
105
|
+
entries to settle, and rethrows the first error. Read plans do not compose SQL,
|
|
106
|
+
cache whole-plan results, or change database pool defaults.
|
|
107
|
+
|
|
70
108
|
### Generate metadata and migrate
|
|
71
109
|
|
|
72
110
|
Configure Vite 8's Oxc decorator transform and point `smrtPlugin()` at the
|
|
@@ -119,7 +157,8 @@ src/routes/api/**/+server.ts
|
|
|
119
157
|
!src/routes/api/v1/**/+server.ts
|
|
120
158
|
```
|
|
121
159
|
|
|
122
|
-
Migration takes the recognized SMRT
|
|
160
|
+
Migration takes the recognized `# SMRT auto-generated routes (from Vite plugin)`
|
|
161
|
+
header — matched as a whole line — plus the contiguous run of
|
|
123
162
|
recursive `+server.ts` wildcards directly beneath it, negations included. The
|
|
124
163
|
run is matched by shape rather than against your current `routesDir`, so a
|
|
125
164
|
project that moved `routesDir` after adopting the plugin still migrates, and
|
|
@@ -372,9 +411,12 @@ Generators produce OpenAPI REST endpoints, Commander CLI commands, and MCP serve
|
|
|
372
411
|
|
|
373
412
|
Long-running item actions may opt into the experimental
|
|
374
413
|
`io.modelcontextprotocol/tasks` extension. Tasks are disabled by default; list
|
|
375
|
-
the action names explicitly and
|
|
414
|
+
the action names explicitly, and — if the class restricts what the job runner
|
|
415
|
+
may dispatch — include the action in that allowlist:
|
|
376
416
|
|
|
377
417
|
```typescript
|
|
418
|
+
import { backgroundEligible } from '@happyvertical/smrt-jobs';
|
|
419
|
+
|
|
378
420
|
@smrt({
|
|
379
421
|
mcp: { include: ['generateReport'], tasks: ['generateReport'] },
|
|
380
422
|
})
|
|
@@ -384,6 +426,12 @@ class Report extends SmrtObject {
|
|
|
384
426
|
}
|
|
385
427
|
```
|
|
386
428
|
|
|
429
|
+
`@backgroundEligible()` is owned and enforced by `@happyvertical/smrt-jobs`, not
|
|
430
|
+
by this package. It is **restrictive**: a class with no marked methods lets
|
|
431
|
+
`TaskRunner` dispatch any of its methods, and the first marked method turns the
|
|
432
|
+
set into an exhaustive allowlist that excludes every other method on the class.
|
|
433
|
+
Use it to narrow the reachable surface, and mark every method you dispatch.
|
|
434
|
+
|
|
387
435
|
The generated MCP server advertises the extension only when at least one task
|
|
388
436
|
action is enabled. A task-aware client can request the action as a durable job,
|
|
389
437
|
then use `tasks/get`, `tasks/update`, and `tasks/cancel` to observe or control
|
package/agents/generators.md
CHANGED
|
@@ -27,6 +27,28 @@ The web module also emits a build-time **`manifestHash`** constant (#1764): `com
|
|
|
27
27
|
|
|
28
28
|
Per-field web emission (#2046): `buildWebFieldDefinitions` carries `description` (from `@field({ description })`) and sanitized `ui` hints (from `@field({ ui: { basic, group, order, locked } })`, read off the manifest `_meta.ui` bag through per-key type guards) into each emitted field definition, and `buildWebToolDescriptors` threads the same `description` into browser MCP tool schemas. `sensitive`/`transient` fields are excluded from emission entirely, so their descriptions never ship. Both keys are conditional, so hint-less schemas emit byte-identical definitions (and hashes) as before; adding a description/ui hint changes the manifest hash — deliberate over-invalidation, harmless per the #1764 contract.
|
|
29
29
|
|
|
30
|
+
## Generated MCP server output language
|
|
31
|
+
|
|
32
|
+
`MCPGenerator` builds every file as TypeScript, so the requested `outputPath`
|
|
33
|
+
extension decides what is written (#2279). `.ts`/`.mts` targets keep the source
|
|
34
|
+
verbatim for `tsx` or Node type stripping — which is why the generated source
|
|
35
|
+
must stay erasable-syntax-only (no parameter properties, enums, or namespaces).
|
|
36
|
+
Every other target (`.smrt/mcp-server/index.js` by default) is transpiled to
|
|
37
|
+
JavaScript with the `typescript` dependency before writing, because the printed
|
|
38
|
+
run script and the generated `claude-config.example.json` both invoke it with
|
|
39
|
+
plain `node`. A `.cjs`/`.cts` target is rejected outright: generated servers are
|
|
40
|
+
ES modules. `src/generators/mcp-emit.ts` owns those decisions — do not
|
|
41
|
+
reintroduce a bare `writeFile` of generated source.
|
|
42
|
+
|
|
43
|
+
Modular output writes `config`, `tools/index`, and `handlers/index` with the
|
|
44
|
+
entry point's own extension, and emits the entry's relative import specifiers
|
|
45
|
+
with that same extension, so the files it imports both exist and load with the
|
|
46
|
+
same module semantics — an `.mjs` entry gets `.mjs` siblings, not `.js` ones a
|
|
47
|
+
CommonJS package would then parse as CommonJS. The entry is written at the
|
|
48
|
+
requested path rather than a hardcoded `index.js`.
|
|
49
|
+
Generated code also has to be valid in an ES module: `arguments` is not a legal
|
|
50
|
+
binding name there, however convenient it reads.
|
|
51
|
+
|
|
30
52
|
## Custom-action contract
|
|
31
53
|
|
|
32
54
|
`resolveCustomActionMetadata()` is the common discovery and invocation contract
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { SmrtCollectionReadPlan, SmrtCollectionReadPlanEntry, SmrtCollectionReadPlanResult } from '../collection-read-plan';
|
|
2
|
+
import { SmrtObject } from '../object';
|
|
3
|
+
type Equal<Left, Right> = (<Value>() => Value extends Left ? 1 : 2) extends <Value>() => Value extends Right ? 1 : 2 ? true : false;
|
|
4
|
+
type Expect<Value extends true> = Value;
|
|
5
|
+
type ReadPlanTypeProbe = SmrtObject & {
|
|
6
|
+
name: string;
|
|
7
|
+
};
|
|
8
|
+
declare const plain: SmrtCollectionReadPlanEntry<ReadPlanTypeProbe>;
|
|
9
|
+
declare const projected: SmrtCollectionReadPlanEntry<ReadPlanTypeProbe, {
|
|
10
|
+
select: readonly ['id', 'name'];
|
|
11
|
+
}>;
|
|
12
|
+
type Result = SmrtCollectionReadPlanResult<{
|
|
13
|
+
plain: typeof plain;
|
|
14
|
+
projected: typeof projected;
|
|
15
|
+
}>;
|
|
16
|
+
type PlainResultIsTyped = Expect<Equal<Result['plain'], ReadPlanTypeProbe[]>>;
|
|
17
|
+
type ProjectedResultIsTyped = Expect<Equal<Result['projected'], {
|
|
18
|
+
id: string | null | undefined;
|
|
19
|
+
name: string;
|
|
20
|
+
}[]>>;
|
|
21
|
+
type InvalidOptionsAreRejected = Expect<Equal<{
|
|
22
|
+
limit: string;
|
|
23
|
+
} extends NonNullable<SmrtCollectionReadPlan[string]['options']> ? true : false, false>>;
|
|
24
|
+
type ProjectionIncludeIsRejected = Expect<Equal<{
|
|
25
|
+
select: readonly ['id'];
|
|
26
|
+
include: string[];
|
|
27
|
+
} extends NonNullable<SmrtCollectionReadPlan[string]['options']> ? true : false, false>>;
|
|
28
|
+
type MixedOptions = {
|
|
29
|
+
limit: number;
|
|
30
|
+
select?: undefined;
|
|
31
|
+
} | {
|
|
32
|
+
select: readonly ['id'];
|
|
33
|
+
include?: never;
|
|
34
|
+
};
|
|
35
|
+
type MixedEntry = SmrtCollectionReadPlanEntry<ReadPlanTypeProbe, MixedOptions>;
|
|
36
|
+
type MixedResult = SmrtCollectionReadPlanResult<{
|
|
37
|
+
mixed: MixedEntry;
|
|
38
|
+
}>;
|
|
39
|
+
type MixedResultIsSound = Expect<Equal<MixedResult['mixed'], ReadPlanTypeProbe[] | {
|
|
40
|
+
id: string | null | undefined;
|
|
41
|
+
}[]>>;
|
|
42
|
+
export type CollectionReadPlanTypeAssertions = [
|
|
43
|
+
PlainResultIsTyped,
|
|
44
|
+
ProjectedResultIsTyped,
|
|
45
|
+
InvalidOptionsAreRejected,
|
|
46
|
+
ProjectionIncludeIsRejected,
|
|
47
|
+
MixedResultIsSound
|
|
48
|
+
];
|
|
49
|
+
export {};
|
|
50
|
+
//# sourceMappingURL=collection-read-plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection-read-plan.d.ts","sourceRoot":"","sources":["../../src/__typechecks__/collection-read-plan.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,sBAAsB,EAC3B,KAAK,2BAA2B,EAChC,KAAK,4BAA4B,EAClC,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE5C,KAAK,KAAK,CAAC,IAAI,EAAE,KAAK,IACpB,CAAC,CAAC,KAAK,OAAO,KAAK,SAAS,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,SAAS,CAChD,KAAK,OACA,KAAK,SAAS,KAAK,GAAG,CAAC,GAAG,CAAC,GAC9B,IAAI,GACJ,KAAK,CAAC;AACZ,KAAK,MAAM,CAAC,KAAK,SAAS,IAAI,IAAI,KAAK,CAAC;AAExC,KAAK,iBAAiB,GAAG,UAAU,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvD,QAAA,MAAM,KAAK,EAAE,2BAA2B,CAAC,iBAAiB,CAEzD,CAAC;AACF,QAAA,MAAM,SAAS,EAAE,2BAA2B,CAC1C,iBAAiB,EACjB;IAAE,MAAM,EAAE,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;CAAE,CAIpC,CAAC;AAEF,KAAK,MAAM,GAAG,4BAA4B,CAAC;IACzC,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,SAAS,EAAE,OAAO,SAAS,CAAC;CAC7B,CAAC,CAAC;AACH,KAAK,kBAAkB,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC;AAC9E,KAAK,sBAAsB,GAAG,MAAM,CAClC,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE;IAAE,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,CAC9E,CAAC;AACF,KAAK,yBAAyB,GAAG,MAAM,CACrC,KAAK,CACH;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,SAAS,WAAW,CACnC,sBAAsB,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,CAC1C,GACG,IAAI,GACJ,KAAK,EACT,KAAK,CACN,CACF,CAAC;AACF,KAAK,2BAA2B,GAAG,MAAM,CACvC,KAAK,CACH;IACE,MAAM,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;IACxB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,SAAS,WAAW,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC,GAC5D,IAAI,GACJ,KAAK,EACT,KAAK,CACN,CACF,CAAC;AAEF,KAAK,YAAY,GACb;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,SAAS,CAAA;CAAE,GACrC;IAAE,MAAM,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC;AACjD,KAAK,UAAU,GAAG,2BAA2B,CAAC,iBAAiB,EAAE,YAAY,CAAC,CAAC;AAK/E,KAAK,WAAW,GAAG,4BAA4B,CAAC;IAAE,KAAK,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC;AACvE,KAAK,kBAAkB,GAAG,MAAM,CAC9B,KAAK,CACH,WAAW,CAAC,OAAO,CAAC,EACpB,iBAAiB,EAAE,GAAG;IAAE,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;CAAE,EAAE,CAC1D,CACF,CAAC;AAiCF,MAAM,MAAM,gCAAgC,GAAG;IAC7C,kBAAkB;IAClB,sBAAsB;IACtB,yBAAyB;IACzB,2BAA2B;IAC3B,kBAAkB;CACnB,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { SmrtClassOptions } from './class';
|
|
2
|
+
import { SmrtListOptions, SmrtSelectedRow, SmrtSelectField } from './collection';
|
|
3
|
+
import { SmrtObject } from './object';
|
|
4
|
+
type DynamicSmrtObject = SmrtObject & Record<string, unknown>;
|
|
5
|
+
type SmrtHydratedCollectionReadOptions<ModelType extends SmrtObject> = Omit<SmrtListOptions<ModelType>, 'select'> & {
|
|
6
|
+
select?: undefined;
|
|
7
|
+
};
|
|
8
|
+
type SmrtProjectedCollectionReadOptions<ModelType extends SmrtObject> = Omit<SmrtListOptions<ModelType>, 'select' | 'include'> & {
|
|
9
|
+
select: readonly SmrtSelectField<ModelType>[];
|
|
10
|
+
include?: never;
|
|
11
|
+
};
|
|
12
|
+
type SmrtCollectionReadOptions<ModelType extends SmrtObject> = SmrtHydratedCollectionReadOptions<ModelType> | SmrtProjectedCollectionReadOptions<ModelType>;
|
|
13
|
+
type DynamicSmrtListOptions = SmrtCollectionReadOptions<DynamicSmrtObject>;
|
|
14
|
+
declare const smrtCollectionReadPlanModel: unique symbol;
|
|
15
|
+
type SmrtCollectionReadPlanEntryOptions<ModelType extends SmrtObject, ListOptions extends SmrtCollectionReadOptions<ModelType> | undefined> = ListOptions extends SmrtProjectedCollectionReadOptions<ModelType> ? {
|
|
16
|
+
/** Projection options forwarded unchanged to `SmrtCollection.list()`. */
|
|
17
|
+
options: ListOptions;
|
|
18
|
+
} : {
|
|
19
|
+
/** Standard hydrated-list options forwarded unchanged to `SmrtCollection.list()`. */
|
|
20
|
+
options?: ListOptions;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* One independent collection read in a bounded read plan.
|
|
24
|
+
*
|
|
25
|
+
* `ModelType` is optional for dynamic registries. Consumers that know the
|
|
26
|
+
* model type can annotate an entry to retain model/projection result typing.
|
|
27
|
+
*/
|
|
28
|
+
export type SmrtCollectionReadPlanEntry<ModelType extends SmrtObject = SmrtObject, ListOptions extends SmrtCollectionReadOptions<ModelType> | undefined = SmrtHydratedCollectionReadOptions<ModelType>> = {
|
|
29
|
+
/** Registered SMRT object or collection name. */
|
|
30
|
+
className: string;
|
|
31
|
+
/** @internal Retains the model type for keyed result inference. */
|
|
32
|
+
readonly [smrtCollectionReadPlanModel]?: ModelType;
|
|
33
|
+
} & SmrtCollectionReadPlanEntryOptions<ModelType, ListOptions>;
|
|
34
|
+
/** A keyed group of independent collection reads. */
|
|
35
|
+
export type SmrtCollectionReadPlan = Record<string, {
|
|
36
|
+
className: string;
|
|
37
|
+
options?: DynamicSmrtListOptions;
|
|
38
|
+
}>;
|
|
39
|
+
type SmrtCollectionReadPlanEntryModel<Entry> = Entry extends {
|
|
40
|
+
readonly [smrtCollectionReadPlanModel]?: infer ModelType;
|
|
41
|
+
} ? ModelType extends SmrtObject ? ModelType : SmrtObject : SmrtObject;
|
|
42
|
+
type SmrtCollectionReadPlanEntryResult<Entry> = Entry extends {
|
|
43
|
+
options: {
|
|
44
|
+
select: infer Select;
|
|
45
|
+
};
|
|
46
|
+
} ? Select extends readonly SmrtSelectField<SmrtCollectionReadPlanEntryModel<Entry>>[] ? SmrtSelectedRow<SmrtCollectionReadPlanEntryModel<Entry>, Select>[] : never : SmrtCollectionReadPlanEntryModel<Entry>[];
|
|
47
|
+
/** Results retain the exact keys declared by the input plan. */
|
|
48
|
+
export type SmrtCollectionReadPlanResult<Plan extends SmrtCollectionReadPlan> = {
|
|
49
|
+
[Key in keyof Plan]: SmrtCollectionReadPlanEntryResult<Plan[Key]>;
|
|
50
|
+
};
|
|
51
|
+
export interface ExecuteCollectionReadPlanOptions {
|
|
52
|
+
/**
|
|
53
|
+
* Maximum number of top-level `collection.list()` operations in flight.
|
|
54
|
+
* Must be a positive integer and is intentionally required so callers make
|
|
55
|
+
* workload policy explicit.
|
|
56
|
+
*/
|
|
57
|
+
maxConcurrency: number;
|
|
58
|
+
/** Normal options used to resolve every collection in the plan. */
|
|
59
|
+
collectionOptions?: SmrtClassOptions;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Execute independent collection reads without unbounded database fan-out.
|
|
63
|
+
*
|
|
64
|
+
* Each entry resolves through `ObjectRegistry.getCollection()` and calls the
|
|
65
|
+
* collection's public `list()` method, preserving interceptors, tenancy, STI,
|
|
66
|
+
* hydration, eager loading, projections, and opt-in collection caching.
|
|
67
|
+
*
|
|
68
|
+
* On failure, no additional queued entry is started. Operations that were
|
|
69
|
+
* already in flight are allowed to settle before the first error is rethrown,
|
|
70
|
+
* so the function never leaves detached database work behind.
|
|
71
|
+
*
|
|
72
|
+
* This function does not compose SQL, cache the plan, or change database pool
|
|
73
|
+
* defaults. It only bounds top-level list-operation concurrency.
|
|
74
|
+
*/
|
|
75
|
+
export declare function executeCollectionReadPlan<const Plan extends SmrtCollectionReadPlan>(plan: Plan, options: ExecuteCollectionReadPlanOptions): Promise<SmrtCollectionReadPlanResult<Plan>>;
|
|
76
|
+
export {};
|
|
77
|
+
//# sourceMappingURL=collection-read-plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection-read-plan.d.ts","sourceRoot":"","sources":["../src/collection-read-plan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ObjectRegistry } from "./registry.js";
|
|
2
|
+
//#region src/collection-read-plan.ts
|
|
3
|
+
async function listCollection(collection, options) {
|
|
4
|
+
if (options?.select !== void 0) return await collection.list(options);
|
|
5
|
+
return await collection.list(options);
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Execute independent collection reads without unbounded database fan-out.
|
|
9
|
+
*
|
|
10
|
+
* Each entry resolves through `ObjectRegistry.getCollection()` and calls the
|
|
11
|
+
* collection's public `list()` method, preserving interceptors, tenancy, STI,
|
|
12
|
+
* hydration, eager loading, projections, and opt-in collection caching.
|
|
13
|
+
*
|
|
14
|
+
* On failure, no additional queued entry is started. Operations that were
|
|
15
|
+
* already in flight are allowed to settle before the first error is rethrown,
|
|
16
|
+
* so the function never leaves detached database work behind.
|
|
17
|
+
*
|
|
18
|
+
* This function does not compose SQL, cache the plan, or change database pool
|
|
19
|
+
* defaults. It only bounds top-level list-operation concurrency.
|
|
20
|
+
*/
|
|
21
|
+
async function executeCollectionReadPlan(plan, options) {
|
|
22
|
+
if (!Number.isInteger(options.maxConcurrency) || options.maxConcurrency <= 0) throw new RangeError("maxConcurrency must be a positive integer");
|
|
23
|
+
const entries = Object.entries(plan);
|
|
24
|
+
if (entries.length === 0) return Object.fromEntries([]);
|
|
25
|
+
let nextIndex = 0;
|
|
26
|
+
let failed = false;
|
|
27
|
+
let firstError;
|
|
28
|
+
const values = new Array(entries.length);
|
|
29
|
+
const runWorker = async () => {
|
|
30
|
+
while (!failed) {
|
|
31
|
+
const entryIndex = nextIndex;
|
|
32
|
+
nextIndex += 1;
|
|
33
|
+
if (entryIndex >= entries.length) return;
|
|
34
|
+
const [, entry] = entries[entryIndex];
|
|
35
|
+
try {
|
|
36
|
+
const value = await listCollection(await ObjectRegistry.getCollection(entry.className, options.collectionOptions), entry.options);
|
|
37
|
+
values[entryIndex] = value;
|
|
38
|
+
} catch (error) {
|
|
39
|
+
if (!failed) {
|
|
40
|
+
failed = true;
|
|
41
|
+
firstError = error;
|
|
42
|
+
}
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const workerCount = Math.min(options.maxConcurrency, entries.length);
|
|
48
|
+
await Promise.all(Array.from({ length: workerCount }, runWorker));
|
|
49
|
+
if (failed) throw firstError;
|
|
50
|
+
return Object.fromEntries(entries.map(([key], index) => [key, values[index]]));
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
export { executeCollectionReadPlan };
|
|
54
|
+
|
|
55
|
+
//# sourceMappingURL=collection-read-plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection-read-plan.js","names":[],"sources":["../src/collection-read-plan.ts"],"sourcesContent":["import type { SmrtClassOptions } from './class';\nimport type {\n SmrtCollection,\n SmrtListOptions,\n SmrtSelectedRow,\n SmrtSelectField,\n} from './collection';\nimport type { SmrtObject } from './object';\nimport { ObjectRegistry } from './registry';\n\ntype DynamicSmrtObject = SmrtObject & Record<string, unknown>;\ntype SmrtHydratedCollectionReadOptions<ModelType extends SmrtObject> = Omit<\n SmrtListOptions<ModelType>,\n 'select'\n> & {\n select?: undefined;\n};\ntype SmrtProjectedCollectionReadOptions<ModelType extends SmrtObject> = Omit<\n SmrtListOptions<ModelType>,\n 'select' | 'include'\n> & {\n select: readonly SmrtSelectField<ModelType>[];\n include?: never;\n};\ntype SmrtCollectionReadOptions<ModelType extends SmrtObject> =\n | SmrtHydratedCollectionReadOptions<ModelType>\n | SmrtProjectedCollectionReadOptions<ModelType>;\ntype DynamicSmrtListOptions = SmrtCollectionReadOptions<DynamicSmrtObject>;\ndeclare const smrtCollectionReadPlanModel: unique symbol;\n\ntype SmrtCollectionReadPlanEntryOptions<\n ModelType extends SmrtObject,\n ListOptions extends SmrtCollectionReadOptions<ModelType> | undefined,\n> =\n ListOptions extends SmrtProjectedCollectionReadOptions<ModelType>\n ? {\n /** Projection options forwarded unchanged to `SmrtCollection.list()`. */\n options: ListOptions;\n }\n : {\n /** Standard hydrated-list options forwarded unchanged to `SmrtCollection.list()`. */\n options?: ListOptions;\n };\n\n/**\n * One independent collection read in a bounded read plan.\n *\n * `ModelType` is optional for dynamic registries. Consumers that know the\n * model type can annotate an entry to retain model/projection result typing.\n */\nexport type SmrtCollectionReadPlanEntry<\n ModelType extends SmrtObject = SmrtObject,\n ListOptions extends\n | SmrtCollectionReadOptions<ModelType>\n | undefined = SmrtHydratedCollectionReadOptions<ModelType>,\n> = {\n /** Registered SMRT object or collection name. */\n className: string;\n /** @internal Retains the model type for keyed result inference. */\n readonly [smrtCollectionReadPlanModel]?: ModelType;\n} & SmrtCollectionReadPlanEntryOptions<ModelType, ListOptions>;\n\n/** A keyed group of independent collection reads. */\nexport type SmrtCollectionReadPlan = Record<\n string,\n {\n className: string;\n options?: DynamicSmrtListOptions;\n }\n>;\n\ntype SmrtCollectionReadPlanEntryModel<Entry> = Entry extends {\n readonly [smrtCollectionReadPlanModel]?: infer ModelType;\n}\n ? ModelType extends SmrtObject\n ? ModelType\n : SmrtObject\n : SmrtObject;\n\ntype SmrtCollectionReadPlanEntryResult<Entry> = Entry extends {\n options: { select: infer Select };\n}\n ? Select extends readonly SmrtSelectField<\n SmrtCollectionReadPlanEntryModel<Entry>\n >[]\n ? SmrtSelectedRow<SmrtCollectionReadPlanEntryModel<Entry>, Select>[]\n : never\n : SmrtCollectionReadPlanEntryModel<Entry>[];\n\n/** Results retain the exact keys declared by the input plan. */\nexport type SmrtCollectionReadPlanResult<Plan extends SmrtCollectionReadPlan> =\n {\n [Key in keyof Plan]: SmrtCollectionReadPlanEntryResult<Plan[Key]>;\n };\n\nexport interface ExecuteCollectionReadPlanOptions {\n /**\n * Maximum number of top-level `collection.list()` operations in flight.\n * Must be a positive integer and is intentionally required so callers make\n * workload policy explicit.\n */\n maxConcurrency: number;\n /** Normal options used to resolve every collection in the plan. */\n collectionOptions?: SmrtClassOptions;\n}\n\nasync function listCollection(\n collection: SmrtCollection<DynamicSmrtObject>,\n options: DynamicSmrtListOptions | undefined,\n): Promise<unknown[]> {\n if (options?.select !== undefined) {\n return await collection.list(\n options as DynamicSmrtListOptions & {\n select: readonly SmrtSelectField<DynamicSmrtObject>[];\n include?: never;\n },\n );\n }\n\n return await collection.list(\n options as\n | (Omit<DynamicSmrtListOptions, 'select'> & {\n select?: undefined;\n })\n | undefined,\n );\n}\n\n/**\n * Execute independent collection reads without unbounded database fan-out.\n *\n * Each entry resolves through `ObjectRegistry.getCollection()` and calls the\n * collection's public `list()` method, preserving interceptors, tenancy, STI,\n * hydration, eager loading, projections, and opt-in collection caching.\n *\n * On failure, no additional queued entry is started. Operations that were\n * already in flight are allowed to settle before the first error is rethrown,\n * so the function never leaves detached database work behind.\n *\n * This function does not compose SQL, cache the plan, or change database pool\n * defaults. It only bounds top-level list-operation concurrency.\n */\nexport async function executeCollectionReadPlan<\n const Plan extends SmrtCollectionReadPlan,\n>(\n plan: Plan,\n options: ExecuteCollectionReadPlanOptions,\n): Promise<SmrtCollectionReadPlanResult<Plan>> {\n if (\n !Number.isInteger(options.maxConcurrency) ||\n options.maxConcurrency <= 0\n ) {\n throw new RangeError('maxConcurrency must be a positive integer');\n }\n\n const entries = Object.entries(plan) as [keyof Plan, Plan[keyof Plan]][];\n\n if (entries.length === 0) {\n return Object.fromEntries(\n [],\n ) as unknown as SmrtCollectionReadPlanResult<Plan>;\n }\n\n let nextIndex = 0;\n let failed = false;\n let firstError: unknown;\n const values: unknown[][] = new Array(entries.length);\n\n const runWorker = async (): Promise<void> => {\n while (!failed) {\n const entryIndex = nextIndex;\n nextIndex += 1;\n if (entryIndex >= entries.length) return;\n\n const [, entry] = entries[entryIndex];\n\n try {\n const collection =\n await ObjectRegistry.getCollection<DynamicSmrtObject>(\n entry.className,\n options.collectionOptions,\n );\n const value = await listCollection(collection, entry.options);\n values[entryIndex] = value;\n } catch (error) {\n if (!failed) {\n failed = true;\n firstError = error;\n }\n return;\n }\n }\n };\n\n const workerCount = Math.min(options.maxConcurrency, entries.length);\n await Promise.all(Array.from({ length: workerCount }, runWorker));\n\n if (failed) throw firstError;\n return Object.fromEntries(\n entries.map(([key], index) => [key, values[index]]),\n ) as unknown as SmrtCollectionReadPlanResult<Plan>;\n}\n"],"mappings":";;AA0GA,eAAe,eACb,YACA,SACoB;CACpB,IAAI,SAAS,WAAW,KAAA,GACtB,OAAO,MAAM,WAAW,KACtB,OAIF;CAGF,OAAO,MAAM,WAAW,KACtB,OAKF;AACF;;;;;;;;;;;;;;;AAgBA,eAAsB,0BAGpB,MACA,SAC6C;CAC7C,IACE,CAAC,OAAO,UAAU,QAAQ,cAAc,KACxC,QAAQ,kBAAkB,GAE1B,MAAM,IAAI,WAAW,2CAA2C;CAGlE,MAAM,UAAU,OAAO,QAAQ,IAAI;CAEnC,IAAI,QAAQ,WAAW,GACrB,OAAO,OAAO,YACZ,CAAC,CACH;CAGF,IAAI,YAAY;CAChB,IAAI,SAAS;CACb,IAAI;CACJ,MAAM,SAAsB,IAAI,MAAM,QAAQ,MAAM;CAEpD,MAAM,YAAY,YAA2B;EAC3C,OAAO,CAAC,QAAQ;GACd,MAAM,aAAa;GACnB,aAAa;GACb,IAAI,cAAc,QAAQ,QAAQ;GAElC,MAAM,GAAG,SAAS,QAAQ;GAE1B,IAAI;IAMF,MAAM,QAAQ,MAAM,eAAe,MAJ3B,eAAe,cACnB,MAAM,WACN,QAAQ,iBACV,GAC6C,MAAM,OAAO;IAC5D,OAAO,cAAc;GACvB,SAAS,OAAO;IACd,IAAI,CAAC,QAAQ;KACX,SAAS;KACT,aAAa;IACf;IACA;GACF;EACF;CACF;CAEA,MAAM,cAAc,KAAK,IAAI,QAAQ,gBAAgB,QAAQ,MAAM;CACnE,MAAM,QAAQ,IAAI,MAAM,KAAK,EAAE,QAAQ,YAAY,GAAG,SAAS,CAAC;CAEhE,IAAI,QAAQ,MAAM;CAClB,OAAO,OAAO,YACZ,QAAQ,KAAK,CAAC,MAAM,UAAU,CAAC,KAAK,OAAO,MAAM,CAAC,CACpD;AACF"}
|
package/dist/collection.d.ts
CHANGED
|
@@ -753,6 +753,10 @@ export declare class SmrtCollection<ModelType extends SmrtObject> extends SmrtCl
|
|
|
753
753
|
* Stores context applicable to all instances of this collection type.
|
|
754
754
|
* Use for patterns that apply to the entire collection (e.g., default parsing strategies).
|
|
755
755
|
*
|
|
756
|
+
* `expiresAt` is stored on the `_smrt_contexts` row as caller-managed metadata.
|
|
757
|
+
* {@link recall} and {@link recallAll} do **not** filter on it, so an expired
|
|
758
|
+
* entry is still returned; filter or delete expired entries yourself.
|
|
759
|
+
*
|
|
756
760
|
* @param options - Context options
|
|
757
761
|
* @returns Promise that resolves when context is stored
|
|
758
762
|
* @example
|
|
@@ -877,7 +881,9 @@ export declare class SmrtCollection<ModelType extends SmrtObject> extends SmrtCl
|
|
|
877
881
|
*
|
|
878
882
|
* @param query - Text to search for
|
|
879
883
|
* @param options - Search options
|
|
880
|
-
* @param options.field - Specific field to search (defaults to first embedding
|
|
884
|
+
* @param options.field - Specific field to search (defaults to first embedding
|
|
885
|
+
* field). Any configured embedding field, or the configured
|
|
886
|
+
* `combinedField.name`, is accepted.
|
|
881
887
|
* @param options.limit - Maximum results to return (default: 10)
|
|
882
888
|
* @param options.minSimilarity - Minimum similarity threshold 0-1 (default: 0)
|
|
883
889
|
* @param options.where - Additional WHERE filters to apply
|
|
@@ -893,6 +899,11 @@ export declare class SmrtCollection<ModelType extends SmrtObject> extends SmrtCl
|
|
|
893
899
|
* for (const article of results) {
|
|
894
900
|
* console.log(`${article.title} (similarity: ${article._similarity})`);
|
|
895
901
|
* }
|
|
902
|
+
*
|
|
903
|
+
* // Search the combined vector built from `combinedField.template`
|
|
904
|
+
* const combined = await articles.semanticSearch('machine learning trends', {
|
|
905
|
+
* field: 'content'
|
|
906
|
+
* });
|
|
896
907
|
* ```
|
|
897
908
|
*/
|
|
898
909
|
semanticSearch(query: string, options?: {
|
package/dist/collection.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAEL,KAAK,qBAAqB,EAQ3B,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"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"}
|
package/dist/collection.js
CHANGED
|
@@ -33,6 +33,22 @@ function resolveMetaTypeInWhere(where) {
|
|
|
33
33
|
return where;
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
|
+
* Field names that carry a stored embedding vector for a class (Issue #2281)
|
|
37
|
+
*
|
|
38
|
+
* `SmrtObject.generateEmbeddings()` writes one vector per configured field and,
|
|
39
|
+
* when `combinedField` is set, an extra vector under `combinedField.name`. Any
|
|
40
|
+
* of those names is a legitimate target for the collection's semantic-search
|
|
41
|
+
* methods, so validation and error messages should describe the whole set.
|
|
42
|
+
*
|
|
43
|
+
* @param config - Resolved embedding configuration for the class
|
|
44
|
+
* @returns Configured field names, plus the combined field name when distinct
|
|
45
|
+
*/
|
|
46
|
+
function getSearchableEmbeddingFields(config) {
|
|
47
|
+
const combinedName = config.combinedField?.name;
|
|
48
|
+
if (!combinedName || config.fields.includes(combinedName)) return config.fields;
|
|
49
|
+
return [...config.fields, combinedName];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
36
52
|
* Typed CRUD collection for a specific `SmrtObject` subclass.
|
|
37
53
|
*
|
|
38
54
|
* Each concrete collection pairs with exactly one model class via the required
|
|
@@ -111,9 +127,9 @@ var SmrtCollection = class SmrtCollection extends SmrtClass {
|
|
|
111
127
|
"!=",
|
|
112
128
|
"in",
|
|
113
129
|
"not in",
|
|
114
|
-
"like"
|
|
115
|
-
"contains"
|
|
130
|
+
"like"
|
|
116
131
|
];
|
|
132
|
+
const UNSUPPORTED_OPERATOR_HINTS = /* @__PURE__ */ new Map([["contains", "Use 'like' with explicit wildcards instead, e.g. { 'name like': '%term%' }."]]);
|
|
117
133
|
const fields = this.getFieldsSync();
|
|
118
134
|
const validFieldNames = new Set(Object.keys(fields).map((f) => toSnakeCase(f)));
|
|
119
135
|
const sensitiveFieldNames = this.collectSensitiveFieldNames(fields);
|
|
@@ -156,8 +172,12 @@ var SmrtCollection = class SmrtCollection extends SmrtClass {
|
|
|
156
172
|
const metaPathTargetsSensitive = (snakeBaseFieldName === "_meta_data" || snakeBaseFieldName === "meta_data") && !!jsonPath && jsonPath.split(".").filter(Boolean).some((segment) => sensitiveFieldNames.has(segment) || sensitiveFieldNames.has(toSnakeCase(segment)));
|
|
157
173
|
if (sensitiveFieldNames.has(snakeBaseFieldName) || metaPathTargetsSensitive) throw new Error(`Invalid WHERE clause field: '${fieldName}'. Filtering on sensitive fields is not allowed.`);
|
|
158
174
|
const effectiveOperator = operator === "=" && Array.isArray(value) ? "in" : operator;
|
|
159
|
-
if (!VALID_OPERATORS.includes(effectiveOperator))
|
|
175
|
+
if (!VALID_OPERATORS.includes(effectiveOperator)) {
|
|
176
|
+
const hint = UNSUPPORTED_OPERATOR_HINTS.get(effectiveOperator);
|
|
177
|
+
throw new Error(`Invalid WHERE clause operator: '${operator}'. Valid operators: ${VALID_OPERATORS.join(", ")}` + (hint ? `. ${hint}` : ""));
|
|
178
|
+
}
|
|
160
179
|
if (!skipFieldValidation && !validFieldNames.has(snakeBaseFieldName)) throw new Error(`Invalid WHERE clause field: '${fieldName}'. Field does not exist on ${itemClassName}. Valid fields: ${Array.from(validFieldNames).sort().join(", ")}`);
|
|
180
|
+
if (jsonPath) throw new Error(`Invalid WHERE clause field: '${fieldName}'. Dot-notation JSON paths are not supported — the path is not rewritten into a JSON extraction expression, so it would reach SQL as a qualified column reference and fail at execution. Filter on the '${baseFieldName}' column itself, or filter the extracted values in application code.`);
|
|
161
181
|
if ((effectiveOperator === "in" || effectiveOperator === "not in") && !Array.isArray(value)) throw new Error(`WHERE clause operator '${effectiveOperator}' requires an array value for field '${fieldName}', got ${typeof value}`);
|
|
162
182
|
if ((effectiveOperator === "in" || effectiveOperator === "not in") && Array.isArray(value) && value.length === 0) throw new Error(`WHERE clause operator '${effectiveOperator}' requires a non-empty array for field '${fieldName}'. Use listByIds([]) for graceful empty array handling.`);
|
|
163
183
|
if (effectiveOperator === "like" && typeof value !== "string") throw new Error(`WHERE clause operator 'like' requires a string value for field '${fieldName}', got ${typeof value}`);
|
|
@@ -1274,6 +1294,10 @@ var SmrtCollection = class SmrtCollection extends SmrtClass {
|
|
|
1274
1294
|
* Stores context applicable to all instances of this collection type.
|
|
1275
1295
|
* Use for patterns that apply to the entire collection (e.g., default parsing strategies).
|
|
1276
1296
|
*
|
|
1297
|
+
* `expiresAt` is stored on the `_smrt_contexts` row as caller-managed metadata.
|
|
1298
|
+
* {@link recall} and {@link recallAll} do **not** filter on it, so an expired
|
|
1299
|
+
* entry is still returned; filter or delete expired entries yourself.
|
|
1300
|
+
*
|
|
1277
1301
|
* @param options - Context options
|
|
1278
1302
|
* @returns Promise that resolves when context is stored
|
|
1279
1303
|
* @example
|
|
@@ -1502,7 +1526,9 @@ var SmrtCollection = class SmrtCollection extends SmrtClass {
|
|
|
1502
1526
|
*
|
|
1503
1527
|
* @param query - Text to search for
|
|
1504
1528
|
* @param options - Search options
|
|
1505
|
-
* @param options.field - Specific field to search (defaults to first embedding
|
|
1529
|
+
* @param options.field - Specific field to search (defaults to first embedding
|
|
1530
|
+
* field). Any configured embedding field, or the configured
|
|
1531
|
+
* `combinedField.name`, is accepted.
|
|
1506
1532
|
* @param options.limit - Maximum results to return (default: 10)
|
|
1507
1533
|
* @param options.minSimilarity - Minimum similarity threshold 0-1 (default: 0)
|
|
1508
1534
|
* @param options.where - Additional WHERE filters to apply
|
|
@@ -1518,6 +1544,11 @@ var SmrtCollection = class SmrtCollection extends SmrtClass {
|
|
|
1518
1544
|
* for (const article of results) {
|
|
1519
1545
|
* console.log(`${article.title} (similarity: ${article._similarity})`);
|
|
1520
1546
|
* }
|
|
1547
|
+
*
|
|
1548
|
+
* // Search the combined vector built from `combinedField.template`
|
|
1549
|
+
* const combined = await articles.semanticSearch('machine learning trends', {
|
|
1550
|
+
* field: 'content'
|
|
1551
|
+
* });
|
|
1521
1552
|
* ```
|
|
1522
1553
|
*/
|
|
1523
1554
|
async semanticSearch(query, options = {}) {
|
|
@@ -1525,7 +1556,8 @@ var SmrtCollection = class SmrtCollection extends SmrtClass {
|
|
|
1525
1556
|
const embeddingConfig = ObjectRegistry.resolveEmbeddingConfig(this._itemClass.name);
|
|
1526
1557
|
if (!embeddingConfig) throw new Error(`No embedding configuration found for ${this._itemClass.name}. Add embeddings config to @smrt() decorator.`);
|
|
1527
1558
|
const searchField = field || embeddingConfig.fields[0];
|
|
1528
|
-
|
|
1559
|
+
const searchableFields = getSearchableEmbeddingFields(embeddingConfig);
|
|
1560
|
+
if (!searchableFields.includes(searchField)) throw new Error(`Field '${searchField}' is not configured for embeddings on ${this._itemClass.name}. Available fields: ${searchableFields.join(", ")}`);
|
|
1529
1561
|
const [queryEmbedding] = await new EmbeddingProvider({
|
|
1530
1562
|
dimensions: embeddingConfig.dimensions,
|
|
1531
1563
|
provider: embeddingConfig.provider,
|