@statezero/core 0.1.76 → 0.1.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/backend1/django_app/calculate-hash.js +77 -0
- package/dist/actions/backend1/django_app/get-current-username.js +62 -0
- package/dist/actions/backend1/django_app/get-server-status.js +65 -0
- package/dist/actions/backend1/django_app/get-user-info.js +67 -0
- package/dist/actions/backend1/django_app/process-data.js +75 -0
- package/dist/actions/backend1/django_app/send-notification.js +78 -0
- package/dist/actions/backend1/index.d.ts +1 -0
- package/dist/actions/backend1/index.js +1 -0
- package/dist/actions/default/django_app/calculate-hash.d.ts +57 -0
- package/dist/actions/{django_app → default/django_app}/calculate-hash.js +1 -1
- package/dist/actions/default/django_app/get-current-username.d.ts +29 -0
- package/dist/actions/{django_app → default/django_app}/get-current-username.js +1 -1
- package/dist/actions/default/django_app/get-server-status.d.ts +38 -0
- package/dist/actions/{django_app → default/django_app}/get-server-status.js +1 -1
- package/dist/actions/default/django_app/get-user-info.d.ts +44 -0
- package/dist/actions/{django_app → default/django_app}/get-user-info.js +1 -1
- package/dist/actions/default/django_app/index.d.ts +6 -0
- package/dist/actions/default/django_app/index.js +6 -0
- package/dist/actions/default/django_app/process-data.d.ts +51 -0
- package/dist/actions/{django_app → default/django_app}/process-data.js +1 -1
- package/dist/actions/default/django_app/send-notification.d.ts +55 -0
- package/dist/actions/{django_app → default/django_app}/send-notification.js +1 -1
- package/dist/actions/default/index.d.ts +1 -0
- package/dist/actions/default/index.js +1 -0
- package/dist/actions/index.d.ts +1 -1
- package/dist/actions/index.js +5 -1
- package/dist/cli/commands/syncActions.js +38 -5
- package/dist/cli/commands/syncModels.js +44 -7
- package/dist/filtering/localFiltering.js +92 -11
- package/dist/models/{django_app → backend1/django_app}/comprehensivemodel.d.ts +3 -3
- package/dist/models/backend1/django_app/comprehensivemodel.js +63 -0
- package/dist/models/{django_app → backend1/django_app}/custompkmodel.d.ts +3 -3
- package/dist/models/backend1/django_app/custompkmodel.js +61 -0
- package/dist/models/{django_app → backend1/django_app}/deepmodellevel1.d.ts +3 -3
- package/dist/models/backend1/django_app/deepmodellevel1.js +64 -0
- package/dist/models/{django_app → backend1/django_app}/deepmodellevel2.d.ts +3 -3
- package/dist/models/backend1/django_app/deepmodellevel2.js +63 -0
- package/dist/models/{django_app → backend1/django_app}/deepmodellevel3.d.ts +3 -3
- package/dist/models/backend1/django_app/deepmodellevel3.js +61 -0
- package/dist/models/{django_app → backend1/django_app}/dummymodel.d.ts +3 -3
- package/dist/models/backend1/django_app/dummymodel.js +63 -0
- package/dist/models/{django_app → backend1/django_app}/dummyrelatedmodel.d.ts +3 -3
- package/dist/models/backend1/django_app/dummyrelatedmodel.js +61 -0
- package/dist/models/{django_app → backend1/django_app}/filetest.d.ts +3 -3
- package/dist/models/backend1/django_app/filetest.js +61 -0
- package/dist/models/{django_app → backend1/django_app}/modelwithcustompkrelation.d.ts +3 -3
- package/dist/models/backend1/django_app/modelwithcustompkrelation.js +63 -0
- package/dist/models/{django_app → backend1/django_app}/namefiltercustompkmodel.d.ts +3 -3
- package/dist/models/backend1/django_app/namefiltercustompkmodel.js +61 -0
- package/dist/models/{django_app → backend1/django_app}/order.d.ts +3 -3
- package/dist/models/backend1/django_app/order.js +61 -0
- package/dist/models/{django_app → backend1/django_app}/orderitem.d.ts +3 -3
- package/dist/models/backend1/django_app/orderitem.js +64 -0
- package/dist/models/{django_app → backend1/django_app}/product.d.ts +3 -3
- package/dist/models/backend1/django_app/product.js +63 -0
- package/dist/models/{django_app → backend1/django_app}/productcategory.d.ts +3 -3
- package/dist/models/backend1/django_app/productcategory.js +61 -0
- package/dist/models/backend1/fileobject.d.ts +4 -0
- package/dist/models/backend1/fileobject.js +9 -0
- package/dist/models/backend1/index.d.ts +2 -0
- package/dist/models/backend1/index.js +2 -0
- package/dist/models/default/django_app/comprehensivemodel.d.ts +47 -0
- package/dist/models/{django_app → default/django_app}/comprehensivemodel.js +2 -2
- package/dist/models/default/django_app/custompkmodel.d.ts +44 -0
- package/dist/models/{django_app → default/django_app}/custompkmodel.js +2 -2
- package/dist/models/default/django_app/deepmodellevel1.d.ts +47 -0
- package/dist/models/{django_app → default/django_app}/deepmodellevel1.js +2 -2
- package/dist/models/default/django_app/deepmodellevel2.d.ts +47 -0
- package/dist/models/{django_app → default/django_app}/deepmodellevel2.js +2 -2
- package/dist/models/default/django_app/deepmodellevel3.d.ts +44 -0
- package/dist/models/{django_app → default/django_app}/deepmodellevel3.js +2 -2
- package/dist/models/default/django_app/dummymodel.d.ts +47 -0
- package/dist/models/{django_app → default/django_app}/dummymodel.js +2 -2
- package/dist/models/default/django_app/dummyrelatedmodel.d.ts +44 -0
- package/dist/models/{django_app → default/django_app}/dummyrelatedmodel.js +2 -2
- package/dist/models/default/django_app/filetest.d.ts +44 -0
- package/dist/models/{django_app → default/django_app}/filetest.js +2 -2
- package/dist/models/default/django_app/index.d.ts +14 -0
- package/dist/models/default/django_app/index.js +14 -0
- package/dist/models/default/django_app/modelwithcustompkrelation.d.ts +47 -0
- package/dist/models/{django_app → default/django_app}/modelwithcustompkrelation.js +2 -2
- package/dist/models/default/django_app/namefiltercustompkmodel.d.ts +44 -0
- package/dist/models/{django_app → default/django_app}/namefiltercustompkmodel.js +2 -2
- package/dist/models/default/django_app/order.d.ts +44 -0
- package/dist/models/{django_app → default/django_app}/order.js +2 -2
- package/dist/models/default/django_app/orderitem.d.ts +47 -0
- package/dist/models/{django_app → default/django_app}/orderitem.js +2 -2
- package/dist/models/default/django_app/product.d.ts +47 -0
- package/dist/models/{django_app → default/django_app}/product.js +2 -2
- package/dist/models/default/django_app/productcategory.d.ts +44 -0
- package/dist/models/{django_app → default/django_app}/productcategory.js +2 -2
- package/dist/models/default/fileobject.d.ts +4 -0
- package/dist/models/default/index.d.ts +2 -0
- package/dist/models/default/index.js +2 -0
- package/dist/models/index.d.ts +1 -2
- package/dist/models/index.js +5 -2
- package/package.json +1 -1
- package/dist/models/fileobject.d.ts +0 -4
- /package/dist/actions/{django_app → backend1/django_app}/calculate-hash.d.ts +0 -0
- /package/dist/actions/{django_app → backend1/django_app}/get-current-username.d.ts +0 -0
- /package/dist/actions/{django_app → backend1/django_app}/get-server-status.d.ts +0 -0
- /package/dist/actions/{django_app → backend1/django_app}/get-user-info.d.ts +0 -0
- /package/dist/actions/{django_app → backend1/django_app}/index.d.ts +0 -0
- /package/dist/actions/{django_app → backend1/django_app}/index.js +0 -0
- /package/dist/actions/{django_app → backend1/django_app}/process-data.d.ts +0 -0
- /package/dist/actions/{django_app → backend1/django_app}/send-notification.d.ts +0 -0
- /package/dist/models/{django_app → backend1/django_app}/index.d.ts +0 -0
- /package/dist/models/{django_app → backend1/django_app}/index.js +0 -0
- /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 '
|
|
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 '
|
|
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';
|
package/dist/actions/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "./
|
|
1
|
+
export * from "./default/index";
|
package/dist/actions/index.js
CHANGED
|
@@ -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" ? "
|
|
442
|
+
const modulePath = process.env.NODE_ENV === "test" ? "../../../../src" : "@statezero/core";
|
|
443
443
|
const appName = (actionDefinition.app || "general").toLowerCase();
|
|
444
|
-
|
|
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
|
-
|
|
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
|
-
|
|
588
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
935
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
@@ -33,10 +72,10 @@ function processFieldPath(fieldPath, value, ModelClass, options = {}) {
|
|
|
33
72
|
'exact', 'iexact', 'contains', 'icontains', 'startswith',
|
|
34
73
|
'istartswith', 'endswith', 'iendswith', 'in', 'gt', 'gte',
|
|
35
74
|
'lt', 'lte', 'isnull', 'regex', 'iregex', 'year', 'month',
|
|
36
|
-
'day', 'week_day', 'hour', 'minute', 'second'
|
|
75
|
+
'day', 'week_day', 'hour', 'minute', 'second', 'date', 'time'
|
|
37
76
|
];
|
|
38
77
|
// Date part lookups that can be followed by comparison lookups
|
|
39
|
-
const dateParts = ['year', 'month', 'day', 'week_day', 'hour', 'minute', 'second'];
|
|
78
|
+
const dateParts = ['year', 'month', 'day', 'week_day', 'hour', 'minute', 'second', 'date', 'time'];
|
|
40
79
|
// Comparison lookups that can follow date parts
|
|
41
80
|
const comparisonLookups = ['gt', 'gte', 'lt', 'lte', 'exact'];
|
|
42
81
|
let lookupChain = [];
|
|
@@ -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,
|
|
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,
|
|
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 =
|
|
125
|
-
if (
|
|
126
|
-
raw =
|
|
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:
|
|
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, '\\$&');
|
|
@@ -333,6 +380,40 @@ function createDateOperations(timezone = 'UTC') {
|
|
|
333
380
|
const second = getDatePart(value, dt => dt.second);
|
|
334
381
|
return second !== null && second === params;
|
|
335
382
|
}, ownerQuery, options);
|
|
383
|
+
},
|
|
384
|
+
// Date - extract date portion (ignore time)
|
|
385
|
+
$date(params, ownerQuery, options) {
|
|
386
|
+
return createEqualsOperation((value) => {
|
|
387
|
+
if (!value)
|
|
388
|
+
return false;
|
|
389
|
+
const dateValue = value instanceof Date ? value : new Date(value);
|
|
390
|
+
if (isNaN(dateValue.getTime()))
|
|
391
|
+
return false;
|
|
392
|
+
// Convert both to timezone and get date portions
|
|
393
|
+
const luxonDate = DateTime.fromJSDate(dateValue).setZone(timezone);
|
|
394
|
+
const paramDate = DateTime.fromJSDate(new Date(params)).setZone(timezone);
|
|
395
|
+
// Compare year, month, and day
|
|
396
|
+
return luxonDate.year === paramDate.year &&
|
|
397
|
+
luxonDate.month === paramDate.month &&
|
|
398
|
+
luxonDate.day === paramDate.day;
|
|
399
|
+
}, ownerQuery, options);
|
|
400
|
+
},
|
|
401
|
+
// Time - extract time portion (ignore date)
|
|
402
|
+
$time(params, ownerQuery, options) {
|
|
403
|
+
return createEqualsOperation((value) => {
|
|
404
|
+
if (!value)
|
|
405
|
+
return false;
|
|
406
|
+
const dateValue = value instanceof Date ? value : new Date(value);
|
|
407
|
+
if (isNaN(dateValue.getTime()))
|
|
408
|
+
return false;
|
|
409
|
+
// Convert to timezone
|
|
410
|
+
const luxonDate = DateTime.fromJSDate(dateValue).setZone(timezone);
|
|
411
|
+
const paramDate = DateTime.fromJSDate(new Date(params)).setZone(timezone);
|
|
412
|
+
// Compare hour, minute, and second
|
|
413
|
+
return luxonDate.hour === paramDate.hour &&
|
|
414
|
+
luxonDate.minute === paramDate.minute &&
|
|
415
|
+
luxonDate.second === paramDate.second;
|
|
416
|
+
}, ownerQuery, options);
|
|
336
417
|
}
|
|
337
418
|
};
|
|
338
419
|
// Define part extractors for each date part with Django compatibility
|
|
@@ -42,6 +42,6 @@ export class ComprehensiveModel extends Model {
|
|
|
42
42
|
*/
|
|
43
43
|
private _defineProperties;
|
|
44
44
|
}
|
|
45
|
-
import { QuerySet } from '
|
|
46
|
-
import { Manager } from '
|
|
47
|
-
import { Model } from '
|
|
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 '
|
|
43
|
-
import { Manager } from '
|
|
44
|
-
import { Model } from '
|
|
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 '
|
|
46
|
-
import { Manager } from '
|
|
47
|
-
import { Model } from '
|
|
45
|
+
import { QuerySet } from '../../../../src';
|
|
46
|
+
import { Manager } from '../../../../src';
|
|
47
|
+
import { Model } from '../../../../src';
|