@statezero/core 0.1.76 → 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 +38 -5
  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);
@@ -573,7 +574,8 @@ export * from '{{this.relativePath}}';
573
574
  const filesByApp = _.groupBy(backendFiles, "appName");
574
575
  for (const appName in filesByApp) {
575
576
  const appFiles = filesByApp[appName];
576
- const appDir = path.join(rootActionsDir, appName);
577
+ // Include backend key in the path
578
+ const appDir = path.join(rootActionsDir, backendName, appName);
577
579
  const appIndexExports = appFiles.map((file) => {
578
580
  const relativePathToAppDir = `./${path.basename(file.relativePath)}`;
579
581
  return { ...file, relativePath: relativePathToAppDir };
@@ -584,10 +586,39 @@ export * from '{{this.relativePath}}';
584
586
  rootExports.push(`export * from './${appName}';`);
585
587
  }
586
588
  const rootIndexContent = rootExports.sort().join("\n");
587
- await fs.writeFile(path.join(rootActionsDir, "index.js"), rootIndexContent);
588
- 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);
589
593
  }
590
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
+ }
591
622
  // ================================================================================================
592
623
  // MAIN SCRIPT RUNNER
593
624
  // ================================================================================================
@@ -656,6 +687,8 @@ async function main() {
656
687
  progressBar.stop();
657
688
  await generateAppLevelIndexFiles(allGeneratedFiles, backendConfigs);
658
689
  await generateActionRegistry(allGeneratedFiles, backendConfigs);
690
+ // Generate top-level index for 'default' backend if it exists
691
+ await generateDefaultBackendIndex(backendConfigs);
659
692
  console.log(`\n✨ Generated ${allGeneratedFiles.length} actions successfully.`);
660
693
  }
661
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';
@@ -0,0 +1,61 @@
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 './custompkmodel.schema.json';
7
+ /**
8
+ * Model-specific QuerySet implementation
9
+ */
10
+ export class CustomPKModelQuerySet extends QuerySet {
11
+ }
12
+ /**
13
+ * Model-specific Manager implementation
14
+ */
15
+ export class CustomPKModelManager extends Manager {
16
+ constructor(ModelClass) {
17
+ super(ModelClass, CustomPKModelQuerySet);
18
+ }
19
+ newQuerySet() {
20
+ return new CustomPKModelQuerySet(this.ModelClass);
21
+ }
22
+ }
23
+ /**
24
+ * Implementation of the CustomPKModel model
25
+ */
26
+ export class CustomPKModel extends Model {
27
+ constructor(data) {
28
+ CustomPKModel.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
+ CustomPKModel.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
+ CustomPKModel.configKey = 'backend1';
56
+ CustomPKModel.modelName = 'django_app.custompkmodel';
57
+ CustomPKModel.primaryKeyField = 'custom_pk';
58
+ CustomPKModel.objects = new CustomPKModelManager(CustomPKModel);
59
+ CustomPKModel.fields = ['custom_pk', 'name'];
60
+ CustomPKModel.schema = schemaData;
61
+ CustomPKModel.relationshipFields = new Map([]);
@@ -42,6 +42,6 @@ export class DeepModelLevel1 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';