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.
Files changed (260) hide show
  1. package/CHANGELOG.md +35 -1
  2. package/README.md +880 -669
  3. package/dist/actions.d.ts +2 -2
  4. package/dist/actions.d.ts.map +1 -1
  5. package/dist/actions.js +1 -1
  6. package/dist/actions.js.map +1 -1
  7. package/dist/ai-promise-db.d.ts +49 -23
  8. package/dist/ai-promise-db.d.ts.map +1 -1
  9. package/dist/ai-promise-db.js +91 -63
  10. package/dist/ai-promise-db.js.map +1 -1
  11. package/dist/authorization.d.ts.map +1 -1
  12. package/dist/authorization.js +38 -30
  13. package/dist/authorization.js.map +1 -1
  14. package/dist/cascade-orchestrator.d.ts +404 -0
  15. package/dist/cascade-orchestrator.d.ts.map +1 -0
  16. package/dist/cascade-orchestrator.js +828 -0
  17. package/dist/cascade-orchestrator.js.map +1 -0
  18. package/dist/cascade-write-strategy.d.ts +584 -0
  19. package/dist/cascade-write-strategy.d.ts.map +1 -0
  20. package/dist/cascade-write-strategy.js +590 -0
  21. package/dist/cascade-write-strategy.js.map +1 -0
  22. package/dist/ch-adapter.d.ts +358 -0
  23. package/dist/ch-adapter.d.ts.map +1 -0
  24. package/dist/ch-adapter.js +929 -0
  25. package/dist/ch-adapter.js.map +1 -0
  26. package/dist/client/index.d.ts +42 -0
  27. package/dist/client/index.d.ts.map +1 -0
  28. package/dist/client/index.js +43 -0
  29. package/dist/client/index.js.map +1 -0
  30. package/dist/client.d.ts +266 -0
  31. package/dist/client.d.ts.map +1 -0
  32. package/dist/client.js +81 -0
  33. package/dist/client.js.map +1 -0
  34. package/dist/constants.d.ts +64 -1
  35. package/dist/constants.d.ts.map +1 -1
  36. package/dist/constants.js +52 -2
  37. package/dist/constants.js.map +1 -1
  38. package/dist/dataloader.d.ts +99 -0
  39. package/dist/dataloader.d.ts.map +1 -0
  40. package/dist/dataloader.js +225 -0
  41. package/dist/dataloader.js.map +1 -0
  42. package/dist/db-provider-port.d.ts +501 -0
  43. package/dist/db-provider-port.d.ts.map +1 -0
  44. package/dist/db-provider-port.js +113 -0
  45. package/dist/db-provider-port.js.map +1 -0
  46. package/dist/digital-objects-provider.d.ts +49 -0
  47. package/dist/digital-objects-provider.d.ts.map +1 -0
  48. package/dist/digital-objects-provider.js +55 -0
  49. package/dist/digital-objects-provider.js.map +1 -0
  50. package/dist/do-sqlite-adapter.d.ts +402 -0
  51. package/dist/do-sqlite-adapter.d.ts.map +1 -0
  52. package/dist/do-sqlite-adapter.js +745 -0
  53. package/dist/do-sqlite-adapter.js.map +1 -0
  54. package/dist/docs-rels/custom-types.d.ts +134 -0
  55. package/dist/docs-rels/custom-types.d.ts.map +1 -0
  56. package/dist/docs-rels/custom-types.js +70 -0
  57. package/dist/docs-rels/custom-types.js.map +1 -0
  58. package/dist/docs-rels/index.d.ts +16 -0
  59. package/dist/docs-rels/index.d.ts.map +1 -0
  60. package/dist/docs-rels/index.js +16 -0
  61. package/dist/docs-rels/index.js.map +1 -0
  62. package/dist/docs-rels/migrations/index.d.ts +30 -0
  63. package/dist/docs-rels/migrations/index.d.ts.map +1 -0
  64. package/dist/docs-rels/migrations/index.js +128 -0
  65. package/dist/docs-rels/migrations/index.js.map +1 -0
  66. package/dist/docs-rels/schema.d.ts +2961 -0
  67. package/dist/docs-rels/schema.d.ts.map +1 -0
  68. package/dist/docs-rels/schema.js +244 -0
  69. package/dist/docs-rels/schema.js.map +1 -0
  70. package/dist/durable-clickhouse.d.ts.map +1 -1
  71. package/dist/durable-clickhouse.js +16 -13
  72. package/dist/durable-clickhouse.js.map +1 -1
  73. package/dist/durable-promise.d.ts.map +1 -1
  74. package/dist/durable-promise.js +34 -15
  75. package/dist/durable-promise.js.map +1 -1
  76. package/dist/errors.d.ts +127 -0
  77. package/dist/errors.d.ts.map +1 -0
  78. package/dist/errors.js +210 -0
  79. package/dist/errors.js.map +1 -0
  80. package/dist/eventbridge.d.ts +117 -0
  81. package/dist/eventbridge.d.ts.map +1 -0
  82. package/dist/eventbridge.js +238 -0
  83. package/dist/eventbridge.js.map +1 -0
  84. package/dist/events.d.ts +2 -2
  85. package/dist/events.d.ts.map +1 -1
  86. package/dist/events.js +1 -1
  87. package/dist/events.js.map +1 -1
  88. package/dist/execution-queue.d.ts.map +1 -1
  89. package/dist/execution-queue.js +4 -5
  90. package/dist/execution-queue.js.map +1 -1
  91. package/dist/index.d.ts +35 -8
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.js +106 -6
  94. package/dist/index.js.map +1 -1
  95. package/dist/linguistic.d.ts +3 -108
  96. package/dist/linguistic.d.ts.map +1 -1
  97. package/dist/linguistic.js +3 -372
  98. package/dist/linguistic.js.map +1 -1
  99. package/dist/logger.d.ts +132 -0
  100. package/dist/logger.d.ts.map +1 -0
  101. package/dist/logger.js +137 -0
  102. package/dist/logger.js.map +1 -0
  103. package/dist/memory-provider.d.ts +128 -0
  104. package/dist/memory-provider.d.ts.map +1 -1
  105. package/dist/memory-provider.js +592 -257
  106. package/dist/memory-provider.js.map +1 -1
  107. package/dist/pg-adapter.d.ts +424 -0
  108. package/dist/pg-adapter.d.ts.map +1 -0
  109. package/dist/pg-adapter.js +921 -0
  110. package/dist/pg-adapter.js.map +1 -0
  111. package/dist/pipelines-iceberg-emitter.d.ts +327 -0
  112. package/dist/pipelines-iceberg-emitter.d.ts.map +1 -0
  113. package/dist/pipelines-iceberg-emitter.js +351 -0
  114. package/dist/pipelines-iceberg-emitter.js.map +1 -0
  115. package/dist/provider-capabilities.d.ts +146 -0
  116. package/dist/provider-capabilities.d.ts.map +1 -0
  117. package/dist/provider-capabilities.js +214 -0
  118. package/dist/provider-capabilities.js.map +1 -0
  119. package/dist/rdb-provider-adapter.d.ts +195 -0
  120. package/dist/rdb-provider-adapter.d.ts.map +1 -0
  121. package/dist/rdb-provider-adapter.js +291 -0
  122. package/dist/rdb-provider-adapter.js.map +1 -0
  123. package/dist/schema/cascade.d.ts +48 -17
  124. package/dist/schema/cascade.d.ts.map +1 -1
  125. package/dist/schema/cascade.js +477 -278
  126. package/dist/schema/cascade.js.map +1 -1
  127. package/dist/schema/definition-caches.d.ts +24 -0
  128. package/dist/schema/definition-caches.d.ts.map +1 -0
  129. package/dist/schema/definition-caches.js +26 -0
  130. package/dist/schema/definition-caches.js.map +1 -0
  131. package/dist/schema/dependency-graph.d.ts +21 -109
  132. package/dist/schema/dependency-graph.d.ts.map +1 -1
  133. package/dist/schema/dependency-graph.js +25 -333
  134. package/dist/schema/dependency-graph.js.map +1 -1
  135. package/dist/schema/diff.d.ts +103 -0
  136. package/dist/schema/diff.d.ts.map +1 -0
  137. package/dist/schema/diff.js +329 -0
  138. package/dist/schema/diff.js.map +1 -0
  139. package/dist/schema/entity-operations.d.ts +99 -0
  140. package/dist/schema/entity-operations.d.ts.map +1 -0
  141. package/dist/schema/entity-operations.js +818 -0
  142. package/dist/schema/entity-operations.js.map +1 -0
  143. package/dist/schema/index.d.ts +28 -34
  144. package/dist/schema/index.d.ts.map +1 -1
  145. package/dist/schema/index.js +454 -521
  146. package/dist/schema/index.js.map +1 -1
  147. package/dist/schema/migration.d.ts +205 -0
  148. package/dist/schema/migration.d.ts.map +1 -0
  149. package/dist/schema/migration.js +327 -0
  150. package/dist/schema/migration.js.map +1 -0
  151. package/dist/schema/nl-query-generator.d.ts +68 -0
  152. package/dist/schema/nl-query-generator.d.ts.map +1 -0
  153. package/dist/schema/nl-query-generator.js +362 -0
  154. package/dist/schema/nl-query-generator.js.map +1 -0
  155. package/dist/schema/nl-query.d.ts +65 -0
  156. package/dist/schema/nl-query.d.ts.map +1 -0
  157. package/dist/schema/nl-query.js +178 -0
  158. package/dist/schema/nl-query.js.map +1 -0
  159. package/dist/schema/parse.d.ts.map +1 -1
  160. package/dist/schema/parse.js +144 -89
  161. package/dist/schema/parse.js.map +1 -1
  162. package/dist/schema/provider.d.ts +37 -0
  163. package/dist/schema/provider.d.ts.map +1 -1
  164. package/dist/schema/provider.js +15 -7
  165. package/dist/schema/provider.js.map +1 -1
  166. package/dist/schema/resolve.d.ts +46 -5
  167. package/dist/schema/resolve.d.ts.map +1 -1
  168. package/dist/schema/resolve.js +237 -95
  169. package/dist/schema/resolve.js.map +1 -1
  170. package/dist/schema/search-utils.d.ts +76 -0
  171. package/dist/schema/search-utils.d.ts.map +1 -0
  172. package/dist/schema/search-utils.js +86 -0
  173. package/dist/schema/search-utils.js.map +1 -0
  174. package/dist/schema/seed.d.ts +53 -0
  175. package/dist/schema/seed.d.ts.map +1 -0
  176. package/dist/schema/seed.js +94 -0
  177. package/dist/schema/seed.js.map +1 -0
  178. package/dist/schema/semantic.d.ts +10 -0
  179. package/dist/schema/semantic.d.ts.map +1 -1
  180. package/dist/schema/semantic.js +192 -86
  181. package/dist/schema/semantic.js.map +1 -1
  182. package/dist/schema/sub-apis.d.ts +52 -0
  183. package/dist/schema/sub-apis.d.ts.map +1 -0
  184. package/dist/schema/sub-apis.js +216 -0
  185. package/dist/schema/sub-apis.js.map +1 -0
  186. package/dist/schema/system-entities.d.ts +42 -0
  187. package/dist/schema/system-entities.d.ts.map +1 -0
  188. package/dist/schema/system-entities.js +101 -0
  189. package/dist/schema/system-entities.js.map +1 -0
  190. package/dist/schema/types.d.ts +91 -9
  191. package/dist/schema/types.d.ts.map +1 -1
  192. package/dist/schema/union-fallback.d.ts.map +1 -1
  193. package/dist/schema/union-fallback.js +21 -15
  194. package/dist/schema/union-fallback.js.map +1 -1
  195. package/dist/schema/value-generators/ai.d.ts +54 -0
  196. package/dist/schema/value-generators/ai.d.ts.map +1 -0
  197. package/dist/schema/value-generators/ai.js +136 -0
  198. package/dist/schema/value-generators/ai.js.map +1 -0
  199. package/dist/schema/value-generators/index.d.ts +126 -0
  200. package/dist/schema/value-generators/index.d.ts.map +1 -0
  201. package/dist/schema/value-generators/index.js +219 -0
  202. package/dist/schema/value-generators/index.js.map +1 -0
  203. package/dist/schema/value-generators/placeholder.d.ts +52 -0
  204. package/dist/schema/value-generators/placeholder.d.ts.map +1 -0
  205. package/dist/schema/value-generators/placeholder.js +328 -0
  206. package/dist/schema/value-generators/placeholder.js.map +1 -0
  207. package/dist/schema/value-generators/types.d.ts +116 -0
  208. package/dist/schema/value-generators/types.d.ts.map +1 -0
  209. package/dist/schema/value-generators/types.js +11 -0
  210. package/dist/schema/value-generators/types.js.map +1 -0
  211. package/dist/schema/version.d.ts +111 -0
  212. package/dist/schema/version.d.ts.map +1 -0
  213. package/dist/schema/version.js +190 -0
  214. package/dist/schema/version.js.map +1 -0
  215. package/dist/schema.d.ts +1095 -24
  216. package/dist/schema.d.ts.map +1 -1
  217. package/dist/schema.js +2852 -40
  218. package/dist/schema.js.map +1 -1
  219. package/dist/semantic-vectors.d.ts +39 -0
  220. package/dist/semantic-vectors.d.ts.map +1 -0
  221. package/dist/semantic-vectors.js +334 -0
  222. package/dist/semantic-vectors.js.map +1 -0
  223. package/dist/semantic.d.ts +29 -1
  224. package/dist/semantic.d.ts.map +1 -1
  225. package/dist/semantic.js +26 -16
  226. package/dist/semantic.js.map +1 -1
  227. package/dist/telemetry.d.ts +128 -0
  228. package/dist/telemetry.d.ts.map +1 -0
  229. package/dist/telemetry.js +305 -0
  230. package/dist/telemetry.js.map +1 -0
  231. package/dist/tests.d.ts.map +1 -1
  232. package/dist/tests.js +30 -22
  233. package/dist/tests.js.map +1 -1
  234. package/dist/type-guards.d.ts +50 -5
  235. package/dist/type-guards.d.ts.map +1 -1
  236. package/dist/type-guards.js +87 -16
  237. package/dist/type-guards.js.map +1 -1
  238. package/dist/types.d.ts +33 -245
  239. package/dist/types.d.ts.map +1 -1
  240. package/dist/types.js +62 -72
  241. package/dist/types.js.map +1 -1
  242. package/dist/validation.d.ts +2 -5
  243. package/dist/validation.d.ts.map +1 -1
  244. package/dist/validation.js +65 -93
  245. package/dist/validation.js.map +1 -1
  246. package/dist/worker/db-provider.d.ts +168 -0
  247. package/dist/worker/db-provider.d.ts.map +1 -0
  248. package/dist/worker/db-provider.js +277 -0
  249. package/dist/worker/db-provider.js.map +1 -0
  250. package/dist/worker/index.d.ts +35 -0
  251. package/dist/worker/index.d.ts.map +1 -0
  252. package/dist/worker/index.js +37 -0
  253. package/dist/worker/index.js.map +1 -0
  254. package/dist/worker.d.ts +779 -0
  255. package/dist/worker.d.ts.map +1 -0
  256. package/dist/worker.js +2786 -0
  257. package/dist/worker.js.map +1 -0
  258. package/package.json +46 -16
  259. package/src/docs-rels/migrations/0001-init.sql +125 -0
  260. package/LICENSE +0 -21
