ai-database 2.1.3 → 2.3.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.
- package/CHANGELOG.md +35 -1
- package/README.md +880 -669
- package/dist/actions.d.ts +2 -2
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +1 -1
- package/dist/actions.js.map +1 -1
- package/dist/ai-promise-db.d.ts +49 -23
- package/dist/ai-promise-db.d.ts.map +1 -1
- package/dist/ai-promise-db.js +91 -63
- package/dist/ai-promise-db.js.map +1 -1
- package/dist/authorization.d.ts.map +1 -1
- package/dist/authorization.js +38 -30
- package/dist/authorization.js.map +1 -1
- package/dist/cascade-orchestrator.d.ts +404 -0
- package/dist/cascade-orchestrator.d.ts.map +1 -0
- package/dist/cascade-orchestrator.js +828 -0
- package/dist/cascade-orchestrator.js.map +1 -0
- package/dist/cascade-write-strategy.d.ts +584 -0
- package/dist/cascade-write-strategy.d.ts.map +1 -0
- package/dist/cascade-write-strategy.js +590 -0
- package/dist/cascade-write-strategy.js.map +1 -0
- package/dist/ch-adapter.d.ts +358 -0
- package/dist/ch-adapter.d.ts.map +1 -0
- package/dist/ch-adapter.js +929 -0
- package/dist/ch-adapter.js.map +1 -0
- package/dist/client/index.d.ts +42 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +43 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client.d.ts +266 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +81 -0
- package/dist/client.js.map +1 -0
- package/dist/constants.d.ts +64 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +52 -2
- package/dist/constants.js.map +1 -1
- package/dist/dataloader.d.ts +99 -0
- package/dist/dataloader.d.ts.map +1 -0
- package/dist/dataloader.js +225 -0
- package/dist/dataloader.js.map +1 -0
- package/dist/db-provider-port.d.ts +501 -0
- package/dist/db-provider-port.d.ts.map +1 -0
- package/dist/db-provider-port.js +113 -0
- package/dist/db-provider-port.js.map +1 -0
- package/dist/digital-objects-provider.d.ts +49 -0
- package/dist/digital-objects-provider.d.ts.map +1 -0
- package/dist/digital-objects-provider.js +55 -0
- package/dist/digital-objects-provider.js.map +1 -0
- package/dist/do-sqlite-adapter.d.ts +402 -0
- package/dist/do-sqlite-adapter.d.ts.map +1 -0
- package/dist/do-sqlite-adapter.js +745 -0
- package/dist/do-sqlite-adapter.js.map +1 -0
- package/dist/docs-rels/custom-types.d.ts +134 -0
- package/dist/docs-rels/custom-types.d.ts.map +1 -0
- package/dist/docs-rels/custom-types.js +70 -0
- package/dist/docs-rels/custom-types.js.map +1 -0
- package/dist/docs-rels/index.d.ts +16 -0
- package/dist/docs-rels/index.d.ts.map +1 -0
- package/dist/docs-rels/index.js +16 -0
- package/dist/docs-rels/index.js.map +1 -0
- package/dist/docs-rels/migrations/index.d.ts +30 -0
- package/dist/docs-rels/migrations/index.d.ts.map +1 -0
- package/dist/docs-rels/migrations/index.js +128 -0
- package/dist/docs-rels/migrations/index.js.map +1 -0
- package/dist/docs-rels/schema.d.ts +2961 -0
- package/dist/docs-rels/schema.d.ts.map +1 -0
- package/dist/docs-rels/schema.js +244 -0
- package/dist/docs-rels/schema.js.map +1 -0
- package/dist/durable-clickhouse.d.ts.map +1 -1
- package/dist/durable-clickhouse.js +16 -13
- package/dist/durable-clickhouse.js.map +1 -1
- package/dist/durable-promise.d.ts.map +1 -1
- package/dist/durable-promise.js +34 -15
- package/dist/durable-promise.js.map +1 -1
- package/dist/errors.d.ts +127 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +210 -0
- package/dist/errors.js.map +1 -0
- package/dist/eventbridge.d.ts +117 -0
- package/dist/eventbridge.d.ts.map +1 -0
- package/dist/eventbridge.js +238 -0
- package/dist/eventbridge.js.map +1 -0
- package/dist/events.d.ts +2 -2
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +1 -1
- package/dist/events.js.map +1 -1
- package/dist/execution-queue.d.ts.map +1 -1
- package/dist/execution-queue.js +4 -5
- package/dist/execution-queue.js.map +1 -1
- package/dist/index.d.ts +35 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +106 -6
- package/dist/index.js.map +1 -1
- package/dist/linguistic.d.ts +3 -108
- package/dist/linguistic.d.ts.map +1 -1
- package/dist/linguistic.js +3 -372
- package/dist/linguistic.js.map +1 -1
- package/dist/logger.d.ts +132 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +137 -0
- package/dist/logger.js.map +1 -0
- package/dist/memory-provider.d.ts +128 -0
- package/dist/memory-provider.d.ts.map +1 -1
- package/dist/memory-provider.js +592 -257
- package/dist/memory-provider.js.map +1 -1
- package/dist/pg-adapter.d.ts +424 -0
- package/dist/pg-adapter.d.ts.map +1 -0
- package/dist/pg-adapter.js +921 -0
- package/dist/pg-adapter.js.map +1 -0
- package/dist/pipelines-iceberg-emitter.d.ts +327 -0
- package/dist/pipelines-iceberg-emitter.d.ts.map +1 -0
- package/dist/pipelines-iceberg-emitter.js +351 -0
- package/dist/pipelines-iceberg-emitter.js.map +1 -0
- package/dist/provider-capabilities.d.ts +146 -0
- package/dist/provider-capabilities.d.ts.map +1 -0
- package/dist/provider-capabilities.js +214 -0
- package/dist/provider-capabilities.js.map +1 -0
- package/dist/rdb-provider-adapter.d.ts +195 -0
- package/dist/rdb-provider-adapter.d.ts.map +1 -0
- package/dist/rdb-provider-adapter.js +291 -0
- package/dist/rdb-provider-adapter.js.map +1 -0
- package/dist/schema/cascade.d.ts +48 -17
- package/dist/schema/cascade.d.ts.map +1 -1
- package/dist/schema/cascade.js +477 -278
- package/dist/schema/cascade.js.map +1 -1
- package/dist/schema/definition-caches.d.ts +24 -0
- package/dist/schema/definition-caches.d.ts.map +1 -0
- package/dist/schema/definition-caches.js +26 -0
- package/dist/schema/definition-caches.js.map +1 -0
- package/dist/schema/dependency-graph.d.ts +21 -109
- package/dist/schema/dependency-graph.d.ts.map +1 -1
- package/dist/schema/dependency-graph.js +25 -333
- package/dist/schema/dependency-graph.js.map +1 -1
- package/dist/schema/diff.d.ts +103 -0
- package/dist/schema/diff.d.ts.map +1 -0
- package/dist/schema/diff.js +329 -0
- package/dist/schema/diff.js.map +1 -0
- package/dist/schema/entity-operations.d.ts +99 -0
- package/dist/schema/entity-operations.d.ts.map +1 -0
- package/dist/schema/entity-operations.js +818 -0
- package/dist/schema/entity-operations.js.map +1 -0
- package/dist/schema/index.d.ts +28 -34
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +454 -521
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/migration.d.ts +205 -0
- package/dist/schema/migration.d.ts.map +1 -0
- package/dist/schema/migration.js +327 -0
- package/dist/schema/migration.js.map +1 -0
- package/dist/schema/nl-query-generator.d.ts +68 -0
- package/dist/schema/nl-query-generator.d.ts.map +1 -0
- package/dist/schema/nl-query-generator.js +362 -0
- package/dist/schema/nl-query-generator.js.map +1 -0
- package/dist/schema/nl-query.d.ts +65 -0
- package/dist/schema/nl-query.d.ts.map +1 -0
- package/dist/schema/nl-query.js +178 -0
- package/dist/schema/nl-query.js.map +1 -0
- package/dist/schema/parse.d.ts.map +1 -1
- package/dist/schema/parse.js +144 -89
- package/dist/schema/parse.js.map +1 -1
- package/dist/schema/provider.d.ts +37 -0
- package/dist/schema/provider.d.ts.map +1 -1
- package/dist/schema/provider.js +15 -7
- package/dist/schema/provider.js.map +1 -1
- package/dist/schema/resolve.d.ts +46 -5
- package/dist/schema/resolve.d.ts.map +1 -1
- package/dist/schema/resolve.js +237 -95
- package/dist/schema/resolve.js.map +1 -1
- package/dist/schema/search-utils.d.ts +76 -0
- package/dist/schema/search-utils.d.ts.map +1 -0
- package/dist/schema/search-utils.js +86 -0
- package/dist/schema/search-utils.js.map +1 -0
- package/dist/schema/seed.d.ts +53 -0
- package/dist/schema/seed.d.ts.map +1 -0
- package/dist/schema/seed.js +94 -0
- package/dist/schema/seed.js.map +1 -0
- package/dist/schema/semantic.d.ts +10 -0
- package/dist/schema/semantic.d.ts.map +1 -1
- package/dist/schema/semantic.js +192 -86
- package/dist/schema/semantic.js.map +1 -1
- package/dist/schema/sub-apis.d.ts +52 -0
- package/dist/schema/sub-apis.d.ts.map +1 -0
- package/dist/schema/sub-apis.js +216 -0
- package/dist/schema/sub-apis.js.map +1 -0
- package/dist/schema/system-entities.d.ts +42 -0
- package/dist/schema/system-entities.d.ts.map +1 -0
- package/dist/schema/system-entities.js +101 -0
- package/dist/schema/system-entities.js.map +1 -0
- package/dist/schema/types.d.ts +91 -9
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/union-fallback.d.ts.map +1 -1
- package/dist/schema/union-fallback.js +21 -15
- package/dist/schema/union-fallback.js.map +1 -1
- package/dist/schema/value-generators/ai.d.ts +54 -0
- package/dist/schema/value-generators/ai.d.ts.map +1 -0
- package/dist/schema/value-generators/ai.js +136 -0
- package/dist/schema/value-generators/ai.js.map +1 -0
- package/dist/schema/value-generators/index.d.ts +126 -0
- package/dist/schema/value-generators/index.d.ts.map +1 -0
- package/dist/schema/value-generators/index.js +219 -0
- package/dist/schema/value-generators/index.js.map +1 -0
- package/dist/schema/value-generators/placeholder.d.ts +52 -0
- package/dist/schema/value-generators/placeholder.d.ts.map +1 -0
- package/dist/schema/value-generators/placeholder.js +328 -0
- package/dist/schema/value-generators/placeholder.js.map +1 -0
- package/dist/schema/value-generators/types.d.ts +116 -0
- package/dist/schema/value-generators/types.d.ts.map +1 -0
- package/dist/schema/value-generators/types.js +11 -0
- package/dist/schema/value-generators/types.js.map +1 -0
- package/dist/schema/version.d.ts +111 -0
- package/dist/schema/version.d.ts.map +1 -0
- package/dist/schema/version.js +190 -0
- package/dist/schema/version.js.map +1 -0
- package/dist/schema.d.ts +1095 -24
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +2852 -40
- package/dist/schema.js.map +1 -1
- package/dist/semantic-vectors.d.ts +39 -0
- package/dist/semantic-vectors.d.ts.map +1 -0
- package/dist/semantic-vectors.js +334 -0
- package/dist/semantic-vectors.js.map +1 -0
- package/dist/semantic.d.ts +29 -1
- package/dist/semantic.d.ts.map +1 -1
- package/dist/semantic.js +26 -16
- package/dist/semantic.js.map +1 -1
- package/dist/telemetry.d.ts +128 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +305 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/tests.d.ts.map +1 -1
- package/dist/tests.js +30 -22
- package/dist/tests.js.map +1 -1
- package/dist/type-guards.d.ts +50 -5
- package/dist/type-guards.d.ts.map +1 -1
- package/dist/type-guards.js +87 -16
- package/dist/type-guards.js.map +1 -1
- package/dist/types.d.ts +33 -245
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +62 -72
- package/dist/types.js.map +1 -1
- package/dist/validation.d.ts +2 -5
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +65 -93
- package/dist/validation.js.map +1 -1
- package/dist/worker/db-provider.d.ts +168 -0
- package/dist/worker/db-provider.d.ts.map +1 -0
- package/dist/worker/db-provider.js +277 -0
- package/dist/worker/db-provider.js.map +1 -0
- package/dist/worker/index.d.ts +35 -0
- package/dist/worker/index.d.ts.map +1 -0
- package/dist/worker/index.js +37 -0
- package/dist/worker/index.js.map +1 -0
- package/dist/worker.d.ts +779 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +2786 -0
- package/dist/worker.js.map +1 -0
- package/package.json +46 -16
- package/src/docs-rels/migrations/0001-init.sql +125 -0
- package/LICENSE +0 -21
package/dist/schema/resolve.d.ts
CHANGED
|
@@ -37,6 +37,25 @@ export declare function resolveContextPath(path: string, entity: Record<string,
|
|
|
37
37
|
* @returns The instructions with all template variables resolved
|
|
38
38
|
*/
|
|
39
39
|
export declare function resolveInstructions(instructions: string, entity: Record<string, unknown>, typeName: string, schema: ParsedSchema, provider: DBProvider): Promise<string>;
|
|
40
|
+
/**
|
|
41
|
+
* Pre-fetch context dependencies declared in $context using dotted paths
|
|
42
|
+
*
|
|
43
|
+
* The $context field declares explicit context dependencies that should
|
|
44
|
+
* be pre-fetched before generating AI fields. Supports dotted paths like
|
|
45
|
+
* 'Startup.icp.industry' for deeply nested context fetching.
|
|
46
|
+
*
|
|
47
|
+
* Context paths can be:
|
|
48
|
+
* - Simple type names: 'Startup' - finds field that references Startup type
|
|
49
|
+
* - Dotted paths: 'Startup.icp.industry' - traverses relationships
|
|
50
|
+
*
|
|
51
|
+
* @param contextDeps - Array of context dependency paths (e.g., ['Startup', 'Startup.icp.industry'])
|
|
52
|
+
* @param entity - The current entity data
|
|
53
|
+
* @param typeName - The current entity type name
|
|
54
|
+
* @param schema - The parsed schema
|
|
55
|
+
* @param provider - The database provider for fetching related entities
|
|
56
|
+
* @returns Map of context path to fetched entity data
|
|
57
|
+
*/
|
|
58
|
+
export declare function prefetchContextPaths(contextDeps: string[], entity: Record<string, unknown>, typeName: string, schema: ParsedSchema, provider: DBProvider): Promise<Map<string, Record<string, unknown>>>;
|
|
40
59
|
/**
|
|
41
60
|
* Pre-fetch context dependencies declared in $context
|
|
42
61
|
*
|
|
@@ -53,7 +72,8 @@ export declare function resolveInstructions(instructions: string, entity: Record
|
|
|
53
72
|
*/
|
|
54
73
|
export declare function prefetchContext(contextDeps: string[], entity: Record<string, unknown>, typeName: string, schema: ParsedSchema, provider: DBProvider): Promise<Map<string, Record<string, unknown>>>;
|
|
55
74
|
/**
|
|
56
|
-
* Check if a field type is a prompt (contains spaces, indicating AI generation)
|
|
75
|
+
* Check if a field type is a prompt (contains spaces, slashes, or question marks indicating AI generation).
|
|
76
|
+
* Examples: 'Describe the product', 'low/medium/high', 'What is the severity?'
|
|
57
77
|
*/
|
|
58
78
|
export declare function isPromptField(field: ParsedField): boolean;
|
|
59
79
|
/**
|
|
@@ -70,7 +90,11 @@ export declare function resolveNestedPending(data: Record<string, unknown>, enti
|
|
|
70
90
|
* For exact matches (-> and <-), creates new entities.
|
|
71
91
|
* For fuzzy matches (~> and <~), searches for existing entities first.
|
|
72
92
|
*/
|
|
73
|
-
export declare function resolveReferenceSpec(spec: ReferenceSpec, contextData: Record<string, unknown>, schema: ParsedSchema, provider: DBProvider, generateContextAwareValue: (fieldName: string, type: string, fullContext: string, hint: string | undefined, parentData: Record<string, unknown>) => string
|
|
93
|
+
export declare function resolveReferenceSpec(spec: ReferenceSpec, contextData: Record<string, unknown>, schema: ParsedSchema, provider: DBProvider, generateContextAwareValue: (fieldName: string, type: string, fullContext: string, hint: string | undefined, parentData: Record<string, unknown>) => string, generateEntityWithAI?: (type: string, prompt: string | undefined, context: {
|
|
94
|
+
parent: string;
|
|
95
|
+
parentData: Record<string, unknown>;
|
|
96
|
+
parentId?: string;
|
|
97
|
+
}, schema: ParsedSchema) => Promise<Record<string, unknown>>): Promise<string | null>;
|
|
74
98
|
/**
|
|
75
99
|
* Hydrate an entity with lazy-loaded relations
|
|
76
100
|
*
|
|
@@ -78,10 +102,27 @@ export declare function resolveReferenceSpec(spec: ReferenceSpec, contextData: R
|
|
|
78
102
|
* of the related type that have a reference pointing TO this entity.
|
|
79
103
|
* This enables reverse lookups like "get all comments for a post".
|
|
80
104
|
*
|
|
81
|
-
* Backward
|
|
82
|
-
*
|
|
105
|
+
* ## Backward Exact (<-) vs Backward Fuzzy (<~) Resolution
|
|
106
|
+
*
|
|
107
|
+
* This function handles both backward exact and backward fuzzy resolution,
|
|
108
|
+
* but the resolution strategies differ:
|
|
109
|
+
*
|
|
110
|
+
* **Backward Exact (`<-`)**: Uses foreign key lookup (exact ID match)
|
|
111
|
+
* - Queries `provider.list(type, { where: { backrefField: id } })`
|
|
112
|
+
* - Returns all entities that explicitly reference the current entity
|
|
113
|
+
* - Example: `Blog.posts: ['<-Post']` finds Posts where `post.blog === blog.$id`
|
|
114
|
+
*
|
|
115
|
+
* **Backward Fuzzy (`<~`)**: Uses pre-resolved IDs from semantic search
|
|
116
|
+
* - During creation, `resolveBackwardFuzzy()` stores matched IDs
|
|
117
|
+
* - Hydration retrieves entities using those stored IDs
|
|
118
|
+
* - Example: `ICP.as: '<~Occupation'` was resolved via semantic search at creation time
|
|
119
|
+
*
|
|
120
|
+
* ## Resolution Cases
|
|
121
|
+
*
|
|
122
|
+
* - Single backward ref with stored ID: resolve directly (e.g., member.team = teamId)
|
|
83
123
|
* - Single backward ref without stored ID: find related entity that points to us via relations
|
|
84
|
-
* - Array backward ref
|
|
124
|
+
* - Array backward ref with stored IDs: resolve each stored ID (backward fuzzy case)
|
|
125
|
+
* - Array backward ref without stored IDs: query via backref lookup (backward exact case)
|
|
85
126
|
*/
|
|
86
127
|
export declare function hydrateEntity(data: Record<string, unknown>, entity: ParsedEntity, schema: ParsedSchema, resolveProvider: () => Promise<DBProvider>): Record<string, unknown>;
|
|
87
128
|
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/schema/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/schema/resolve.ts"],"names":[],"mappings":"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"}
|
package/dist/schema/resolve.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { hasSemanticSearch } from './provider.js';
|
|
10
10
|
import { isPrimitiveType } from './parse.js';
|
|
11
|
+
import { findBestMatchAcrossTypes } from './search-utils.js';
|
|
11
12
|
// =============================================================================
|
|
12
13
|
// Context Resolution - Template Variables and $instructions
|
|
13
14
|
// =============================================================================
|
|
@@ -104,63 +105,132 @@ export async function resolveInstructions(instructions, entity, typeName, schema
|
|
|
104
105
|
return resolved;
|
|
105
106
|
}
|
|
106
107
|
/**
|
|
107
|
-
*
|
|
108
|
+
* Find the field in the entity that references a given type
|
|
109
|
+
*/
|
|
110
|
+
function findFieldByRelatedType(entityDef, relatedType) {
|
|
111
|
+
for (const [fieldName, field] of entityDef.fields) {
|
|
112
|
+
if (field.isRelation && field.relatedType === relatedType) {
|
|
113
|
+
return { fieldName, field };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Pre-fetch context dependencies declared in $context using dotted paths
|
|
108
120
|
*
|
|
109
121
|
* The $context field declares explicit context dependencies that should
|
|
110
|
-
* be pre-fetched before generating AI fields.
|
|
111
|
-
*
|
|
122
|
+
* be pre-fetched before generating AI fields. Supports dotted paths like
|
|
123
|
+
* 'Startup.icp.industry' for deeply nested context fetching.
|
|
112
124
|
*
|
|
113
|
-
*
|
|
125
|
+
* Context paths can be:
|
|
126
|
+
* - Simple type names: 'Startup' - finds field that references Startup type
|
|
127
|
+
* - Dotted paths: 'Startup.icp.industry' - traverses relationships
|
|
128
|
+
*
|
|
129
|
+
* @param contextDeps - Array of context dependency paths (e.g., ['Startup', 'Startup.icp.industry'])
|
|
114
130
|
* @param entity - The current entity data
|
|
115
131
|
* @param typeName - The current entity type name
|
|
116
132
|
* @param schema - The parsed schema
|
|
117
133
|
* @param provider - The database provider for fetching related entities
|
|
118
|
-
* @returns Map of context
|
|
134
|
+
* @returns Map of context path to fetched entity data
|
|
119
135
|
*/
|
|
120
|
-
export async function
|
|
136
|
+
export async function prefetchContextPaths(contextDeps, entity, typeName, schema, provider) {
|
|
121
137
|
const contextData = new Map();
|
|
122
138
|
const currentEntity = schema.entities.get(typeName);
|
|
123
139
|
if (!currentEntity)
|
|
124
140
|
return contextData;
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
141
|
+
// Track already fetched entities to avoid duplicate fetches
|
|
142
|
+
// Key: "TypeName:entityId", Value: fetched entity data
|
|
143
|
+
const fetchCache = new Map();
|
|
144
|
+
// Deduplicate context deps
|
|
145
|
+
const uniqueDeps = [...new Set(contextDeps)];
|
|
146
|
+
for (const dep of uniqueDeps) {
|
|
147
|
+
// Parse the path - could be "Startup" or "Startup.icp.industry"
|
|
148
|
+
const pathParts = dep.split('.');
|
|
149
|
+
// Start from the current entity and traverse the path
|
|
150
|
+
let currentData = entity;
|
|
151
|
+
let currentEntityDef = currentEntity;
|
|
152
|
+
let currentPath = '';
|
|
153
|
+
for (let i = 0; i < pathParts.length; i++) {
|
|
154
|
+
const part = pathParts[i];
|
|
155
|
+
// Try two approaches:
|
|
156
|
+
// 1. Convert PascalCase type name to camelCase field name
|
|
157
|
+
// 2. Find field by related type
|
|
158
|
+
const camelCaseFieldName = part.charAt(0).toLowerCase() + part.slice(1);
|
|
159
|
+
let fieldInfo;
|
|
160
|
+
// First try exact field name match
|
|
161
|
+
const directField = currentEntityDef.fields.get(camelCaseFieldName);
|
|
162
|
+
if (directField?.isRelation && directField.relatedType) {
|
|
163
|
+
fieldInfo = { fieldName: camelCaseFieldName, field: directField };
|
|
164
|
+
}
|
|
165
|
+
// If no direct match, try to find field by related type (e.g., 'Related' -> find field that points to Related)
|
|
166
|
+
if (!fieldInfo) {
|
|
167
|
+
fieldInfo = findFieldByRelatedType(currentEntityDef, part);
|
|
168
|
+
}
|
|
169
|
+
if (!fieldInfo) {
|
|
170
|
+
// Path invalid - no field found
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
const { fieldName, field } = fieldInfo;
|
|
174
|
+
currentPath = currentPath ? `${currentPath}.${fieldName}` : fieldName;
|
|
175
|
+
// Get the entity ID from current data
|
|
176
|
+
const entityId = currentData[fieldName];
|
|
177
|
+
if (typeof entityId !== 'string') {
|
|
178
|
+
// Path invalid - no entity ID
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
// Check cache first
|
|
182
|
+
const cacheKey = `${field.relatedType}:${entityId}`;
|
|
183
|
+
let fetched = fetchCache.get(cacheKey);
|
|
184
|
+
if (!fetched) {
|
|
185
|
+
// Fetch the related entity
|
|
186
|
+
const fetchedResult = await provider.get(field.relatedType, entityId);
|
|
187
|
+
if (fetchedResult) {
|
|
188
|
+
fetched = fetchedResult;
|
|
189
|
+
fetchCache.set(cacheKey, fetched);
|
|
151
190
|
}
|
|
152
191
|
}
|
|
192
|
+
if (!fetched) {
|
|
193
|
+
// Path invalid - entity not found
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
// Store the result with the actual field name path
|
|
197
|
+
contextData.set(currentPath, fetched);
|
|
198
|
+
// Update for next iteration
|
|
199
|
+
currentData = fetched;
|
|
200
|
+
const nextEntityDef = schema.entities.get(field.relatedType);
|
|
201
|
+
if (!nextEntityDef) {
|
|
202
|
+
// Path invalid - entity type not in schema
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
currentEntityDef = nextEntityDef;
|
|
153
206
|
}
|
|
154
207
|
}
|
|
155
208
|
return contextData;
|
|
156
209
|
}
|
|
157
210
|
/**
|
|
158
|
-
*
|
|
211
|
+
* Pre-fetch context dependencies declared in $context
|
|
212
|
+
*
|
|
213
|
+
* The $context field declares explicit context dependencies that should
|
|
214
|
+
* be pre-fetched before generating AI fields. This ensures all referenced
|
|
215
|
+
* entities are available for template variable resolution.
|
|
216
|
+
*
|
|
217
|
+
* @param contextDeps - Array of context dependency types (e.g., ['Startup', 'ICP'])
|
|
218
|
+
* @param entity - The current entity data
|
|
219
|
+
* @param typeName - The current entity type name
|
|
220
|
+
* @param schema - The parsed schema
|
|
221
|
+
* @param provider - The database provider for fetching related entities
|
|
222
|
+
* @returns Map of context name to fetched entity data
|
|
223
|
+
*/
|
|
224
|
+
export async function prefetchContext(contextDeps, entity, typeName, schema, provider) {
|
|
225
|
+
// Use the new path-based implementation which handles both simple and dotted paths
|
|
226
|
+
return prefetchContextPaths(contextDeps, entity, typeName, schema, provider);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Check if a field type is a prompt (contains spaces, slashes, or question marks indicating AI generation).
|
|
230
|
+
* Examples: 'Describe the product', 'low/medium/high', 'What is the severity?'
|
|
159
231
|
*/
|
|
160
232
|
export function isPromptField(field) {
|
|
161
|
-
|
|
162
|
-
return field.type.includes(' ') ||
|
|
163
|
-
(field.type === 'string' && !field.isRelation && !isPrimitiveType(field.type));
|
|
233
|
+
return field.type.includes(' ') || field.type.includes('/') || field.type.includes('?');
|
|
164
234
|
}
|
|
165
235
|
// =============================================================================
|
|
166
236
|
// Nested Pending Resolution
|
|
@@ -184,7 +254,7 @@ export async function resolveNestedPending(data, entity, schema, provider) {
|
|
|
184
254
|
if (relatedEntity) {
|
|
185
255
|
const resolvedNested = await resolveNestedPending(pending.data, relatedEntity, schema, provider);
|
|
186
256
|
const created = await provider.create(pending.type, undefined, resolvedNested);
|
|
187
|
-
resolved[fieldName] = created
|
|
257
|
+
resolved[fieldName] = created['$id'];
|
|
188
258
|
}
|
|
189
259
|
}
|
|
190
260
|
}
|
|
@@ -199,7 +269,7 @@ export async function resolveNestedPending(data, entity, schema, provider) {
|
|
|
199
269
|
* For exact matches (-> and <-), creates new entities.
|
|
200
270
|
* For fuzzy matches (~> and <~), searches for existing entities first.
|
|
201
271
|
*/
|
|
202
|
-
export async function resolveReferenceSpec(spec, contextData, schema, provider, generateContextAwareValue) {
|
|
272
|
+
export async function resolveReferenceSpec(spec, contextData, schema, provider, generateContextAwareValue, generateEntityWithAI) {
|
|
203
273
|
const targetEntity = schema.entities.get(spec.type);
|
|
204
274
|
if (!targetEntity) {
|
|
205
275
|
throw new Error(`Unknown target type: ${spec.type}`);
|
|
@@ -208,66 +278,123 @@ export async function resolveReferenceSpec(spec, contextData, schema, provider,
|
|
|
208
278
|
// For fuzzy references, try to find an existing entity first
|
|
209
279
|
if (hasSemanticSearch(provider)) {
|
|
210
280
|
const searchQuery = spec.generatedText || spec.prompt || spec.field;
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
281
|
+
// Use threshold from spec, or default to 0.75
|
|
282
|
+
const threshold = spec.threshold ?? 0.75;
|
|
283
|
+
// Determine which types to search - all union types or just the primary type
|
|
284
|
+
const typesToSearch = spec.unionTypes && spec.unionTypes.length > 0 ? spec.unionTypes : [spec.type];
|
|
285
|
+
// Search all types and find the best match using shared utility
|
|
286
|
+
const bestMatchResult = await findBestMatchAcrossTypes(typesToSearch, searchQuery, {
|
|
287
|
+
threshold,
|
|
288
|
+
limit: 5,
|
|
289
|
+
schema,
|
|
290
|
+
provider,
|
|
214
291
|
});
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
292
|
+
if (bestMatchResult) {
|
|
293
|
+
// Update the matched entity with metadata
|
|
294
|
+
await provider.update(bestMatchResult.type, bestMatchResult.match.$id, {
|
|
295
|
+
$matchedType: bestMatchResult.type,
|
|
296
|
+
$similarity: bestMatchResult.match.$score,
|
|
297
|
+
});
|
|
298
|
+
// Also set metadata on contextData for edge creation later
|
|
299
|
+
contextData[`${spec.field}$matchedType`] = bestMatchResult.type;
|
|
300
|
+
contextData[`${spec.field}$score`] = bestMatchResult.match.$score;
|
|
301
|
+
return bestMatchResult.match.$id;
|
|
218
302
|
}
|
|
219
303
|
}
|
|
220
304
|
// If no match found for fuzzy, fall through to create
|
|
221
305
|
}
|
|
222
306
|
// Create a new entity
|
|
223
|
-
|
|
224
|
-
// Build context for generation
|
|
307
|
+
let generatedData = {};
|
|
225
308
|
const hint = spec.generatedText || spec.prompt || spec.field;
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
309
|
+
const parentType = contextData['$type'];
|
|
310
|
+
const parentId = contextData['$id'];
|
|
311
|
+
// Try AI generation first if available
|
|
312
|
+
if (generateEntityWithAI && parentType) {
|
|
313
|
+
try {
|
|
314
|
+
const aiData = await generateEntityWithAI(spec.type, hint, {
|
|
315
|
+
parent: parentType,
|
|
316
|
+
parentData: contextData,
|
|
317
|
+
...(parentId !== undefined ? { parentId } : {}),
|
|
318
|
+
}, schema);
|
|
319
|
+
if (aiData && Object.keys(aiData).length > 0) {
|
|
320
|
+
// Resolve any nested pending relations (from generateEntity)
|
|
321
|
+
generatedData = await resolveNestedPending(aiData, targetEntity, schema, provider);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
// AI generation failed - fall through to placeholder generation
|
|
230
326
|
}
|
|
231
327
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
else if (field.type === 'number') {
|
|
240
|
-
generatedData[fieldName] = 0;
|
|
241
|
-
}
|
|
242
|
-
else if (field.type === 'boolean') {
|
|
243
|
-
generatedData[fieldName] = false;
|
|
328
|
+
// If AI generation didn't produce data, fall back to placeholder generation
|
|
329
|
+
if (Object.keys(generatedData).length === 0) {
|
|
330
|
+
// Build context for generation
|
|
331
|
+
const parentContextFields = [];
|
|
332
|
+
for (const [key, value] of Object.entries(contextData)) {
|
|
333
|
+
if (!key.startsWith('$') && !key.startsWith('_') && typeof value === 'string' && value) {
|
|
334
|
+
parentContextFields.push(`${key}: ${value}`);
|
|
244
335
|
}
|
|
245
336
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
337
|
+
// Include source entity's $instructions (parent context) and target entity's $instructions
|
|
338
|
+
const sourceInstructions = spec.sourceInstructions;
|
|
339
|
+
const targetInstructions = targetEntity.schema?.['$instructions'];
|
|
340
|
+
const contextParts = [];
|
|
341
|
+
// Source instructions first (parent context propagation)
|
|
342
|
+
if (sourceInstructions) {
|
|
343
|
+
contextParts.push(sourceInstructions);
|
|
344
|
+
}
|
|
345
|
+
// Then target instructions (child entity's own context)
|
|
346
|
+
if (targetInstructions) {
|
|
347
|
+
contextParts.push(targetInstructions);
|
|
348
|
+
}
|
|
349
|
+
if (hint) {
|
|
350
|
+
contextParts.push(hint);
|
|
351
|
+
}
|
|
352
|
+
contextParts.push(...parentContextFields);
|
|
353
|
+
const fullContext = contextParts.filter(Boolean).join(' | ');
|
|
354
|
+
// Generate default values for the target entity's fields
|
|
355
|
+
for (const [fieldName, field] of targetEntity.fields) {
|
|
356
|
+
if (!field.isRelation && !field.isOptional) {
|
|
357
|
+
const isPrompt = isPromptField(field);
|
|
358
|
+
if (field.type === 'string' || isPrompt) {
|
|
359
|
+
// For prompt fields, use the type as additional context for generation
|
|
360
|
+
const fieldHint = isPrompt ? field.type : hint;
|
|
361
|
+
generatedData[fieldName] = generateContextAwareValue(fieldName, spec.type, fullContext, fieldHint, contextData);
|
|
362
|
+
}
|
|
363
|
+
else if (field.type === 'number') {
|
|
364
|
+
generatedData[fieldName] = 0;
|
|
365
|
+
}
|
|
366
|
+
else if (field.type === 'boolean') {
|
|
367
|
+
generatedData[fieldName] = false;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
else if (field.isRelation &&
|
|
371
|
+
field.operator === '->' &&
|
|
372
|
+
!field.isArray &&
|
|
373
|
+
!field.isOptional) {
|
|
374
|
+
// Recursively resolve nested forward exact relations
|
|
375
|
+
const nestedSpec = {
|
|
376
|
+
field: fieldName,
|
|
377
|
+
operator: '->',
|
|
378
|
+
type: field.relatedType,
|
|
379
|
+
matchMode: 'exact',
|
|
380
|
+
resolved: false,
|
|
381
|
+
...(field.prompt !== undefined ? { prompt: field.prompt } : {}),
|
|
382
|
+
};
|
|
383
|
+
const nestedId = await resolveReferenceSpec(nestedSpec, generatedData, schema, provider, generateContextAwareValue, generateEntityWithAI);
|
|
384
|
+
if (nestedId) {
|
|
385
|
+
generatedData[fieldName] = nestedId;
|
|
386
|
+
}
|
|
259
387
|
}
|
|
260
388
|
}
|
|
261
389
|
}
|
|
262
390
|
// Use parent ID if available, otherwise use a descriptive string
|
|
263
|
-
const parentId = contextData.$id;
|
|
264
391
|
const created = await provider.create(spec.type, undefined, {
|
|
265
392
|
...generatedData,
|
|
266
393
|
$generated: true,
|
|
267
394
|
$generatedBy: parentId || (spec.matchMode === 'fuzzy' ? 'fuzzy-resolution' : 'reference-resolution'),
|
|
268
395
|
$sourceField: spec.field,
|
|
269
396
|
});
|
|
270
|
-
return created
|
|
397
|
+
return created['$id'];
|
|
271
398
|
}
|
|
272
399
|
// =============================================================================
|
|
273
400
|
// Entity Hydration
|
|
@@ -279,14 +406,31 @@ export async function resolveReferenceSpec(spec, contextData, schema, provider,
|
|
|
279
406
|
* of the related type that have a reference pointing TO this entity.
|
|
280
407
|
* This enables reverse lookups like "get all comments for a post".
|
|
281
408
|
*
|
|
282
|
-
* Backward
|
|
283
|
-
*
|
|
409
|
+
* ## Backward Exact (<-) vs Backward Fuzzy (<~) Resolution
|
|
410
|
+
*
|
|
411
|
+
* This function handles both backward exact and backward fuzzy resolution,
|
|
412
|
+
* but the resolution strategies differ:
|
|
413
|
+
*
|
|
414
|
+
* **Backward Exact (`<-`)**: Uses foreign key lookup (exact ID match)
|
|
415
|
+
* - Queries `provider.list(type, { where: { backrefField: id } })`
|
|
416
|
+
* - Returns all entities that explicitly reference the current entity
|
|
417
|
+
* - Example: `Blog.posts: ['<-Post']` finds Posts where `post.blog === blog.$id`
|
|
418
|
+
*
|
|
419
|
+
* **Backward Fuzzy (`<~`)**: Uses pre-resolved IDs from semantic search
|
|
420
|
+
* - During creation, `resolveBackwardFuzzy()` stores matched IDs
|
|
421
|
+
* - Hydration retrieves entities using those stored IDs
|
|
422
|
+
* - Example: `ICP.as: '<~Occupation'` was resolved via semantic search at creation time
|
|
423
|
+
*
|
|
424
|
+
* ## Resolution Cases
|
|
425
|
+
*
|
|
426
|
+
* - Single backward ref with stored ID: resolve directly (e.g., member.team = teamId)
|
|
284
427
|
* - Single backward ref without stored ID: find related entity that points to us via relations
|
|
285
|
-
* - Array backward ref
|
|
428
|
+
* - Array backward ref with stored IDs: resolve each stored ID (backward fuzzy case)
|
|
429
|
+
* - Array backward ref without stored IDs: query via backref lookup (backward exact case)
|
|
286
430
|
*/
|
|
287
431
|
export function hydrateEntity(data, entity, schema, resolveProvider) {
|
|
288
432
|
const hydrated = { ...data };
|
|
289
|
-
const id = (data
|
|
433
|
+
const id = (data['$id'] || data['id']);
|
|
290
434
|
const typeName = entity.name;
|
|
291
435
|
// Add lazy getters for relations
|
|
292
436
|
for (const [fieldName, field] of entity.fields) {
|
|
@@ -302,9 +446,7 @@ export function hydrateEntity(data, entity, schema, resolveProvider) {
|
|
|
302
446
|
if (!isBackward && !field.isArray && data[fieldName]) {
|
|
303
447
|
const storedId = data[fieldName];
|
|
304
448
|
// For union types, we need to check all possible types
|
|
305
|
-
const typesToSearch = field.unionTypes && field.unionTypes.length > 0
|
|
306
|
-
? field.unionTypes
|
|
307
|
-
: [field.relatedType];
|
|
449
|
+
const typesToSearch = field.unionTypes && field.unionTypes.length > 0 ? field.unionTypes : [field.relatedType];
|
|
308
450
|
// Check if we have a stored matchedType from the creation
|
|
309
451
|
const storedMatchedType = data[`${fieldName}$matchedType`];
|
|
310
452
|
const thenableProxy = new Proxy({}, {
|
|
@@ -352,6 +494,12 @@ export function hydrateEntity(data, entity, schema, resolveProvider) {
|
|
|
352
494
|
if (prop === '$type') {
|
|
353
495
|
return storedMatchedType || field.relatedType;
|
|
354
496
|
}
|
|
497
|
+
// Return the stored ID for $id property access (useful for equality checks)
|
|
498
|
+
if (prop === '$id') {
|
|
499
|
+
return storedId;
|
|
500
|
+
}
|
|
501
|
+
// For any other property access, return undefined
|
|
502
|
+
// Note: Use problem.task.$id or String(problem.task) for equality checks
|
|
355
503
|
return undefined;
|
|
356
504
|
},
|
|
357
505
|
});
|
|
@@ -365,9 +513,7 @@ export function hydrateEntity(data, entity, schema, resolveProvider) {
|
|
|
365
513
|
if (!isBackward && field.isArray && Array.isArray(data[fieldName])) {
|
|
366
514
|
const storedIds = data[fieldName];
|
|
367
515
|
// For union types, we need to check all possible types
|
|
368
|
-
const typesToSearch = field.unionTypes && field.unionTypes.length > 0
|
|
369
|
-
? field.unionTypes
|
|
370
|
-
: [field.relatedType];
|
|
516
|
+
const typesToSearch = field.unionTypes && field.unionTypes.length > 0 ? field.unionTypes : [field.relatedType];
|
|
371
517
|
// Create a proxy that wraps the array but adds thenable behavior
|
|
372
518
|
const thenableArray = new Proxy(storedIds, {
|
|
373
519
|
get(target, prop) {
|
|
@@ -428,9 +574,7 @@ export function hydrateEntity(data, entity, schema, resolveProvider) {
|
|
|
428
574
|
if (storedId) {
|
|
429
575
|
// Has stored ID - directly fetch the related entity
|
|
430
576
|
const result = await provider.get(field.relatedType, storedId);
|
|
431
|
-
return result
|
|
432
|
-
? hydrateEntity(result, relatedEntity, schema, resolveProvider)
|
|
433
|
-
: null;
|
|
577
|
+
return result ? hydrateEntity(result, relatedEntity, schema, resolveProvider) : null;
|
|
434
578
|
}
|
|
435
579
|
// No stored ID - find via inverse relation lookup
|
|
436
580
|
// Find entities of relatedType that have this entity in their relations
|
|
@@ -443,9 +587,9 @@ export function hydrateEntity(data, entity, schema, resolveProvider) {
|
|
|
443
587
|
// Check if any entity of relatedType has this entity in that relation
|
|
444
588
|
const allRelated = await provider.list(field.relatedType);
|
|
445
589
|
for (const candidate of allRelated) {
|
|
446
|
-
const candidateId = (candidate
|
|
590
|
+
const candidateId = (candidate['$id'] || candidate['id']);
|
|
447
591
|
const related = await provider.related(field.relatedType, candidateId, relFieldName);
|
|
448
|
-
if (related.some(r => (r
|
|
592
|
+
if (related.some((r) => (r['$id'] || r['id']) === id)) {
|
|
449
593
|
return hydrateEntity(candidate, relatedEntity, schema, resolveProvider);
|
|
450
594
|
}
|
|
451
595
|
}
|
|
@@ -533,9 +677,7 @@ export function hydrateEntity(data, entity, schema, resolveProvider) {
|
|
|
533
677
|
if (!relatedId)
|
|
534
678
|
return null;
|
|
535
679
|
const result = await provider.get(field.relatedType, relatedId);
|
|
536
|
-
return result
|
|
537
|
-
? hydrateEntity(result, relatedEntity, schema, resolveProvider)
|
|
538
|
-
: null;
|
|
680
|
+
return result ? hydrateEntity(result, relatedEntity, schema, resolveProvider) : null;
|
|
539
681
|
}
|
|
540
682
|
})();
|
|
541
683
|
},
|