@statezero/core 0.1.75 → 0.1.77

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 (109) hide show
  1. package/dist/actions/backend1/django_app/calculate-hash.js +77 -0
  2. package/dist/actions/backend1/django_app/get-current-username.js +62 -0
  3. package/dist/actions/backend1/django_app/get-server-status.js +65 -0
  4. package/dist/actions/backend1/django_app/get-user-info.js +67 -0
  5. package/dist/actions/backend1/django_app/process-data.js +75 -0
  6. package/dist/actions/backend1/django_app/send-notification.js +78 -0
  7. package/dist/actions/backend1/index.d.ts +1 -0
  8. package/dist/actions/backend1/index.js +1 -0
  9. package/dist/actions/default/django_app/calculate-hash.d.ts +57 -0
  10. package/dist/actions/{django_app → default/django_app}/calculate-hash.js +1 -1
  11. package/dist/actions/default/django_app/get-current-username.d.ts +29 -0
  12. package/dist/actions/{django_app → default/django_app}/get-current-username.js +1 -1
  13. package/dist/actions/default/django_app/get-server-status.d.ts +38 -0
  14. package/dist/actions/{django_app → default/django_app}/get-server-status.js +1 -1
  15. package/dist/actions/default/django_app/get-user-info.d.ts +44 -0
  16. package/dist/actions/{django_app → default/django_app}/get-user-info.js +1 -1
  17. package/dist/actions/default/django_app/index.d.ts +6 -0
  18. package/dist/actions/default/django_app/index.js +6 -0
  19. package/dist/actions/default/django_app/process-data.d.ts +51 -0
  20. package/dist/actions/{django_app → default/django_app}/process-data.js +1 -1
  21. package/dist/actions/default/django_app/send-notification.d.ts +55 -0
  22. package/dist/actions/{django_app → default/django_app}/send-notification.js +1 -1
  23. package/dist/actions/default/index.d.ts +1 -0
  24. package/dist/actions/default/index.js +1 -0
  25. package/dist/actions/index.d.ts +1 -1
  26. package/dist/actions/index.js +5 -1
  27. package/dist/cli/commands/syncActions.js +66 -22
  28. package/dist/cli/commands/syncModels.js +44 -7
  29. package/dist/filtering/localFiltering.js +56 -9
  30. package/dist/models/{django_app → backend1/django_app}/comprehensivemodel.d.ts +3 -3
  31. package/dist/models/backend1/django_app/comprehensivemodel.js +63 -0
  32. package/dist/models/{django_app → backend1/django_app}/custompkmodel.d.ts +3 -3
  33. package/dist/models/backend1/django_app/custompkmodel.js +61 -0
  34. package/dist/models/{django_app → backend1/django_app}/deepmodellevel1.d.ts +3 -3
  35. package/dist/models/backend1/django_app/deepmodellevel1.js +64 -0
  36. package/dist/models/{django_app → backend1/django_app}/deepmodellevel2.d.ts +3 -3
  37. package/dist/models/backend1/django_app/deepmodellevel2.js +63 -0
  38. package/dist/models/{django_app → backend1/django_app}/deepmodellevel3.d.ts +3 -3
  39. package/dist/models/backend1/django_app/deepmodellevel3.js +61 -0
  40. package/dist/models/{django_app → backend1/django_app}/dummymodel.d.ts +3 -3
  41. package/dist/models/backend1/django_app/dummymodel.js +63 -0
  42. package/dist/models/{django_app → backend1/django_app}/dummyrelatedmodel.d.ts +3 -3
  43. package/dist/models/backend1/django_app/dummyrelatedmodel.js +61 -0
  44. package/dist/models/{django_app → backend1/django_app}/filetest.d.ts +3 -3
  45. package/dist/models/backend1/django_app/filetest.js +61 -0
  46. package/dist/models/{django_app → backend1/django_app}/modelwithcustompkrelation.d.ts +3 -3
  47. package/dist/models/backend1/django_app/modelwithcustompkrelation.js +63 -0
  48. package/dist/models/{django_app → backend1/django_app}/namefiltercustompkmodel.d.ts +3 -3
  49. package/dist/models/backend1/django_app/namefiltercustompkmodel.js +61 -0
  50. package/dist/models/{django_app → backend1/django_app}/order.d.ts +3 -3
  51. package/dist/models/backend1/django_app/order.js +61 -0
  52. package/dist/models/{django_app → backend1/django_app}/orderitem.d.ts +3 -3
  53. package/dist/models/backend1/django_app/orderitem.js +64 -0
  54. package/dist/models/{django_app → backend1/django_app}/product.d.ts +3 -3
  55. package/dist/models/backend1/django_app/product.js +63 -0
  56. package/dist/models/{django_app → backend1/django_app}/productcategory.d.ts +3 -3
  57. package/dist/models/backend1/django_app/productcategory.js +61 -0
  58. package/dist/models/backend1/fileobject.d.ts +4 -0
  59. package/dist/models/backend1/fileobject.js +9 -0
  60. package/dist/models/backend1/index.d.ts +2 -0
  61. package/dist/models/backend1/index.js +2 -0
  62. package/dist/models/default/django_app/comprehensivemodel.d.ts +47 -0
  63. package/dist/models/{django_app → default/django_app}/comprehensivemodel.js +2 -2
  64. package/dist/models/default/django_app/custompkmodel.d.ts +44 -0
  65. package/dist/models/{django_app → default/django_app}/custompkmodel.js +2 -2
  66. package/dist/models/default/django_app/deepmodellevel1.d.ts +47 -0
  67. package/dist/models/{django_app → default/django_app}/deepmodellevel1.js +2 -2
  68. package/dist/models/default/django_app/deepmodellevel2.d.ts +47 -0
  69. package/dist/models/{django_app → default/django_app}/deepmodellevel2.js +2 -2
  70. package/dist/models/default/django_app/deepmodellevel3.d.ts +44 -0
  71. package/dist/models/{django_app → default/django_app}/deepmodellevel3.js +2 -2
  72. package/dist/models/default/django_app/dummymodel.d.ts +47 -0
  73. package/dist/models/{django_app → default/django_app}/dummymodel.js +2 -2
  74. package/dist/models/default/django_app/dummyrelatedmodel.d.ts +44 -0
  75. package/dist/models/{django_app → default/django_app}/dummyrelatedmodel.js +2 -2
  76. package/dist/models/default/django_app/filetest.d.ts +44 -0
  77. package/dist/models/{django_app → default/django_app}/filetest.js +2 -2
  78. package/dist/models/default/django_app/index.d.ts +14 -0
  79. package/dist/models/default/django_app/index.js +14 -0
  80. package/dist/models/default/django_app/modelwithcustompkrelation.d.ts +47 -0
  81. package/dist/models/{django_app → default/django_app}/modelwithcustompkrelation.js +2 -2
  82. package/dist/models/default/django_app/namefiltercustompkmodel.d.ts +44 -0
  83. package/dist/models/{django_app → default/django_app}/namefiltercustompkmodel.js +2 -2
  84. package/dist/models/default/django_app/order.d.ts +44 -0
  85. package/dist/models/{django_app → default/django_app}/order.js +2 -2
  86. package/dist/models/default/django_app/orderitem.d.ts +47 -0
  87. package/dist/models/{django_app → default/django_app}/orderitem.js +2 -2
  88. package/dist/models/default/django_app/product.d.ts +47 -0
  89. package/dist/models/{django_app → default/django_app}/product.js +2 -2
  90. package/dist/models/default/django_app/productcategory.d.ts +44 -0
  91. package/dist/models/{django_app → default/django_app}/productcategory.js +2 -2
  92. package/dist/models/default/fileobject.d.ts +4 -0
  93. package/dist/models/default/index.d.ts +2 -0
  94. package/dist/models/default/index.js +2 -0
  95. package/dist/models/index.d.ts +1 -2
  96. package/dist/models/index.js +5 -2
  97. package/package.json +1 -1
  98. package/dist/models/fileobject.d.ts +0 -4
  99. /package/dist/actions/{django_app → backend1/django_app}/calculate-hash.d.ts +0 -0
  100. /package/dist/actions/{django_app → backend1/django_app}/get-current-username.d.ts +0 -0
  101. /package/dist/actions/{django_app → backend1/django_app}/get-server-status.d.ts +0 -0
  102. /package/dist/actions/{django_app → backend1/django_app}/get-user-info.d.ts +0 -0
  103. /package/dist/actions/{django_app → backend1/django_app}/index.d.ts +0 -0
  104. /package/dist/actions/{django_app → backend1/django_app}/index.js +0 -0
  105. /package/dist/actions/{django_app → backend1/django_app}/process-data.d.ts +0 -0
  106. /package/dist/actions/{django_app → backend1/django_app}/send-notification.d.ts +0 -0
  107. /package/dist/models/{django_app → backend1/django_app}/index.d.ts +0 -0
  108. /package/dist/models/{django_app → backend1/django_app}/index.js +0 -0
  109. /package/dist/models/{fileobject.js → default/fileobject.js} +0 -0
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Process a list of numbers with various mathematical operations
3
+ *
4
+ * @param any[] data - List of numbers to process
5
+ * @param 'sum' | 'avg' | 'max' | 'min' operation - Mathematical operation to perform
6
+ * @param {Object} [axiosOverrides] - Allows overriding Axios request parameters.
7
+ * @returns {Promise<Object>} A promise that resolves with the action's result.
8
+ */
9
+ export function processData(data: any, operation: any, axiosOverrides?: Object): Promise<Object>;
10
+ export namespace processData {
11
+ let actionName: string;
12
+ let title: string;
13
+ let app: string;
14
+ let permissions: string[];
15
+ let configKey: string;
16
+ }
17
+ /**
18
+ * Zod schema for the input of processData.
19
+ * NOTE: This is an object schema for validating the data payload.
20
+ */
21
+ export const processDataInputSchema: z.ZodObject<{
22
+ data: z.ZodArray<z.ZodAny, "many">;
23
+ operation: z.ZodEnum<["sum", "avg", "max", "min"]>;
24
+ }, "strip", z.ZodTypeAny, {
25
+ data: any[];
26
+ operation: "min" | "max" | "avg" | "sum";
27
+ }, {
28
+ data: any[];
29
+ operation: "min" | "max" | "avg" | "sum";
30
+ }>;
31
+ /**
32
+ * Zod schema for the response of processData.
33
+ */
34
+ export const processDataResponseSchema: z.ZodObject<{
35
+ operation: z.ZodString;
36
+ result: z.ZodNumber;
37
+ processed_count: z.ZodNumber;
38
+ execution_time_ms: z.ZodNumber;
39
+ }, "strip", z.ZodTypeAny, {
40
+ operation: string;
41
+ result: number;
42
+ processed_count: number;
43
+ execution_time_ms: number;
44
+ }, {
45
+ operation: string;
46
+ result: number;
47
+ processed_count: number;
48
+ execution_time_ms: number;
49
+ }>;
50
+ export default processData;
51
+ import { z } from 'zod';
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import axios from 'axios';
7
7
  import { z } from 'zod';