@@ -0,0 +1,818 @@
1
+ /**
2
+ * Entity Operations Factory
3
+ *
4
+ * Contains the createEntityOperations<T>() function that creates typed CRUD operations
5
+ * for database entities. This includes get, list, find, create, update, upsert, delete,
6
+ * search, forEach, draft, and resolve operations.
7
+ *
8
+ * @packageDocumentation
9
+ */
10
+ import { resolveProvider, hasSemanticSearch, hasHybridSearch, hasTransactionSupport, } from './provider.js';
11
+ import { hydrateEntity, resolveReferenceSpec, isPromptField } from './resolve.js';
12
+ import { resolveForwardExact, generateAIFields, generateContextAwareValue, generateNaturalLanguageContent, generateEntity, } from './cascade.js';
13
+ import { resolveBackwardFuzzy, resolveForwardFuzzy, getFuzzyThreshold } from './semantic.js';
14
+ import { isNotFoundError, isEntityExistsError, wrapDatabaseError, DatabaseError, CapabilityNotSupportedError, } from '../errors.js';
15
+ // =============================================================================
16
+ // Helper Functions
17
+ // =============================================================================
18
+ /** Metadata field suffixes that should be stripped from entity data */
19
+ const METADATA_SUFFIXES = ['$matchedType', '$score', '$fallbackUsed', '$searchedTypes'];
20
+ /**
21
+ * Check if a key is a metadata field that should be excluded from entity data
22
+ */
23
+ function isMetadataField(key) {
24
+ if (key.startsWith('$'))
25
+ return true;
26
+ return METADATA_SUFFIXES.some((suffix) => key.endsWith(suffix));
27
+ }
28
+ /**
29
+ * Remove metadata fields from entity data
30
+ */
31
+ function cleanEntityData(data) {
32
+ const cleaned = {};
33
+ for (const [key, value] of Object.entries(data)) {
34
+ if (!isMetadataField(key)) {
35
+ cleaned[key] = value;
36
+ }
37
+ }
38
+ return cleaned;
39
+ }
40
+ /**
41
+ * Apply where filters to a list of records
42
+ */
43
+ function applyWhereFilter(records, where) {
44
+ return records.filter((record) => Object.entries(where).every(([key, value]) => record[key] === value));
45
+ }
46
+ /**
47
+ * Apply pagination (offset/limit) to a list of records
48
+ */
49
+ function applyPagination(records, offset, limit) {
50
+ if (!limit)
51
+ return records;
52
+ const start = offset ?? 0;
53
+ return records.slice(start, start + limit);
54
+ }
55
+ // =============================================================================
56
+ // Entity Operations Factory
57
+ // =============================================================================
58
+ /**
59
+ * Create typed CRUD operations for a database entity
60
+ *
61
+ * This function creates the full set of operations for working with a specific
62
+ * entity type in the database, including:
63
+ * - get/list/find for reading
64
+ * - create/update/upsert/delete for writing
65
+ * - search/semanticSearch/hybridSearch for querying
66
+ * - forEach for iteration
67
+ * - draft/resolve for two-phase entity creation
68
+ *
69
+ * @param typeName - The type name for the entity
70
+ * @param entity - The parsed entity definition
71
+ * @param schema - The full parsed schema
72
+ * @returns EntityOperations<T> with all available methods
73
+ */
74
+ export function createEntityOperations(typeName, entity, schema) {
75
+ return {
76
+ async get(id) {
77
+ const provider = await resolveProvider();
78
+ const result = await provider.get(typeName, id);
79
+ if (!result)
80
+ return null;
81
+ return hydrateEntity(result, entity, schema, resolveProvider);
82
+ },
83
+ async list(options) {
84
+ try {
85
+ const provider = await resolveProvider();
86
+ const results = await provider.list(typeName, options);
87
+ return Promise.all(results.map((r) => hydrateEntity(r, entity, schema, resolveProvider)));
88
+ }
89
+ catch (error) {
90
+ // Handle error with callback if provided
91
+ if (options?.onError) {
92
+ const wrappedError = error instanceof Error ? error : new Error(String(error));
93
+ const fallback = options.onError(wrappedError);
94
+ return (fallback ?? []);
95
+ }
96
+ // Suppress errors if requested
97
+ if (options?.suppressErrors) {
98
+ return [];
99
+ }
100
+ throw error;
101
+ }
102
+ },
103
+ async find(where) {
104
+ const provider = await resolveProvider();
105
+ const results = await provider.list(typeName, {
106
+ where: where,
107
+ });
108
+ return Promise.all(results.map((r) => hydrateEntity(r, entity, schema, resolveProvider)));
109
+ },
110
+ async search(query, options) {
111
+ const provider = await resolveProvider();
112
+ const results = await provider.search(typeName, query, options);
113
+ return Promise.all(results.map((r) => hydrateEntity(r, entity, schema, resolveProvider)));
114
+ },
115
+ async create(idOrData, maybeData, maybeOptions) {
116
+ const providedId = typeof idOrData === 'string' ? idOrData : undefined;
117
+ const data = typeof idOrData === 'string'
118
+ ? maybeData
119
+ : idOrData;
120
+ // Options can be passed as third arg when id is provided, or as second arg when id is not
121
+ const options = typeof idOrData === 'string' ? maybeOptions : maybeData;
122
+ const entityId = providedId || crypto.randomUUID();
123
+ try {
124
+ const provider = await resolveProvider();
125
+ // Use transaction if provider supports it, to ensure atomicity of cascade
126
+ const useTransaction = hasTransactionSupport(provider);
127
+ const txn = useTransaction ? await provider.beginTransaction() : null;
128
+ // The target for write operations: transaction if available, otherwise provider directly
129
+ const writeTarget = txn ?? provider;
130
+ try {
131
+ let finalData;
132
+ let pendingRelations = [];
133
+ let fuzzyPendingRelations = [];
134
+ // Check if data has already been through draft/resolve (called from schema.ts wrapper)
135
+ if (options?._preResolved) {
136
+ // Data is already resolved - skip draft/resolve phases
137
+ const cleanedData = cleanEntityData(data);
138
+ // Still need to resolve backward fuzzy references and generate AI fields
139
+ const backwardResolvedData = await resolveBackwardFuzzy(typeName, cleanedData, entity, schema, provider);
140
+ finalData = await generateAIFields(backwardResolvedData, typeName, entity, schema, provider);
141
+ // Extract pending relations from already-resolved data by checking field values
142
+ // that look like entity IDs and match relation field definitions
143
+ for (const [fieldName, field] of entity.fields) {
144
+ if (field.isRelation && field.relatedType) {
145
+ const resolvedValue = cleanedData[fieldName];
146
+ if (!resolvedValue)
147
+ continue;
148
+ const isFuzzy = field.operator === '~>' || (field.matchMode && field.matchMode === 'fuzzy');
149
+ if (Array.isArray(resolvedValue)) {
150
+ for (const targetId of resolvedValue) {
151
+ if (typeof targetId === 'string') {
152
+ if (isFuzzy) {
153
+ fuzzyPendingRelations.push({
154
+ fieldName,
155
+ targetType: field.relatedType,
156
+ targetId,
157
+ });
158
+ }
159
+ else {
160
+ pendingRelations.push({
161
+ fieldName,
162
+ targetType: field.relatedType,
163
+ targetId,
164
+ });
165
+ }
166
+ }
167
+ }
168
+ }
169
+ else if (typeof resolvedValue === 'string') {
170
+ if (isFuzzy) {
171
+ fuzzyPendingRelations.push({
172
+ fieldName,
173
+ targetType: field.relatedType,
174
+ targetId: resolvedValue,
175
+ });
176
+ }
177
+ else {
178
+ pendingRelations.push({
179
+ fieldName,
180
+ targetType: field.relatedType,
181
+ targetId: resolvedValue,
182
+ });
183
+ }
184
+ }
185
+ }
186
+ }
187
+ }
188
+ else {
189
+ // Phase 1: Draft - generate entity with $refs for unresolved references
190
+ // Use _skipPromptlessRefs to avoid generating refs for array fields without prompts
191
+ // (those are handled by cascade generation when enabled)
192
+ const draft = await this.draft(data, {
193
+ _skipPromptlessRefs: true,
194
+ });
195
+ // Phase 2: Resolve - resolve $refs to actual entity IDs
196
+ // Inject $id and $type into draft for context during resolution
197
+ const draftWithContext = draft;
198
+ draftWithContext['$id'] = entityId;
199
+ draftWithContext['$type'] = typeName;
200
+ const resolved = await this.resolve(draft);
201
+ const resolvedData = resolved;
202
+ // Extract pending relations from resolved data
203
+ // The resolve phase sets metadata fields like ${field}$matchedType and ${field}$score
204
+ const refs = draft['$refs'];
205
+ if (refs) {
206
+ for (const [fieldName, refSpec] of Object.entries(refs)) {
207
+ const resolvedValue = resolvedData[fieldName];
208
+ if (!resolvedValue)
209
+ continue;
210
+ if (Array.isArray(refSpec)) {
211
+ // Array of references
212
+ const resolvedIds = Array.isArray(resolvedValue) ? resolvedValue : [resolvedValue];
213
+ for (let i = 0; i < resolvedIds.length; i++) {
214
+ const targetId = resolvedIds[i];
215
+ const spec = refSpec[i] || refSpec[0];
216
+ if (spec.matchMode === 'fuzzy') {
217
+ const matchedType = resolvedData[`${fieldName}$matchedType`];
218
+ const similarity = resolvedData[`${fieldName}$score`];
219
+ fuzzyPendingRelations.push({
220
+ fieldName,
221
+ targetType: matchedType || spec.type,
222
+ targetId,
223
+ ...(similarity !== undefined && { similarity }),
224
+ ...(matchedType !== undefined && { matchedType }),
225
+ });
226
+ }
227
+ else {
228
+ pendingRelations.push({
229
+ fieldName,
230
+ targetType: spec.type,
231
+ targetId,
232
+ });
233
+ }
234
+ }
235
+ }
236
+ else {
237
+ // Single reference
238
+ const targetId = resolvedValue;
239
+ if (refSpec.matchMode === 'fuzzy') {
240
+ const matchedType = resolvedData[`${fieldName}$matchedType`];
241
+ const similarity = resolvedData[`${fieldName}$score`];
242
+ fuzzyPendingRelations.push({
243
+ fieldName,
244
+ targetType: matchedType || refSpec.type,
245
+ targetId,
246
+ ...(similarity !== undefined && { similarity }),
247
+ ...(matchedType !== undefined && { matchedType }),
248
+ });
249
+ }
250
+ else {
251
+ pendingRelations.push({
252
+ fieldName,
253
+ targetType: refSpec.type,
254
+ targetId,
255
+ });
256
+ }
257
+ }
258
+ }
259
+ }
260
+ // Clean up resolution metadata from the data before further processing
261
+ const cleanedData = cleanEntityData(resolvedData);
262
+ // Phase 3: Resolve backward fuzzy references (<~)
263
+ const backwardResolvedData = await resolveBackwardFuzzy(typeName, cleanedData, entity, schema, provider);
264
+ // Phase 4: Generate AI fields based on $instructions and $context
265
+ finalData = await generateAIFields(backwardResolvedData, typeName, entity, schema, provider);
266
+ }
267
+ // Phase 5: Persist the entity
268
+ const result = await writeTarget.create(typeName, entityId, finalData);
269
+ // Phase 6: Create relationship edges for exact forward references
270
+ for (const rel of pendingRelations) {
271
+ await writeTarget.relate(typeName, entityId, rel.fieldName, rel.targetType, rel.targetId);
272
+ }
273
+ // Phase 7: Create relationship edges for fuzzy forward references
274
+ const createdEdgeIds = new Set();
275
+ for (const rel of fuzzyPendingRelations) {
276
+ await writeTarget.relate(typeName, entityId, rel.fieldName, rel.targetType, rel.targetId, {
277
+ matchMode: 'fuzzy',
278
+ ...(rel.similarity !== undefined && { similarity: rel.similarity }),
279
+ ...(rel.matchedType !== undefined && { matchedType: rel.matchedType }),
280
+ });
281
+ const edgeId = `${typeName}_${rel.fieldName}_${entityId}_${rel.targetId}`;
282
+ if (!createdEdgeIds.has(edgeId)) {
283
+ createdEdgeIds.add(edgeId);
284
+ try {
285
+ await writeTarget.create('Edge', edgeId, {
286
+ from: typeName,
287
+ name: rel.fieldName,
288
+ to: rel.targetType,
289
+ direction: 'forward',
290
+ matchMode: 'fuzzy',
291
+ similarity: rel.similarity,
292
+ matchedType: rel.matchedType,
293
+ fromId: entityId,
294
+ toId: rel.targetId,
295
+ });
296
+ }
297
+ catch (error) {
298
+ // Only ignore actual duplicate key errors, propagate other errors
299
+ if (!isEntityExistsError(error)) {
300
+ throw wrapDatabaseError(error, 'create', 'Edge', edgeId);
301
+ }
302
+ }
303
+ }
304
+ }
305
+ // Commit the transaction if we used one
306
+ if (txn)
307
+ await txn.commit();
308
+ return hydrateEntity(result, entity, schema, resolveProvider);
309
+ }
310
+ catch (innerError) {
311
+ // Rollback on any error if we have a transaction
312
+ if (txn) {
313
+ try {
314
+ await txn.rollback();
315
+ }
316
+ catch {
317
+ // Ignore rollback errors
318
+ }
319
+ }
320
+ throw innerError;
321
+ }
322
+ }
323
+ catch (error) {
324
+ // Wrap provider errors with context
325
+ if (error instanceof DatabaseError)
326
+ throw error;
327
+ throw wrapDatabaseError(error, 'create', typeName, entityId);
328
+ }
329
+ },
330
+ async update(id, data) {
331
+ try {
332
+ const provider = await resolveProvider();
333
+ const result = await provider.update(typeName, id, data);
334
+ return hydrateEntity(result, entity, schema, resolveProvider);
335
+ }
336
+ catch (error) {
337
+ if (error instanceof DatabaseError)
338
+ throw error;
339
+ throw wrapDatabaseError(error, 'update', typeName, id);
340
+ }
341
+ },
342
+ async upsert(id, data) {
343
+ try {
344
+ const provider = await resolveProvider();
345
+ const existing = await provider.get(typeName, id);
346
+ if (existing) {
347
+ const result = await provider.update(typeName, id, data);
348
+ return hydrateEntity(result, entity, schema, resolveProvider);
349
+ }
350
+ const result = await provider.create(typeName, id, data);
351
+ return hydrateEntity(result, entity, schema, resolveProvider);
352
+ }
353
+ catch (error) {
354
+ if (error instanceof DatabaseError)
355
+ throw error;
356
+ throw wrapDatabaseError(error, 'upsert', typeName, id);
357
+ }
358
+ },
359
+ async delete(id) {
360
+ try {
361
+ const provider = await resolveProvider();
362
+ return provider.delete(typeName, id);
363
+ }
364
+ catch (error) {
365
+ if (error instanceof DatabaseError)
366
+ throw error;
367
+ throw wrapDatabaseError(error, 'delete', typeName, id);
368
+ }
369
+ },
370
+ async forEach(optionsOrCallback, maybeCallback) {
371
+ const options = typeof optionsOrCallback === 'function' ? undefined : optionsOrCallback;
372
+ const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : maybeCallback;
373
+ const items = await this.list(options);
374
+ for (const item of items) {
375
+ await callback(item);
376
+ }
377
+ },
378
+ async semanticSearch(query, options) {
379
+ const provider = await resolveProvider();
380
+ if (!hasSemanticSearch(provider)) {
381
+ throw new CapabilityNotSupportedError('hasSemanticSearch', `Semantic search is not supported by the current provider. ` +
382
+ `The provider does not implement the semanticSearch method required for vector similarity search.`, `Use the regular search() method instead, which performs basic text matching.`);
383
+ }
384
+ const results = await provider.semanticSearch(typeName, query, options);
385
+ return Promise.all(results.map((r) => ({
386
+ ...hydrateEntity(r, entity, schema, resolveProvider),
387
+ $score: r.$score,
388
+ })));
389
+ },
390
+ async hybridSearch(query, options) {
391
+ const provider = await resolveProvider();
392
+ if (!hasHybridSearch(provider)) {
393
+ throw new CapabilityNotSupportedError('hasHybridSearch', `Hybrid search is not supported by the current provider. ` +
394
+ `The provider does not implement the hybridSearch method required for combined FTS and vector search.`, `Use the regular search() method instead, which performs basic text matching.`);
395
+ }
396
+ const results = await provider.hybridSearch(typeName, query, options);
397
+ return Promise.all(results.map((r) => ({
398
+ ...hydrateEntity(r, entity, schema, resolveProvider),
399
+ $rrfScore: r.$rrfScore,
400
+ $ftsRank: r.$ftsRank,
401
+ $semanticRank: r.$semanticRank,
402
+ $score: r.$score,
403
+ })));
404
+ },
405
+ async draft(data, options) {
406
+ const draftData = { ...data, $phase: 'draft' };
407
+ const refs = {};
408
+ // Get the raw schema to detect prompt fields and source instructions
409
+ const rawSchema = entity.schema || {};
410
+ const sourceInstructions = rawSchema['$instructions'];
411
+ const hasContextDependencies = Array.isArray(rawSchema['$context']) && rawSchema['$context'].length > 0;
412
+ for (const [fieldName, field] of entity.fields) {
413
+ if (draftData[fieldName] !== undefined && draftData[fieldName] !== null) {
414
+ continue;
415
+ }
416
+ if (field.operator && field.relatedType) {
417
+ // Skip optional relation fields - they shouldn't auto-generate
418
+ if (field.isOptional)
419
+ continue;
420
+ // Skip backward references - they're resolved lazily via hydrateEntity
421
+ // Backward refs find entities that reference US, not entities we create
422
+ if (field.operator === '<-' || field.operator === '<~')
423
+ continue;
424
+ // Relationship field with operator
425
+ const matchMode = field.matchMode ?? (field.operator.includes('~') ? 'fuzzy' : 'exact');
426
+ if (field.isArray) {
427
+ // Get hint value for array fuzzy matching (e.g., categoriesHint for categories field)
428
+ const hintKey = `${fieldName}Hint`;
429
+ const hintValue = data[hintKey];
430
+ // Get fuzzy threshold from entity schema
431
+ const threshold = field.threshold ?? getFuzzyThreshold(entity);
432
+ // If hint is an array, create one ref spec per hint item
433
+ // Skip promptless fields only when _skipPromptlessRefs is set (internal create() without cascade)
434
+ const shouldSkipPromptless = options?._skipPromptlessRefs && !field.prompt;
435
+ const hints = Array.isArray(hintValue)
436
+ ? hintValue
437
+ : hintValue
438
+ ? [hintValue]
439
+ : shouldSkipPromptless
440
+ ? []
441
+ : [
442
+ generateNaturalLanguageContent(fieldName, field.prompt, field.relatedType, data),
443
+ ];
444
+ const refSpecs = hints.map((hint) => {
445
+ const generatedText = String(hint);
446
+ const spec = {
447
+ field: fieldName,
448
+ operator: field.operator,
449
+ type: field.relatedType,
450
+ matchMode,
451
+ resolved: false,
452
+ ...(field.unionTypes !== undefined && { unionTypes: field.unionTypes }),
453
+ ...(field.prompt !== undefined && { prompt: field.prompt }),
454
+ ...(generatedText !== undefined && { generatedText }),
455
+ ...(sourceInstructions !== undefined && { sourceInstructions }),
456
+ ...(threshold !== undefined && { threshold }),
457
+ };
458
+ return spec;
459
+ });
460
+ // Store the combined generated text for the draft display
461
+ draftData[fieldName] = hints.map(String).join(', ');
462
+ refs[fieldName] = refSpecs;
463
+ if (options?.stream && options.onChunk) {
464
+ for (const spec of refSpecs) {
465
+ if (spec.generatedText) {
466
+ options.onChunk(spec.generatedText);
467
+ }
468
+ }
469
+ }
470
+ }
471
+ else {
472
+ // Get hint value for fuzzy matching (e.g., contentHint for content field)
473
+ const hintKey = `${fieldName}Hint`;
474
+ const hintValue = data[hintKey];
475
+ // Get fuzzy threshold from entity schema
476
+ const threshold = field.threshold ?? getFuzzyThreshold(entity);
477
+ // Use hint if available, otherwise generate natural language content
478
+ const generatedText = hintValue ||
479
+ generateNaturalLanguageContent(fieldName, field.prompt, field.relatedType, data);
480
+ draftData[fieldName] = generatedText;
481
+ const spec = {
482
+ field: fieldName,
483
+ operator: field.operator,
484
+ type: field.relatedType,
485
+ matchMode,
486
+ resolved: false,
487
+ ...(field.unionTypes !== undefined && { unionTypes: field.unionTypes }),
488
+ ...(field.prompt !== undefined && { prompt: field.prompt }),
489
+ ...(generatedText !== undefined && { generatedText }),
490
+ ...(sourceInstructions !== undefined && { sourceInstructions }),
491
+ ...(threshold !== undefined && { threshold }),
492
+ };
493
+ refs[fieldName] = spec;
494
+ if (options?.stream && options.onChunk) {
495
+ options.onChunk(generatedText);
496
+ }
497
+ }
498
+ }
499
+ else if (!field.isRelation) {
500
+ // Non-relationship field - check if it's a prompt field
501
+ const isPrompt = isPromptField(field);
502
+ if (isPrompt && !hasContextDependencies) {
503
+ // Generate content for prompt field using the type as the prompt
504
+ // NOTE: Skip generation when entity has $context dependencies, as those fields
505
+ // need the pre-fetched context to generate properly (done in generateAIFields)
506
+ const generatedText = generateContextAwareValue(fieldName, typeName, field.type, field.type, data);
507
+ draftData[fieldName] = generatedText;
508
+ if (options?.stream && options.onChunk) {
509
+ options.onChunk(generatedText);
510
+ }
511
+ }
512
+ }
513
+ }
514
+ draftData['$refs'] = refs;
515
+ return draftData;
516
+ },
517
+ async resolve(draft, options) {
518
+ // Draft<T> interface requires $phase: 'draft', so we can access it directly
519
+ if (draft.$phase !== 'draft') {
520
+ throw new Error('Cannot resolve entity: not a draft (missing $phase: "draft")');
521
+ }
522
+ const provider = await resolveProvider();
523
+ const resolved = { ...draft };
524
+ const errors = [];
525
+ delete resolved['$refs'];
526
+ resolved['$phase'] = 'resolved';
527
+ const refs = draft.$refs;
528
+ for (const [fieldName, refSpec] of Object.entries(refs)) {
529
+ try {
530
+ if (Array.isArray(refSpec)) {
531
+ const resolvedIds = [];
532
+ for (const spec of refSpec) {
533
+ const resolvedId = await resolveReferenceSpec(spec, resolved, schema, provider, generateContextAwareValue, generateEntity);
534
+ if (resolvedId) {
535
+ resolvedIds.push(resolvedId);
536
+ options?.onResolved?.(fieldName, resolvedId);
537
+ }
538
+ }
539
+ resolved[fieldName] = resolvedIds;
540
+ }
541
+ else {
542
+ const resolvedId = await resolveReferenceSpec(refSpec, resolved, schema, provider, generateContextAwareValue, generateEntity);
543
+ if (resolvedId) {
544
+ resolved[fieldName] = resolvedId;
545
+ options?.onResolved?.(fieldName, resolvedId);
546
+ }
547
+ }
548
+ }
549
+ catch (err) {
550
+ const errorMsg = err instanceof Error ? err.message : String(err);
551
+ if (options?.onError === 'skip') {
552
+ errors.push({ field: fieldName, error: errorMsg });
553
+ }
554
+ else {
555
+ throw err;
556
+ }
557
+ }
558
+ }
559
+ if (errors.length > 0 || options?.onError === 'skip') {
560
+ // resolved is typed as Record<string, unknown>, so we can assign $errors directly
561
+ resolved['$errors'] = errors;
562
+ }
563
+ return resolved;
564
+ },
565
+ };
566
+ }
567
+ // =============================================================================
568
+ // Edge Entity Operations Factory
569
+ // =============================================================================
570
+ /**
571
+ * Create specialized operations for the Edge entity type
572
+ *
573
+ * Edge entities are auto-generated from schema relationships and have
574
+ * restricted write operations (no manual create/update/delete).
575
+ *
576
+ * @param schemaEdgeRecords - Edge records derived from the schema
577
+ * @param getProvider - Function to get the database provider
578
+ * @returns EntityOperations for Edge type
579
+ */
580
+ /**
581
+ * Create specialized operations for the Noun entity type
582
+ *
583
+ * Noun entities are auto-generated from schema entity types and have
584
+ * restricted write operations (no manual create/update/delete).
585
+ *
586
+ * @param nounRecords - Noun records derived from the schema entity types
587
+ * @returns EntityOperations for Noun type
588
+ */
589
+ export function createNounEntityOperations(nounRecords) {
590
+ const addNounMetadata = (n) => ({
591
+ ...n,
592
+ $id: n['$id'] || n['name'],
593
+ $type: 'Noun',
594
+ });
595
+ return {
596
+ async get(id) {
597
+ return nounRecords.find((n) => n['name'] === id || n['$id'] === id) ?? null;
598
+ },
599
+ async list(options) {
600
+ let results = [...nounRecords];
601
+ if (options?.where) {
602
+ results = applyWhereFilter(results, options.where);
603
+ }
604
+ results = applyPagination(results, options?.offset, options?.limit);
605
+ return results.map(addNounMetadata);
606
+ },
607
+ async find(where) {
608
+ return applyWhereFilter([...nounRecords], where).map(addNounMetadata);
609
+ },
610
+ async search(query) {
611
+ const queryLower = query.toLowerCase();
612
+ return nounRecords
613
+ .filter((n) => String(n['name']).toLowerCase().includes(queryLower) ||
614
+ String(n['singular']).toLowerCase().includes(queryLower) ||
615
+ String(n['plural']).toLowerCase().includes(queryLower) ||
616
+ String(n['description'] || '')
617
+ .toLowerCase()
618
+ .includes(queryLower))
619
+ .map(addNounMetadata);
620
+ },
621
+ async create() {
622
+ throw new Error('Cannot manually create Noun records - they are auto-generated from schema');
623
+ },
624
+ async update() {
625
+ throw new Error('Cannot manually update Noun records - they are auto-generated from schema');
626
+ },
627
+ async upsert() {
628
+ throw new Error('Cannot manually upsert Noun records - they are auto-generated from schema');
629
+ },
630
+ async delete() {
631
+ throw new Error('Cannot manually delete Noun records - they are auto-generated from schema');
632
+ },
633
+ async forEach(optionsOrCallback, maybeCallback) {
634
+ const options = typeof optionsOrCallback === 'function' ? undefined : optionsOrCallback;
635
+ const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : maybeCallback;
636
+ for (const item of await this.list(options)) {
637
+ await callback(item);
638
+ }
639
+ },
640
+ async semanticSearch() {
641
+ return [];
642
+ },
643
+ async hybridSearch() {
644
+ return [];
645
+ },
646
+ };
647
+ }
648
+ /**
649
+ * Create specialized operations for the Verb entity type
650
+ *
651
+ * Verb entities are the standard verb definitions (create, update, delete, etc.)
652
+ * and any custom verbs defined through the verbs API.
653
+ *
654
+ * @param verbRecords - Verb records with conjugation forms
655
+ * @returns EntityOperations for Verb type
656
+ */
657
+ export function createVerbEntityOperations(verbRecords) {
658
+ const addVerbMetadata = (v) => ({
659
+ ...v,
660
+ $id: v['$id'] || v['action'],
661
+ $type: 'Verb',
662
+ });
663
+ return {
664
+ async get(id) {
665
+ return verbRecords.find((v) => v['action'] === id || v['$id'] === id) ?? null;
666
+ },
667
+ async list(options) {
668
+ let results = [...verbRecords];
669
+ if (options?.where) {
670
+ results = applyWhereFilter(results, options.where);
671
+ }
672
+ results = applyPagination(results, options?.offset, options?.limit);
673
+ return results.map(addVerbMetadata);
674
+ },
675
+ async find(where) {
676
+ return applyWhereFilter([...verbRecords], where).map(addVerbMetadata);
677
+ },
678
+ async search(query) {
679
+ const queryLower = query.toLowerCase();
680
+ return verbRecords
681
+ .filter((v) => String(v['action']).toLowerCase().includes(queryLower) ||
682
+ String(v['actor'] || '')
683
+ .toLowerCase()
684
+ .includes(queryLower) ||
685
+ String(v['activity'] || '')
686
+ .toLowerCase()
687
+ .includes(queryLower) ||
688
+ String(v['description'] || '')
689
+ .toLowerCase()
690
+ .includes(queryLower))
691
+ .map(addVerbMetadata);
692
+ },
693
+ async create() {
694
+ throw new Error('Cannot manually create Verb records - use verbs.define() instead');
695
+ },
696
+ async update() {
697
+ throw new Error('Cannot manually update Verb records - use verbs.define() instead');
698
+ },
699
+ async upsert() {
700
+ throw new Error('Cannot manually upsert Verb records - use verbs.define() instead');
701
+ },
702
+ async delete() {
703
+ throw new Error('Cannot manually delete Verb records');
704
+ },
705
+ async forEach(optionsOrCallback, maybeCallback) {
706
+ const options = typeof optionsOrCallback === 'function' ? undefined : optionsOrCallback;
707
+ const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : maybeCallback;
708
+ for (const item of await this.list(options)) {
709
+ await callback(item);
710
+ }
711
+ },
712
+ async semanticSearch() {
713
+ return [];
714
+ },
715
+ async hybridSearch() {
716
+ return [];
717
+ },
718
+ };
719
+ }
720
+ export function createEdgeEntityOperations(schemaEdgeRecords, getProvider) {
721
+ const addEdgeMetadata = (e) => ({
722
+ ...e,
723
+ $id: e['$id'] || `${e['from']}:${e['name']}`,
724
+ $type: 'Edge',
725
+ });
726
+ /**
727
+ * Get runtime edges from the provider.
728
+ * Returns empty array for not-found errors (Edge table may not exist yet).
729
+ */
730
+ async function getRuntimeEdges(suppressErrors) {
731
+ try {
732
+ const provider = await getProvider();
733
+ return await provider.list('Edge');
734
+ }
735
+ catch (error) {
736
+ if (isNotFoundError(error) || suppressErrors) {
737
+ return [];
738
+ }
739
+ throw wrapDatabaseError(error, 'list', 'Edge');
740
+ }
741
+ }
742
+ /**
743
+ * Merge schema edges with runtime edges, preferring runtime versions.
744
+ */
745
+ async function getAllEdges(suppressErrors) {
746
+ const runtimeEdges = await getRuntimeEdges(suppressErrors);
747
+ const runtimeEdgeKeys = new Set(runtimeEdges.map((e) => `${e['from']}:${e['name']}`));
748
+ const filteredSchemaEdges = schemaEdgeRecords.filter((e) => {
749
+ const key = `${e['from']}:${e['name']}`;
750
+ const hasRuntimeVersion = runtimeEdgeKeys.has(key);
751
+ // Exclude schema edges that have runtime versions (unless it's a fuzzy match override)
752
+ return !hasRuntimeVersion || (hasRuntimeVersion && e['matchMode'] !== 'fuzzy');
753
+ });
754
+ return [...filteredSchemaEdges, ...runtimeEdges];
755
+ }
756
+ return {
757
+ async get(id) {
758
+ const runtimeEdges = await getRuntimeEdges();
759
+ const runtimeMatch = runtimeEdges.find((e) => e['$id'] === id || `${e['from']}:${e['name']}` === id);
760
+ if (runtimeMatch)
761
+ return { ...runtimeMatch, $type: 'Edge' };
762
+ return schemaEdgeRecords.find((e) => `${e['from']}:${e['name']}` === id) ?? null;
763
+ },
764
+ async list(options) {
765
+ try {
766
+ let results = await getAllEdges(options?.suppressErrors);
767
+ if (options?.where) {
768
+ results = applyWhereFilter(results, options.where);
769
+ }
770
+ return results.map(addEdgeMetadata);
771
+ }
772
+ catch (error) {
773
+ if (options?.onError) {
774
+ const fallback = options.onError(error instanceof Error ? error : new Error(String(error)));
775
+ return (fallback ?? []);
776
+ }
777
+ throw error;
778
+ }
779
+ },
780
+ async find(where) {
781
+ return applyWhereFilter(await getAllEdges(), where).map(addEdgeMetadata);
782
+ },
783
+ async search(query) {
784
+ const queryLower = query.toLowerCase();
785
+ return (await getAllEdges())
786
+ .filter((e) => String(e['from']).toLowerCase().includes(queryLower) ||
787
+ String(e['name']).toLowerCase().includes(queryLower) ||
788
+ String(e['to']).toLowerCase().includes(queryLower))
789
+ .map(addEdgeMetadata);
790
+ },
791
+ async create() {
792
+ throw new Error('Cannot manually create Edge records - they are auto-generated');
793
+ },
794
+ async update() {
795
+ throw new Error('Cannot manually update Edge records - they are auto-generated');
796
+ },
797
+ async upsert() {
798
+ throw new Error('Cannot manually upsert Edge records - they are auto-generated');
799
+ },
800
+ async delete() {
801
+ throw new Error('Cannot manually delete Edge records - they are auto-generated');
802
+ },
803
+ async forEach(optionsOrCallback, maybeCallback) {
804
+ const options = typeof optionsOrCallback === 'function' ? undefined : optionsOrCallback;
805
+ const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : maybeCallback;
806
+ for (const item of await this.list(options)) {
807
+ await callback(item);
808
+ }
809
+ },
810
+ async semanticSearch() {
811
+ return [];
812
+ },
813
+ async hybridSearch() {
814
+ return [];
815
+ },
816
+ };
817
+ }
818
+ //# sourceMappingURL=entity-operations.js.map