@jskit-ai/crud-server-generator 0.1.26 → 0.1.28

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.
@@ -0,0 +1,632 @@
1
+ import * as recast from "recast";
2
+ import { parse as parseBabel } from "@babel/parser";
3
+ import { normalizeText } from "@jskit-ai/database-runtime/shared";
4
+ import { normalizeCrudLookupNamespace } from "@jskit-ai/kernel/shared/support/crudLookup";
5
+
6
+ const { namedTypes: n, builders: b } = recast.types;
7
+
8
+ const BABEL_PARSE_PLUGINS = Object.freeze([
9
+ "jsx",
10
+ "importAssertions",
11
+ "importAttributes",
12
+ "dynamicImport",
13
+ "classProperties",
14
+ "classPrivateProperties",
15
+ "classPrivateMethods",
16
+ "optionalChaining",
17
+ "nullishCoalescingOperator",
18
+ "objectRestSpread",
19
+ "topLevelAwait"
20
+ ]);
21
+
22
+ const BABEL_REC_AST_PARSER = Object.freeze({
23
+ parse(source, options = {}) {
24
+ const sourcePlugins = Array.isArray(options?.plugins) ? options.plugins : [];
25
+ const mergedPlugins = [...new Set([...sourcePlugins, ...BABEL_PARSE_PLUGINS])];
26
+ return parseBabel(String(source || ""), {
27
+ ...options,
28
+ sourceType: "module",
29
+ plugins: mergedPlugins
30
+ });
31
+ }
32
+ });
33
+
34
+ const IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
35
+
36
+ function isIdentifierName(value = "") {
37
+ return IDENTIFIER_PATTERN.test(String(value || ""));
38
+ }
39
+
40
+ function parseModule(source = "", context = "crud-server-generator add-field") {
41
+ try {
42
+ return recast.parse(String(source || ""), { parser: BABEL_REC_AST_PARSER });
43
+ } catch (error) {
44
+ throw new Error(
45
+ `${context} could not parse resource file: ${String(error?.message || error || "unknown parse error")}`
46
+ );
47
+ }
48
+ }
49
+
50
+ function parseExpression(source = "", context = "crud-server-generator add-field") {
51
+ const expressionSource = `const __jskitFieldExpression = ${String(source || "")};`;
52
+ const ast = parseModule(expressionSource, context);
53
+ const statement = ast?.program?.body?.[0];
54
+ if (!statement || !n.VariableDeclaration.check(statement)) {
55
+ throw new Error(`${context} could not parse expression: ${String(source || "")}`);
56
+ }
57
+ const declaration = statement.declarations?.[0];
58
+ if (!declaration || !n.VariableDeclarator.check(declaration) || !declaration.init) {
59
+ throw new Error(`${context} could not resolve parsed expression: ${String(source || "")}`);
60
+ }
61
+ return declaration.init;
62
+ }
63
+
64
+ function parseStatement(source = "", context = "crud-server-generator add-field") {
65
+ const ast = parseModule(String(source || ""), context);
66
+ const statement = ast?.program?.body?.[0];
67
+ if (!statement) {
68
+ throw new Error(`${context} could not parse statement.`);
69
+ }
70
+ return statement;
71
+ }
72
+
73
+ function resolveNodeKeyName(keyNode, { computed = false } = {}) {
74
+ if (!keyNode) {
75
+ return "";
76
+ }
77
+ if (!computed && n.Identifier.check(keyNode)) {
78
+ return String(keyNode.name || "");
79
+ }
80
+ if (n.StringLiteral.check(keyNode) || n.Literal.check(keyNode)) {
81
+ return String(keyNode.value || "");
82
+ }
83
+ return "";
84
+ }
85
+
86
+ function createObjectPropertyKeyNode(key = "") {
87
+ const normalizedKey = String(key || "");
88
+ if (isIdentifierName(normalizedKey)) {
89
+ return b.identifier(normalizedKey);
90
+ }
91
+ return b.stringLiteral(normalizedKey);
92
+ }
93
+
94
+ function findVariableDeclarator(programNode, variableName = "") {
95
+ const targetName = normalizeText(variableName);
96
+ if (!targetName || !programNode || !Array.isArray(programNode.body)) {
97
+ return null;
98
+ }
99
+
100
+ for (const statement of programNode.body) {
101
+ if (!n.VariableDeclaration.check(statement)) {
102
+ continue;
103
+ }
104
+ for (const declaration of statement.declarations || []) {
105
+ if (!n.VariableDeclarator.check(declaration) || !n.Identifier.check(declaration.id)) {
106
+ continue;
107
+ }
108
+ if (declaration.id.name === targetName) {
109
+ return declaration;
110
+ }
111
+ }
112
+ }
113
+
114
+ return null;
115
+ }
116
+
117
+ function requireVariableDeclarator(programNode, variableName = "", context = "crud-server-generator add-field") {
118
+ const declaration = findVariableDeclarator(programNode, variableName);
119
+ if (declaration) {
120
+ return declaration;
121
+ }
122
+ throw new Error(`${context} could not find const ${variableName}.`);
123
+ }
124
+
125
+ function requireSchemaPropertiesObject(programNode, variableName = "", context = "crud-server-generator add-field") {
126
+ const declaration = requireVariableDeclarator(programNode, variableName, context);
127
+ const initExpression = declaration.init;
128
+ if (!n.CallExpression.check(initExpression)) {
129
+ throw new Error(`${context} expected ${variableName} to be initialized with Type.Object(...).`);
130
+ }
131
+
132
+ const callee = initExpression.callee;
133
+ const isTypeObjectCall =
134
+ n.MemberExpression.check(callee) &&
135
+ !callee.computed &&
136
+ n.Identifier.check(callee.object) &&
137
+ callee.object.name === "Type" &&
138
+ n.Identifier.check(callee.property) &&
139
+ callee.property.name === "Object";
140
+ if (!isTypeObjectCall) {
141
+ throw new Error(`${context} expected ${variableName} to call Type.Object(...).`);
142
+ }
143
+
144
+ const firstArgument = initExpression.arguments?.[0];
145
+ if (!n.ObjectExpression.check(firstArgument)) {
146
+ throw new Error(`${context} expected ${variableName} Type.Object first argument to be an object literal.`);
147
+ }
148
+
149
+ return firstArgument;
150
+ }
151
+
152
+ function requireObjectFreezePayloadObject(programNode, variableName = "", context = "crud-server-generator add-field") {
153
+ const declaration = requireVariableDeclarator(programNode, variableName, context);
154
+ const initExpression = declaration.init;
155
+ if (!n.CallExpression.check(initExpression)) {
156
+ throw new Error(`${context} expected ${variableName} to be initialized with Object.freeze(...).`);
157
+ }
158
+
159
+ const callee = initExpression.callee;
160
+ const isObjectFreezeCall =
161
+ n.MemberExpression.check(callee) &&
162
+ !callee.computed &&
163
+ n.Identifier.check(callee.object) &&
164
+ callee.object.name === "Object" &&
165
+ n.Identifier.check(callee.property) &&
166
+ callee.property.name === "freeze";
167
+ if (!isObjectFreezeCall) {
168
+ throw new Error(`${context} expected ${variableName} to call Object.freeze(...).`);
169
+ }
170
+
171
+ const payload = initExpression.arguments?.[0];
172
+ if (!n.ObjectExpression.check(payload)) {
173
+ throw new Error(`${context} expected ${variableName} Object.freeze payload to be an object literal.`);
174
+ }
175
+
176
+ return payload;
177
+ }
178
+
179
+ function findObjectPropertyByName(objectNode, propertyName = "") {
180
+ const targetName = normalizeText(propertyName);
181
+ if (!targetName || !n.ObjectExpression.check(objectNode)) {
182
+ return null;
183
+ }
184
+
185
+ for (const property of objectNode.properties || []) {
186
+ if (!n.ObjectProperty.check(property) && !n.Property.check(property) && !n.ObjectMethod.check(property)) {
187
+ continue;
188
+ }
189
+ const name = resolveNodeKeyName(property.key, {
190
+ computed: Boolean(property.computed)
191
+ });
192
+ if (name === targetName) {
193
+ return property;
194
+ }
195
+ }
196
+
197
+ return null;
198
+ }
199
+
200
+ function requireNormalizeFunctionBody(programNode, variableName = "", context = "crud-server-generator add-field") {
201
+ const validatorObject = requireObjectFreezePayloadObject(programNode, variableName, context);
202
+ const normalizeProperty = findObjectPropertyByName(validatorObject, "normalize");
203
+ if (!normalizeProperty) {
204
+ throw new Error(`${context} expected ${variableName}.normalize(...) to exist.`);
205
+ }
206
+
207
+ if (n.ObjectMethod.check(normalizeProperty)) {
208
+ return normalizeProperty.body;
209
+ }
210
+
211
+ const propertyValue = normalizeProperty.value;
212
+ if (n.FunctionExpression.check(propertyValue) || n.ArrowFunctionExpression.check(propertyValue)) {
213
+ if (n.BlockStatement.check(propertyValue.body)) {
214
+ return propertyValue.body;
215
+ }
216
+ }
217
+
218
+ throw new Error(`${context} expected ${variableName}.normalize to be a function with a block body.`);
219
+ }
220
+
221
+ function requireNormalizedObjectLiteral(functionBody, context = "crud-server-generator add-field") {
222
+ for (const statement of functionBody.body || []) {
223
+ if (!n.VariableDeclaration.check(statement)) {
224
+ continue;
225
+ }
226
+ for (const declaration of statement.declarations || []) {
227
+ if (!n.VariableDeclarator.check(declaration) || !n.Identifier.check(declaration.id)) {
228
+ continue;
229
+ }
230
+ if (declaration.id.name !== "normalized") {
231
+ continue;
232
+ }
233
+ if (!n.ObjectExpression.check(declaration.init)) {
234
+ throw new Error(`${context} expected normalized to be initialized as an object literal.`);
235
+ }
236
+ return declaration.init;
237
+ }
238
+ }
239
+ throw new Error(`${context} could not find "const normalized = { ... }".`);
240
+ }
241
+
242
+ function hasObjectProperty(objectNode, propertyName = "") {
243
+ return Boolean(findObjectPropertyByName(objectNode, propertyName));
244
+ }
245
+
246
+ function insertObjectProperty(
247
+ objectNode,
248
+ propertyName = "",
249
+ valueExpressionSource = "",
250
+ {
251
+ context = "crud-server-generator add-field",
252
+ insertBeforeComputed = false
253
+ } = {}
254
+ ) {
255
+ if (!n.ObjectExpression.check(objectNode)) {
256
+ throw new Error(`${context} expected object expression while inserting property "${propertyName}".`);
257
+ }
258
+
259
+ const key = normalizeText(propertyName);
260
+ if (!key) {
261
+ throw new Error(`${context} insertObjectProperty requires propertyName.`);
262
+ }
263
+
264
+ if (hasObjectProperty(objectNode, key)) {
265
+ return false;
266
+ }
267
+
268
+ const valueNode = parseExpression(valueExpressionSource, context);
269
+ const propertyNode = b.objectProperty(createObjectPropertyKeyNode(key), valueNode);
270
+ const firstComputedIndex = insertBeforeComputed
271
+ ? objectNode.properties.findIndex((property) => Boolean(property?.computed))
272
+ : -1;
273
+ const insertionIndex =
274
+ insertBeforeComputed && firstComputedIndex >= 0
275
+ ? firstComputedIndex
276
+ : objectNode.properties.length;
277
+
278
+ if (insertionIndex >= 0 && insertionIndex < objectNode.properties.length) {
279
+ objectNode.properties.splice(insertionIndex, 0, propertyNode);
280
+ } else {
281
+ objectNode.properties.push(propertyNode);
282
+ }
283
+
284
+ return true;
285
+ }
286
+
287
+ function hasNormalizeIfInSourceCall(functionBody, fieldKey = "") {
288
+ const targetFieldKey = normalizeText(fieldKey);
289
+ if (!targetFieldKey) {
290
+ return false;
291
+ }
292
+
293
+ for (const statement of functionBody.body || []) {
294
+ if (!n.ExpressionStatement.check(statement) || !n.CallExpression.check(statement.expression)) {
295
+ continue;
296
+ }
297
+ const callExpression = statement.expression;
298
+ if (!n.Identifier.check(callExpression.callee) || callExpression.callee.name !== "normalizeIfInSource") {
299
+ continue;
300
+ }
301
+ const keyArgument = callExpression.arguments?.[2];
302
+ if (n.StringLiteral.check(keyArgument) && normalizeText(keyArgument.value) === targetFieldKey) {
303
+ return true;
304
+ }
305
+ if (n.Literal.check(keyArgument) && typeof keyArgument.value === "string" && normalizeText(keyArgument.value) === targetFieldKey) {
306
+ return true;
307
+ }
308
+ }
309
+
310
+ return false;
311
+ }
312
+
313
+ function resolveReturnNormalizedIndex(functionBody) {
314
+ const statements = Array.isArray(functionBody?.body) ? functionBody.body : [];
315
+ for (const [index, statement] of statements.entries()) {
316
+ if (!n.ReturnStatement.check(statement)) {
317
+ continue;
318
+ }
319
+ if (n.Identifier.check(statement.argument) && statement.argument.name === "normalized") {
320
+ return index;
321
+ }
322
+ }
323
+ return statements.length;
324
+ }
325
+
326
+ function hasResourceFieldMetaEntry(programNode, fieldKey = "") {
327
+ const targetKey = normalizeText(fieldKey);
328
+ if (!targetKey || !programNode || !Array.isArray(programNode.body)) {
329
+ return false;
330
+ }
331
+
332
+ for (const statement of programNode.body) {
333
+ if (!n.ExpressionStatement.check(statement) || !n.CallExpression.check(statement.expression)) {
334
+ continue;
335
+ }
336
+ const callExpression = statement.expression;
337
+ if (!n.MemberExpression.check(callExpression.callee) || callExpression.callee.computed) {
338
+ continue;
339
+ }
340
+ if (!n.Identifier.check(callExpression.callee.object) || callExpression.callee.object.name !== "RESOURCE_FIELD_META") {
341
+ continue;
342
+ }
343
+ if (!n.Identifier.check(callExpression.callee.property) || callExpression.callee.property.name !== "push") {
344
+ continue;
345
+ }
346
+
347
+ const firstArgument = callExpression.arguments?.[0];
348
+ if (!n.ObjectExpression.check(firstArgument)) {
349
+ continue;
350
+ }
351
+ const keyProperty = findObjectPropertyByName(firstArgument, "key");
352
+ if (!keyProperty) {
353
+ continue;
354
+ }
355
+ const keyValue = keyProperty.value;
356
+ if (n.StringLiteral.check(keyValue) && normalizeText(keyValue.value) === targetKey) {
357
+ return true;
358
+ }
359
+ if (n.Literal.check(keyValue) && typeof keyValue.value === "string" && normalizeText(keyValue.value) === targetKey) {
360
+ return true;
361
+ }
362
+ }
363
+
364
+ return false;
365
+ }
366
+
367
+ function sortImportSpecifiers(importDeclaration) {
368
+ const sourceSpecifiers = Array.isArray(importDeclaration?.specifiers) ? importDeclaration.specifiers : [];
369
+ const named = sourceSpecifiers
370
+ .filter((specifier) => n.ImportSpecifier.check(specifier))
371
+ .sort((left, right) => {
372
+ const leftName = String(left?.imported?.name || left?.imported?.value || "");
373
+ const rightName = String(right?.imported?.name || right?.imported?.value || "");
374
+ return leftName.localeCompare(rightName);
375
+ });
376
+ const nonNamed = sourceSpecifiers.filter((specifier) => !n.ImportSpecifier.check(specifier));
377
+ importDeclaration.specifiers = [...nonNamed, ...named];
378
+ }
379
+
380
+ function ensureNamedImport(programNode, modulePath = "", importName = "") {
381
+ const normalizedModulePath = normalizeText(modulePath);
382
+ const normalizedImportName = normalizeText(importName);
383
+ if (!normalizedModulePath || !normalizedImportName) {
384
+ return false;
385
+ }
386
+
387
+ const importDeclarations = (programNode.body || []).filter((statement) => n.ImportDeclaration.check(statement));
388
+ let declaration = importDeclarations.find((statement) => {
389
+ const source = statement.source;
390
+ if (n.StringLiteral.check(source)) {
391
+ return source.value === normalizedModulePath;
392
+ }
393
+ if (n.Literal.check(source)) {
394
+ return source.value === normalizedModulePath;
395
+ }
396
+ return false;
397
+ });
398
+
399
+ if (!declaration) {
400
+ declaration = b.importDeclaration(
401
+ [b.importSpecifier(b.identifier(normalizedImportName), b.identifier(normalizedImportName))],
402
+ b.stringLiteral(normalizedModulePath)
403
+ );
404
+ const insertionIndex = (() => {
405
+ const body = Array.isArray(programNode.body) ? programNode.body : [];
406
+ let index = 0;
407
+ while (index < body.length && n.ImportDeclaration.check(body[index])) {
408
+ index += 1;
409
+ }
410
+ return index;
411
+ })();
412
+ programNode.body.splice(insertionIndex, 0, declaration);
413
+ return true;
414
+ }
415
+
416
+ const hasSpecifier = (declaration.specifiers || []).some((specifier) => {
417
+ if (!n.ImportSpecifier.check(specifier)) {
418
+ return false;
419
+ }
420
+ const importedName = String(specifier.imported?.name || specifier.imported?.value || "");
421
+ const localName = String(specifier.local?.name || "");
422
+ return importedName === normalizedImportName || localName === normalizedImportName;
423
+ });
424
+ if (hasSpecifier) {
425
+ return false;
426
+ }
427
+
428
+ declaration.specifiers = [
429
+ ...(declaration.specifiers || []),
430
+ b.importSpecifier(b.identifier(normalizedImportName), b.identifier(normalizedImportName))
431
+ ];
432
+ sortImportSpecifiers(declaration);
433
+ return true;
434
+ }
435
+
436
+ function resolveObjectPropertyStringValue(objectNode, propertyName = "") {
437
+ const propertyNode = findObjectPropertyByName(objectNode, propertyName);
438
+ if (!propertyNode) {
439
+ return "";
440
+ }
441
+ const valueNode = propertyNode.value;
442
+ if (n.StringLiteral.check(valueNode)) {
443
+ return String(valueNode.value || "");
444
+ }
445
+ if (n.Literal.check(valueNode) && typeof valueNode.value === "string") {
446
+ return String(valueNode.value || "");
447
+ }
448
+ return "";
449
+ }
450
+
451
+ function resolveCrudResourceDefaults(source = "", context = "crud-server-generator add-field") {
452
+ const ast = parseModule(source, context);
453
+ const statements = Array.isArray(ast?.program?.body) ? ast.program.body : [];
454
+
455
+ for (const statement of statements) {
456
+ if (!n.VariableDeclaration.check(statement)) {
457
+ continue;
458
+ }
459
+ for (const declaration of statement.declarations || []) {
460
+ if (!n.VariableDeclarator.check(declaration) || !n.ObjectExpression.check(declaration.init)) {
461
+ continue;
462
+ }
463
+ const tableName = resolveObjectPropertyStringValue(declaration.init, "tableName");
464
+ if (!tableName) {
465
+ continue;
466
+ }
467
+ const idColumn = resolveObjectPropertyStringValue(declaration.init, "idColumn");
468
+ return Object.freeze({
469
+ tableName,
470
+ idColumn: idColumn || "id"
471
+ });
472
+ }
473
+ }
474
+
475
+ throw new Error(`${context} could not resolve resource tableName/idColumn from resource object literal.`);
476
+ }
477
+
478
+ function renderResourceFieldMetaPushStatement(entry = {}) {
479
+ const key = normalizeText(entry?.key);
480
+ if (!key) {
481
+ throw new Error("crud-server-generator add-field fieldMeta entry requires key.");
482
+ }
483
+
484
+ const lines = ["RESOURCE_FIELD_META.push({"];
485
+ lines.push(` key: ${JSON.stringify(key)},`);
486
+
487
+ const dbColumn = normalizeText(entry?.dbColumn);
488
+ if (dbColumn) {
489
+ lines.push(` dbColumn: ${JSON.stringify(dbColumn)},`);
490
+ }
491
+
492
+ const relation = entry?.relation && typeof entry.relation === "object" ? entry.relation : null;
493
+ if (relation) {
494
+ const relationNamespace =
495
+ normalizeCrudLookupNamespace(relation.namespace) ||
496
+ normalizeCrudLookupNamespace(relation.apiPath);
497
+ if (!relationNamespace) {
498
+ throw new Error("crud-server-generator add-field fieldMeta relation requires namespace.");
499
+ }
500
+ lines.push(" relation: {");
501
+ lines.push(` kind: ${JSON.stringify(normalizeText(relation.kind) || "lookup")},`);
502
+ lines.push(` namespace: ${JSON.stringify(relationNamespace)},`);
503
+ lines.push(` valueKey: ${JSON.stringify(normalizeText(relation.valueKey) || "id")}`);
504
+ lines.push(" },");
505
+ }
506
+
507
+ const formControl = normalizeText(entry?.ui?.formControl);
508
+ if (formControl) {
509
+ lines.push(" ui: {");
510
+ lines.push(` formControl: ${JSON.stringify(formControl)} // or "select"`);
511
+ lines.push(" }");
512
+ } else {
513
+ const lastIndex = lines.length - 1;
514
+ lines[lastIndex] = lines[lastIndex].replace(/,$/, "");
515
+ }
516
+
517
+ lines.push("});");
518
+ return lines.join("\n");
519
+ }
520
+
521
+ function applyCrudResourceFieldPatch(
522
+ source = "",
523
+ {
524
+ fieldKey = "",
525
+ outputSchemaExpression = "",
526
+ createSchemaExpression = "",
527
+ outputNormalizationExpression = "",
528
+ inputNormalizationExpression = "",
529
+ fieldMetaEntry = null,
530
+ normalizeImportNames = [],
531
+ databaseRuntimeImportNames = [],
532
+ databaseRuntimeRepositoryOptionsImportNames = [],
533
+ context = "crud-server-generator add-field"
534
+ } = {}
535
+ ) {
536
+ const normalizedFieldKey = normalizeText(fieldKey);
537
+ if (!normalizedFieldKey) {
538
+ throw new Error(`${context} apply patch requires fieldKey.`);
539
+ }
540
+ if (!normalizeText(outputSchemaExpression)) {
541
+ throw new Error(`${context} apply patch requires outputSchemaExpression.`);
542
+ }
543
+ if (!normalizeText(createSchemaExpression)) {
544
+ throw new Error(`${context} apply patch requires createSchemaExpression.`);
545
+ }
546
+ if (!normalizeText(outputNormalizationExpression)) {
547
+ throw new Error(`${context} apply patch requires outputNormalizationExpression.`);
548
+ }
549
+ if (!normalizeText(inputNormalizationExpression)) {
550
+ throw new Error(`${context} apply patch requires inputNormalizationExpression.`);
551
+ }
552
+
553
+ const ast = parseModule(source, context);
554
+ const programNode = ast.program;
555
+ let changed = false;
556
+
557
+ const recordOutputSchemaObject = requireSchemaPropertiesObject(programNode, "recordOutputSchema", context);
558
+ changed =
559
+ insertObjectProperty(recordOutputSchemaObject, normalizedFieldKey, outputSchemaExpression, {
560
+ context,
561
+ insertBeforeComputed: true
562
+ }) || changed;
563
+
564
+ const createBodySchemaObject = requireSchemaPropertiesObject(programNode, "createBodySchema", context);
565
+ changed =
566
+ insertObjectProperty(createBodySchemaObject, normalizedFieldKey, createSchemaExpression, {
567
+ context
568
+ }) || changed;
569
+
570
+ const recordNormalizeFunctionBody = requireNormalizeFunctionBody(programNode, "recordOutputValidator", context);
571
+ const recordNormalizedObject = requireNormalizedObjectLiteral(recordNormalizeFunctionBody, context);
572
+ changed =
573
+ insertObjectProperty(recordNormalizedObject, normalizedFieldKey, outputNormalizationExpression, {
574
+ context
575
+ }) || changed;
576
+
577
+ const createNormalizeFunctionBody = requireNormalizeFunctionBody(programNode, "createBodyValidator", context);
578
+ if (!hasNormalizeIfInSourceCall(createNormalizeFunctionBody, normalizedFieldKey)) {
579
+ const insertionStatement = parseStatement(
580
+ `normalizeIfInSource(source, normalized, ${JSON.stringify(normalizedFieldKey)}, ${inputNormalizationExpression});`,
581
+ context
582
+ );
583
+ const insertionIndex = resolveReturnNormalizedIndex(createNormalizeFunctionBody);
584
+ createNormalizeFunctionBody.body.splice(insertionIndex, 0, insertionStatement);
585
+ changed = true;
586
+ }
587
+
588
+ const validFieldMetaEntry =
589
+ fieldMetaEntry &&
590
+ typeof fieldMetaEntry === "object" &&
591
+ normalizeText(fieldMetaEntry.key) === normalizedFieldKey
592
+ ? fieldMetaEntry
593
+ : null;
594
+ if (validFieldMetaEntry && !hasResourceFieldMetaEntry(programNode, normalizedFieldKey)) {
595
+ const fieldMetaStatement = parseStatement(renderResourceFieldMetaPushStatement(validFieldMetaEntry), context);
596
+ programNode.body.push(fieldMetaStatement);
597
+ changed = true;
598
+ }
599
+
600
+ const normalizeImports = Array.isArray(normalizeImportNames) ? normalizeImportNames : [];
601
+ for (const importName of normalizeImports) {
602
+ changed =
603
+ ensureNamedImport(programNode, "@jskit-ai/kernel/shared/support/normalize", importName) || changed;
604
+ }
605
+
606
+ const databaseRuntimeImports = Array.isArray(databaseRuntimeImportNames) ? databaseRuntimeImportNames : [];
607
+ for (const importName of databaseRuntimeImports) {
608
+ changed = ensureNamedImport(programNode, "@jskit-ai/database-runtime/shared", importName) || changed;
609
+ }
610
+
611
+ const databaseRuntimeRepositoryOptionsImports = Array.isArray(databaseRuntimeRepositoryOptionsImportNames)
612
+ ? databaseRuntimeRepositoryOptionsImportNames
613
+ : [];
614
+ for (const importName of databaseRuntimeRepositoryOptionsImports) {
615
+ changed =
616
+ ensureNamedImport(
617
+ programNode,
618
+ "@jskit-ai/database-runtime/shared/repositoryOptions",
619
+ importName
620
+ ) || changed;
621
+ }
622
+
623
+ return {
624
+ changed,
625
+ content: changed ? recast.print(ast, { reuseWhitespace: true }).code : String(source || "")
626
+ };
627
+ }
628
+
629
+ export {
630
+ resolveCrudResourceDefaults,
631
+ applyCrudResourceFieldPatch
632
+ };