8
- import { configInstance, parseStateZeroError } from '../../../src';
8
+ import { configInstance, parseStateZeroError } from '../../../../src';
9
9
  /**
10
10
  * Zod schema for the input of processData.
11
11
  * NOTE: This is an object schema for validating the data payload.
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Send notifications to multiple recipients
3
+ *
4
+ * @param string message - Notification message to send
5
+ * @param any[] recipients - List of email addresses to notify
6
+ * @param 'low' | 'high' priority - Notification priority level
7
+ * @param {Object} [axiosOverrides] - Allows overriding Axios request parameters.
8
+ * @returns {Promise<Object>} A promise that resolves with the action's result.
9
+ */
10
+ export function sendNotification(message: any, recipients: any, priority?: string, axiosOverrides?: Object): Promise<Object>;
11
+ export namespace sendNotification {
12
+ let actionName: string;
13
+ let title: string;
14
+ let app: string;
15
+ let permissions: string[];
16
+ let configKey: string;
17
+ }
18
+ /**
19
+ * Zod schema for the input of sendNotification.
20
+ * NOTE: This is an object schema for validating the data payload.
21
+ */
22
+ export const sendNotificationInputSchema: z.ZodObject<{
23
+ message: z.ZodString;
24
+ recipients: z.ZodArray<z.ZodAny, "many">;
25
+ priority: z.ZodDefault<z.ZodOptional<z.ZodEnum<["low", "high"]>>>;
26
+ }, "strip", z.ZodTypeAny, {
27
+ message: string;
28
+ priority: "low" | "high";
29
+ recipients: any[];
30
+ }, {
31
+ message: string;
32
+ recipients: any[];
33
+ priority?: "low" | "high" | undefined;
34
+ }>;
35
+ /**
36
+ * Zod schema for the response of sendNotification.
37
+ */
38
+ export const sendNotificationResponseSchema: z.ZodObject<{
39
+ success: z.ZodBoolean;
40
+ message_id: z.ZodString;
41
+ sent_to: z.ZodNumber;
42
+ queued_at: z.ZodString;
43
+ }, "strip", z.ZodTypeAny, {
44
+ success: boolean;
45
+ message_id: string;
46
+ sent_to: number;
47
+ queued_at: string;
48
+ }, {
49
+ success: boolean;
50
+ message_id: string;
51
+ sent_to: number;
52
+ queued_at: string;
53
+ }>;
54
+ export default sendNotification;
55
+ import { z } from 'zod';
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import axios from 'axios';
7
7
  import { z } from 'zod';
8
- import { configInstance, parseStateZeroError } from '../../../src';
8
+ import { configInstance, parseStateZeroError } from '../../../../src';
9
9
  /**
10
10
  * Zod schema for the input of sendNotification.
11
11
  * NOTE: This is an object schema for validating the data payload.
@@ -0,0 +1 @@
1
+ export * from "./django_app";
@@ -0,0 +1 @@
1
+ export * from './django_app';
@@ -1 +1 @@
1
- export * from "./django_app";
1
+ export * from "./default/index";
@@ -1 +1,5 @@
1
- export * from './django_app';
1
+ /**
2
+ * This file was auto-generated. Do not make direct changes to the file.
3
+ * Re-exports from the 'default' backend for backwards compatibility.
4
+ */
5
+ export * from './default/index';
@@ -439,9 +439,10 @@ function prepareActionTemplateData(modulePath, functionName, actionName, actionD
439
439
  }
440
440
  async function generateActionFile(backend, actionName, actionDefinition) {
441
441
  const functionName = _.camelCase(actionName);
442
- const modulePath = process.env.NODE_ENV === "test" ? "../../../src" : "@statezero/core";
442
+ const modulePath = process.env.NODE_ENV === "test" ? "../../../../src" : "@statezero/core";
443
443
  const appName = (actionDefinition.app || "general").toLowerCase();
444
- const outDir = path.join(backend.GENERATED_ACTIONS_DIR, appName);
444
+ // Always include the backend key as the top-level folder
445
+ const outDir = path.join(backend.GENERATED_ACTIONS_DIR, backend.NAME, appName);
445
446
  await fs.mkdir(outDir, { recursive: true });
446
447
  const templateData = prepareActionTemplateData(modulePath, functionName, actionName, actionDefinition, backend.NAME);
447
448
  const fileName = _.kebabCase(actionName);
@@ -469,6 +470,7 @@ async function generateActionRegistry(generatedFiles, backendConfigs) {
469
470
  const registryByBackend = {};
470
471
  const schemasByBackend = {};
471
472
  const allImports = new Set();
473
+ const allSchemaImports = new Set();
472
474
  for (const file of generatedFiles) {
473
475
  const backendKey = file.backend;
474
476
  if (!backendKey)
@@ -482,12 +484,16 @@ async function generateActionRegistry(generatedFiles, backendConfigs) {
482
484
  const importPath = path
483
485
  .relative(process.cwd(), path.join(actionsDir, `${file.relativePath}.js`))
484
486
  .replace(/\\/g, "/");
487
+ // Generate action import
485
488
  const importStatement = `import { ${functionName} } from './${importPath}';`;
486
489
  allImports.add(importStatement);
487
490
  registryByBackend[backendKey].actions[file.action] = functionName;
488
- // Store schema path for getSchema function
491
+ // Generate schema import
492
+ const schemaImportName = `${functionName}Schema`;
489
493
  const schemaPath = `./${importPath.replace(/\.js$/, '')}.schema.json`;
490
- schemasByBackend[backendKey][file.action] = schemaPath;
494
+ const schemaImportStatement = `import ${schemaImportName} from '${schemaPath}' assert { type: 'json' };`;
495
+ allSchemaImports.add(schemaImportStatement);
496
+ schemasByBackend[backendKey][file.action] = schemaImportName;
491
497
  }
492
498
  let registryContent = `/**
493
499
  * This file was auto-generated. Do not make direct changes to the file.
@@ -495,9 +501,11 @@ async function generateActionRegistry(generatedFiles, backendConfigs) {
495
501
  */
496
502
 
497
503
  `;
504
+ // Add action imports
498
505
  registryContent += Array.from(allImports).sort().join("\n") + "\n\n";
499
- // Add schema paths constant
500
- registryContent += `const SCHEMA_PATHS = ${JSON.stringify(schemasByBackend, null, 2)};\n\n`;
506
+ // Add schema imports
507
+ registryContent += Array.from(allSchemaImports).sort().join("\n") + "\n\n";
508
+ // Add ACTION_REGISTRY
501
509
  registryContent += `export const ACTION_REGISTRY = {\n`;
502
510
  Object.entries(registryByBackend).forEach(([backendKey, data], index, arr) => {
503
511
  registryContent += ` '${backendKey}': {\n`;
@@ -508,6 +516,17 @@ async function generateActionRegistry(generatedFiles, backendConfigs) {
508
516
  registryContent += ` }${index < arr.length - 1 ? "," : ""}\n`;
509
517
  });
510
518
  registryContent += `};\n\n`;
519
+ // Add SCHEMA_REGISTRY
520
+ registryContent += `export const SCHEMA_REGISTRY = {\n`;
521
+ Object.entries(schemasByBackend).forEach(([backendKey, schemas], index, arr) => {
522
+ registryContent += ` '${backendKey}': {\n`;
523
+ const schemaEntries = Object.entries(schemas);
524
+ schemaEntries.forEach(([actionName, schemaName], idx, schemasArr) => {
525
+ registryContent += ` '${actionName}': ${schemaName}${idx < schemasArr.length - 1 ? "," : ""}\n`;
526
+ });
527
+ registryContent += ` }${index < arr.length - 1 ? "," : ""}\n`;
528
+ });
529
+ registryContent += `};\n\n`;
511
530
  registryContent += `/**
512
531
  * Get an action function by name and config key
513
532
  * @param {string} actionName - The name of the action
@@ -524,25 +543,18 @@ export function getAction(actionName, configKey) {
524
543
  }
525
544
 
526
545
  /**
527
- * Get the full schema for an action by loading its schema file
546
+ * Get the full schema for an action
528
547
  * @param {string} actionName - The name of the action
529
548
  * @param {string} configKey - The backend config key
530
- * @returns {Promise<Object|null>} The action schema or null if not found
549
+ * @returns {Object|null} The action schema or null if not found
531
550
  */
532
- export async function getSchema(actionName, configKey) {
533
- const schemaPath = SCHEMA_PATHS[configKey]?.[actionName];
534
- if (!schemaPath) {
551
+ export function getSchema(actionName, configKey) {
552
+ const schema = SCHEMA_REGISTRY[configKey]?.[actionName];
553
+ if (!schema) {
535
554
  console.warn(\`Schema for action '\${actionName}' not found for config key '\${configKey}'.\`);
536
555
  return null;
537
556
  }
538
-
539
- try {
540
- const schema = await import(schemaPath, { assert: { type: 'json' } });
541
- return schema.default || schema;
542
- } catch (error) {
543
- console.error(\`Error loading schema for action '\${actionName}':\`, error);
544
- return null;
545
- }
557
+ return schema;
546
558
  }
547
559
  `;
548
560
  const registryFilePath = path.join(process.cwd(), "action-registry.js");
@@ -562,7 +574,8 @@ export * from '{{this.relativePath}}';
562
574
  const filesByApp = _.groupBy(backendFiles, "appName");
563
575
  for (const appName in filesByApp) {
564
576
  const appFiles = filesByApp[appName];
565
- const appDir = path.join(rootActionsDir, appName);
577
+ // Include backend key in the path
578
+ const appDir = path.join(rootActionsDir, backendName, appName);
566
579
  const appIndexExports = appFiles.map((file) => {
567
580
  const relativePathToAppDir = `./${path.basename(file.relativePath)}`;
568
581
  return { ...file, relativePath: relativePathToAppDir };
@@ -573,10 +586,39 @@ export * from '{{this.relativePath}}';
573
586
  rootExports.push(`export * from './${appName}';`);
574
587
  }
575
588
  const rootIndexContent = rootExports.sort().join("\n");
576
- await fs.writeFile(path.join(rootActionsDir, "index.js"), rootIndexContent);
577
- await fs.writeFile(path.join(rootActionsDir, "index.d.ts"), rootIndexContent);
589
+ // Include backend key in the path for root index files
590
+ const backendDir = path.join(rootActionsDir, backendName);
591
+ await fs.writeFile(path.join(backendDir, "index.js"), rootIndexContent);
592
+ await fs.writeFile(path.join(backendDir, "index.d.ts"), rootIndexContent);
578
593
  }
579
594
  }
595
+ /**
596
+ * Generates a top-level index file that re-exports from the 'default' backend
597
+ * if it exists. This improves backwards compatibility and reduces verbosity.
598
+ * @param {Object.<string, BackendConfig>} backendConfigs
599
+ * @returns {Promise<void>}
600
+ */
601
+ async function generateDefaultBackendIndex(backendConfigs) {
602
+ // Check if a 'default' backend exists
603
+ if (!backendConfigs['default']) {
604
+ return; // No default backend, skip
605
+ }
606
+ const defaultBackend = backendConfigs['default'];
607
+ const actionsDir = defaultBackend.GENERATED_ACTIONS_DIR;
608
+ // Create top-level index files that re-export from default
609
+ const indexContent = `/**
610
+ * This file was auto-generated. Do not make direct changes to the file.
611
+ * Re-exports from the 'default' backend for backwards compatibility.
612
+ */
613
+
614
+ export * from './default/index';
615
+ `;
616
+ const jsIndexPath = path.join(actionsDir, 'index.js');
617
+ const dtsIndexPath = path.join(actionsDir, 'index.d.ts');
618
+ await fs.writeFile(jsIndexPath, indexContent);
619
+ await fs.writeFile(dtsIndexPath, indexContent);
620
+ console.log(`✨ Generated top-level index for 'default' backend`);
621
+ }
580
622
  // ================================================================================================
581
623
  // MAIN SCRIPT RUNNER
582
624
  // ================================================================================================
@@ -645,6 +687,8 @@ async function main() {
645
687
  progressBar.stop();
646
688
  await generateAppLevelIndexFiles(allGeneratedFiles, backendConfigs);
647
689
  await generateActionRegistry(allGeneratedFiles, backendConfigs);
690
+ // Generate top-level index for 'default' backend if it exists
691
+ await generateDefaultBackendIndex(backendConfigs);
648
692
  console.log(`\n✨ Generated ${allGeneratedFiles.length} actions successfully.`);
649
693
  }
650
694
  /**
@@ -521,9 +521,11 @@ async function generateSchemaForModel(backend, model) {
521
521
  const interfaceName = `${className}Fields`;
522
522
  const parts = model.split(".");
523
523
  const currentApp = parts.length > 1 ? parts[0] : "";
524
- const modulePath = process.env.NODE_ENV === "test" ? "../../../src" : "@statezero/core";
524
+ // With the backend folder added, we need an extra ../
525
+ const modulePath = process.env.NODE_ENV === "test" ? "../../../../src" : "@statezero/core";
525
526
  const templateData = prepareTemplateData(modulePath, className, interfaceName, rawModelName, schema, currentApp, backend.NAME);
526
- let outDir = backend.GENERATED_TYPES_DIR;
527
+ // Always include the backend key as the top-level folder
528
+ let outDir = path.join(backend.GENERATED_TYPES_DIR, backend.NAME);
527
529
  if (parts.length > 1) {
528
530
  outDir = path.join(outDir, ...parts.slice(0, -1).map((p) => p.toLowerCase()));
529
531
  }
@@ -908,7 +910,8 @@ export * from '{{this.relativePath}}';
908
910
  }
909
911
  else {
910
912
  // Create app-level index files with proper relative paths
911
- const appDir = path.join(backend.GENERATED_TYPES_DIR, app.toLowerCase());
913
+ // Include backend key in the path
914
+ const appDir = path.join(backend.GENERATED_TYPES_DIR, backend.NAME, app.toLowerCase());
912
915
  // Create relative paths for imports within the app directory
913
916
  // These should be relative to the app directory, not the backend root
914
917
  const appFiles = files.map((file) => {
@@ -931,8 +934,10 @@ export * from '{{this.relativePath}}';
931
934
  "export * from './fileobject';", // Add this line
932
935
  ...rootExports,
933
936
  ].join("\n");
934
- await fs.writeFile(path.join(backend.GENERATED_TYPES_DIR, "index.js"), backendIndexContent);
935
- await fs.writeFile(path.join(backend.GENERATED_TYPES_DIR, "index.d.ts"), backendIndexContent);
937
+ // Include backend key in the path for index files
938
+ const backendDir = path.join(backend.GENERATED_TYPES_DIR, backend.NAME);
939
+ await fs.writeFile(path.join(backendDir, "index.js"), backendIndexContent);
940
+ await fs.writeFile(path.join(backendDir, "index.d.ts"), backendIndexContent);
936
941
  }
937
942
  }
938
943
  // FileObject template
@@ -962,7 +967,10 @@ async function generateFileObjectForBackend(backend) {
962
967
  modulePath: modulePath,
963
968
  };
964
969
  const fileObjectContent = fileObjectTemplate(templateData);
965
- const fileObjectPath = path.join(backend.GENERATED_TYPES_DIR, "fileobject.js");
970
+ // Include backend key in the path
971
+ const backendDir = path.join(backend.GENERATED_TYPES_DIR, backend.NAME);
972
+ await fs.mkdir(backendDir, { recursive: true });
973
+ const fileObjectPath = path.join(backendDir, "fileobject.js");
966
974
  await fs.writeFile(fileObjectPath, fileObjectContent);
967
975
  // Also generate TypeScript declaration
968
976
  const dtsContent = `/**
@@ -978,9 +986,36 @@ export declare class ${backend.NAME}FileObject extends BaseFileObject {
978
986
 
979
987
  export declare const FileObject: typeof ${backend.NAME}FileObject;
980
988
  `;
981
- const dtsPath = path.join(backend.GENERATED_TYPES_DIR, "fileobject.d.ts");
989
+ const dtsPath = path.join(backendDir, "fileobject.d.ts");
982
990
  await fs.writeFile(dtsPath, dtsContent);
983
991
  }
992
+ /**
993
+ * Generates a top-level index file that re-exports from the 'default' backend
994
+ * if it exists. This improves backwards compatibility and reduces verbosity.
995
+ * @param {Object.<string, BackendConfig>} backendConfigs
996
+ * @returns {Promise<void>}
997
+ */
998
+ async function generateDefaultBackendIndex(backendConfigs) {
999
+ // Check if a 'default' backend exists
1000
+ if (!backendConfigs['default']) {
1001
+ return; // No default backend, skip
1002
+ }
1003
+ const defaultBackend = backendConfigs['default'];
1004
+ const typesDir = defaultBackend.GENERATED_TYPES_DIR;
1005
+ // Create top-level index files that re-export from default
1006
+ const indexContent = `/**
1007
+ * This file was auto-generated. Do not make direct changes to the file.
1008
+ * Re-exports from the 'default' backend for backwards compatibility.
1009
+ */
1010
+
1011
+ export * from './default/index';
1012
+ `;
1013
+ const jsIndexPath = path.join(typesDir, 'index.js');
1014
+ const dtsIndexPath = path.join(typesDir, 'index.d.ts');
1015
+ await fs.writeFile(jsIndexPath, indexContent);
1016
+ await fs.writeFile(dtsIndexPath, indexContent);
1017
+ console.log(`✨ Generated top-level index for 'default' backend`);
1018
+ }
984
1019
  // --------------------
985
1020
  // Main Runner: Fetch models and prompt selection
986
1021
  // --------------------
@@ -1060,6 +1095,8 @@ async function main() {
1060
1095
  await generateAppLevelIndexFiles(allGeneratedFiles, backendConfigs);
1061
1096
  // Generate model registry
1062
1097
  await generateModelRegistry(allGeneratedFiles, backendConfigs);
1098
+ // Generate top-level index for 'default' backend if it exists
1099
+ await generateDefaultBackendIndex(backendConfigs);
1063
1100
  console.log(`✨ Generated JavaScript files with TypeScript declarations for ${selectedModels.length} models across ${Object.keys(backendConfigs).length} backends.`);
1064
1101
  }
1065
1102
  /**
@@ -14,6 +14,45 @@ function getBackendTimezone(ModelClass) {
14
14
  const backendConfig = config.backendConfigs[ModelClass.configKey] || config.backendConfigs.default;
15
15
  return backendConfig.BACKEND_TZ || 'UTC';
16
16
  }
17
+ /**
18
+ * Normalizes a filter value based on the field's schema type.
19
+ * This ensures Django-like behavior where date strings are automatically
20
+ * converted to Date objects for comparison.
21
+ *
22
+ * @param {string} fieldName - The field name to check in the schema
23
+ * @param {any} value - The filter value to normalize
24
+ * @param {Class} ModelClass - The model class containing the field
25
+ * @returns {any} The normalized value
26
+ */
27
+ function normalizeFilterValue(fieldName, value, ModelClass) {
28
+ // If no schema or field name, return value as-is
29
+ if (!ModelClass?.schema || !fieldName)
30
+ return value;
31
+ // Get the field schema
32
+ const fieldSchema = ModelClass.schema.properties?.[fieldName];
33
+ if (!fieldSchema)
34
+ return value;
35
+ const { type, format } = fieldSchema;
36
+ // Handle date/datetime fields - convert strings to Date objects
37
+ if (type === 'string' && (format === 'date' || format === 'date-time')) {
38
+ if (typeof value === 'string') {
39
+ const parsed = new Date(value);
40
+ // Only return the parsed date if it's valid
41
+ return isNaN(parsed.getTime()) ? value : parsed;
42
+ }
43
+ }
44
+ // Handle 'in' lookups - normalize array values
45
+ if (Array.isArray(value) && type === 'string' && (format === 'date' || format === 'date-time')) {
46
+ return value.map(v => {
47
+ if (typeof v === 'string') {
48
+ const parsed = new Date(v);
49
+ return isNaN(parsed.getTime()) ? v : parsed;
50
+ }
51
+ return v;
52
+ });
53
+ }
54
+ return value;
55
+ }
17
56
  /**
18
57
  * Process a Django-style field path with relationships to match Django ORM behavior.
19
58
  * This handles nested relationships by traversing the model schema and properly
@@ -61,6 +100,7 @@ function processFieldPath(fieldPath, value, ModelClass, options = {}) {
61
100
  let currentModel = ModelClass;
62
101
  let processedPath = [];
63
102
  let isRelationship = false;
103
+ let finalFieldName = null; // Track the actual field name for schema lookup
64
104
  for (let i = 0; i < fieldParts.length; i++) {
65
105
  let part = fieldParts[i];
66
106
  if (part === 'pk' && currentModel)
@@ -87,16 +127,19 @@ function processFieldPath(fieldPath, value, ModelClass, options = {}) {
87
127
  // to properly match Django's behavior
88
128
  const pkField = relatedModel.primaryKeyField || 'id';
89
129
  processedPath.push(pkField);
130
+ finalFieldName = pkField;
131
+ currentModel = relatedModel;
90
132
  }
91
133
  }
92
134
  else if (currentModel && currentModel.fields && currentModel.fields.includes(part)) {
93
135
  // This is a regular field
94
136
  processedPath.push(part);
137
+ finalFieldName = part;
95
138
  // If it's the last part, we're done
96
139
  if (isLastPart) {
97
140
  break;
98
141
  }
99
- // If it's not the last part but it's a regular field,
142
+ // If it's not the last part but it's a regular field,
100
143
  // we can't continue traversal
101
144
  throw new Error(`Field '${part}' in '${fieldPath}' is not a relationship field and cannot be traversed.`);
102
145
  }
@@ -107,28 +150,30 @@ function processFieldPath(fieldPath, value, ModelClass, options = {}) {
107
150
  }
108
151
  // Join the processed path parts, using dot notation for sift
109
152
  const finalPath = processedPath.join('.');
153
+ // Normalize the value based on the field schema
154
+ const normalizedValue = normalizeFilterValue(finalFieldName, value, currentModel);
110
155
  // Handle the date part + comparison chain if present
111
156
  if (lookupChain.length === 2) {
112
157
  const [datePart, comparisonOperator] = lookupChain;
113
- return createDatePartComparisonOperator(finalPath, datePart, comparisonOperator, value, isRelationship);
158
+ return createDatePartComparisonOperator(finalPath, datePart, comparisonOperator, normalizedValue, isRelationship);
114
159
  }
115
160
  // Handle the single lookup operation if present
116
161
  if (lookup) {
117
- return createOperatorFromLookup(finalPath, lookup, value, isRelationship);
162
+ return createOperatorFromLookup(finalPath, lookup, normalizedValue, isRelationship, currentModel, finalFieldName);
118
163
  }
119
164
  // If there's no explicit lookup and this is a relationship field,
120
165
  // we've already appended the PK field name to the path
121
166
  // so we just need to apply the equality operator to the value
122
167
  if (isRelationship) {
123
168
  // In case the user passed in a raw model as the query value
124
- let raw = value;
125
- if (value && typeof value === 'object' && 'pk' in value) {
126
- raw = value.pk;
169
+ let raw = normalizedValue;
170
+ if (normalizedValue && typeof normalizedValue === 'object' && 'pk' in normalizedValue) {
171
+ raw = normalizedValue.pk;
127
172
  }
128
173
  return { field: finalPath, operator: { $eq: raw } };
129
174
  }
130
175
  // Default to direct equality
131
- return { field: finalPath, operator: { $eq: value } };
176
+ return { field: finalPath, operator: { $eq: normalizedValue } };
132
177
  }
133
178
  /**
134
179
  * Creates a special operator for date part comparison (e.g., created_at__hour__gt: 12)
@@ -157,11 +202,13 @@ function createDatePartComparisonOperator(field, datePart, comparisonOperator, v
157
202
  * Creates a sift operator from a Django-style lookup
158
203
  * @param {string} field - Processed field path
159
204
  * @param {string} lookup - Django-style lookup (e.g., 'contains', 'iexact')
160
- * @param {any} value - Value to filter by
205
+ * @param {any} value - Value to filter by (already normalized)
161
206
  * @param {boolean} isRelationship - Whether the field is a relationship
207
+ * @param {Class} ModelClass - The model class (unused, for future extensibility)
208
+ * @param {string} finalFieldName - The final field name (unused, for future extensibility)
162
209
  * @returns {Object} Object with field name and sift operator
163
210
  */
164
- function createOperatorFromLookup(field, lookup, value, isRelationship) {
211
+ function createOperatorFromLookup(field, lookup, value, isRelationship, ModelClass, finalFieldName) {
165
212
  // Helper function to escape special characters in regex
166
213
  function escapeRegExp(string) {
167
214
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
@@ -42,6 +42,6 @@ export class ComprehensiveModel extends Model {
42
42
  */
43
43
  private _defineProperties;
44
44
  }
45
- import { QuerySet } from '../../../src';
46
- import { Manager } from '../../../src';
47
- import { Model } from '../../../src';
45
+ import { QuerySet } from '../../../../src';
46
+ import { Manager } from '../../../../src';
47
+ import { Model } from '../../../../src';
@@ -0,0 +1,63 @@
1
+ /**
2
+ * This file was auto-generated. Do not make direct changes to the file.
3
+ */
4
+ import { Model, Manager, QuerySet, getModelClass } from '../../../../src';
5
+ import { wrapReactiveModel } from '../../../../src';
6
+ import schemaData from './comprehensivemodel.schema.json';
7
+ /**
8
+ * Model-specific QuerySet implementation
9
+ */
10
+ export class ComprehensiveModelQuerySet extends QuerySet {
11
+ }
12
+ /**
13
+ * Model-specific Manager implementation
14
+ */
15
+ export class ComprehensiveModelManager extends Manager {
16
+ constructor(ModelClass) {
17
+ super(ModelClass, ComprehensiveModelQuerySet);
18
+ }
19
+ newQuerySet() {
20
+ return new ComprehensiveModelQuerySet(this.ModelClass);
21
+ }
22
+ }
23
+ /**
24
+ * Implementation of the ComprehensiveModel model
25
+ */
26
+ export class ComprehensiveModel extends Model {
27
+ constructor(data) {
28
+ ComprehensiveModel.validateFields(data);
29
+ super(data);
30
+ // Define getters and setters for all fields
31
+ this._defineProperties();
32
+ return wrapReactiveModel(this);
33
+ }
34
+ /**
35
+ * Define property getters and setters for all model fields
36
+ * @private
37
+ */
38
+ _defineProperties() {
39
+ // For each field, define a property that gets/sets from internal storage
40
+ ComprehensiveModel.fields.forEach(field => {
41
+ Object.defineProperty(this, field, {
42
+ get: function () {
43
+ return this.getField(field);
44
+ },
45
+ set: function (value) {
46
+ this.setField(field, value);
47
+ },
48
+ enumerable: true, // Make sure fields are enumerable for serialization
49
+ configurable: true
50
+ });
51
+ });
52
+ }
53
+ }
54
+ // Bind this model to its backend
55
+ ComprehensiveModel.configKey = 'backend1';
56
+ ComprehensiveModel.modelName = 'django_app.comprehensivemodel';
57
+ ComprehensiveModel.primaryKeyField = 'id';
58
+ ComprehensiveModel.objects = new ComprehensiveModelManager(ComprehensiveModel);
59
+ ComprehensiveModel.fields = ['id', 'char_field', 'text_field', 'int_field', 'bool_field', 'datetime_field', 'decimal_field', 'json_field', 'money_field_currency', 'money_field', 'related'];
60
+ ComprehensiveModel.schema = schemaData;
61
+ ComprehensiveModel.relationshipFields = new Map([
62
+ ['related', { 'ModelClass': () => getModelClass('django_app.deepmodellevel1', 'backend1'), 'relationshipType': 'foreign-key' }]
63
+ ]);
@@ -39,6 +39,6 @@ export class CustomPKModel extends Model {
39
39
  */
40
40
  private _defineProperties;
41
41
  }
42
- import { QuerySet } from '../../../src';
43
- import { Manager } from '../../../src';
44
- import { Model } from '../../../src';
42
+ import { QuerySet } from '../../../../src';
43
+ import { Manager } from '../../../../src';
44
+ import { Model } from '../../../../src';