rastack 0.0.16
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/CHANGELOG.md +43013 -0
- package/dist/.prettierrc +7 -0
- package/dist/compile/analyze.d.ts +12 -0
- package/dist/compile/analyze.js +233 -0
- package/dist/compile/index.d.ts +19 -0
- package/dist/compile/index.js +107 -0
- package/dist/compile/manifest.d.ts +4 -0
- package/dist/compile/manifest.js +12 -0
- package/dist/compile/model.d.ts +54 -0
- package/dist/compile/model.js +7 -0
- package/dist/compile/openapi.d.ts +11 -0
- package/dist/compile/openapi.js +233 -0
- package/dist/compile/program.d.ts +10 -0
- package/dist/compile/program.js +61 -0
- package/dist/csv-schema.d.ts +28 -0
- package/dist/csv-schema.js +162 -0
- package/dist/define/index.d.ts +117 -0
- package/dist/define/index.js +74 -0
- package/dist/entity-generation/delete-method.d.ts +1 -0
- package/dist/entity-generation/delete-method.js +31 -0
- package/dist/entity-generation/form.d.ts +13 -0
- package/dist/entity-generation/form.js +136 -0
- package/dist/entity-generation/get-method.d.ts +1 -0
- package/dist/entity-generation/get-method.js +67 -0
- package/dist/entity-generation/imports.d.ts +5 -0
- package/dist/entity-generation/imports.js +91 -0
- package/dist/entity-generation/sync-method.d.ts +30 -0
- package/dist/entity-generation/sync-method.js +158 -0
- package/dist/entity-generation/update-method.d.ts +1 -0
- package/dist/entity-generation/update-method.js +29 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +53 -0
- package/dist/rad-compile.d.ts +11 -0
- package/dist/rad-compile.js +82 -0
- package/dist/rad-wasm-build.d.ts +14 -0
- package/dist/rad-wasm-build.js +74 -0
- package/dist/rad.d.ts +17 -0
- package/dist/rad.js +88 -0
- package/dist/scan.d.ts +12 -0
- package/dist/scan.js +79 -0
- package/dist/schema/camel-to-pastel.d.ts +1 -0
- package/dist/schema/camel-to-pastel.js +7 -0
- package/dist/schema/capitalise-first-letter.d.ts +1 -0
- package/dist/schema/capitalise-first-letter.js +7 -0
- package/dist/schema/extract-response.d.ts +2 -0
- package/dist/schema/extract-response.js +30 -0
- package/dist/schema/fetch-schema.d.ts +7 -0
- package/dist/schema/fetch-schema.js +23 -0
- package/dist/schema/remove-non-model-paths.d.ts +2 -0
- package/dist/schema/remove-non-model-paths.js +15 -0
- package/dist/schema/to-camel-case.d.ts +1 -0
- package/dist/schema/to-camel-case.js +11 -0
- package/dist/schema/to-pastel-case.d.ts +1 -0
- package/dist/schema/to-pastel-case.js +10 -0
- package/dist/schema-convert.d.ts +1 -0
- package/dist/schema-convert.js +63 -0
- package/dist/schema-entities.d.ts +14 -0
- package/dist/schema-entities.js +293 -0
- package/dist/schema-fetch.d.ts +1 -0
- package/dist/schema-fetch.js +81 -0
- package/dist/schema-full.d.ts +1 -0
- package/dist/schema-full.js +45 -0
- package/dist/schema-index.d.ts +1 -0
- package/dist/schema-index.js +34 -0
- package/dist/schema-params.d.ts +1 -0
- package/dist/schema-params.js +100 -0
- package/dist/seed.d.ts +17 -0
- package/dist/seed.js +252 -0
- package/hooks/form/form.ts +207 -0
- package/hooks/form/index.ts +8 -0
- package/hooks/form/interfaces.ts +217 -0
- package/hooks/form/structure.ts +39 -0
- package/hooks/form/validate-schema.ts +49 -0
- package/hooks/index.ts +3 -0
- package/hooks/query/api.ts +42 -0
- package/hooks/query/delete.ts +45 -0
- package/hooks/query/fetch.ts +48 -0
- package/hooks/query/index.ts +21 -0
- package/hooks/query/interfaces.ts +111 -0
- package/hooks/query/list.ts +286 -0
- package/hooks/query/update.ts +88 -0
- package/hooks/query/url.ts +31 -0
- package/hooks/real-time/index.ts +1 -0
- package/hooks/real-time/pusher.ts +43 -0
- package/jest.config.cjs +6 -0
- package/package.json +57 -0
- package/provider/index.ts +13 -0
- package/provider/provider.tsx +187 -0
- package/provider/types.ts +114 -0
- package/provider/warehouse.ts +167 -0
- package/provider/wasm.ts +124 -0
- package/runtime.ts +3 -0
- package/src/.prettierrc +7 -0
- package/src/compile/analyze.ts +224 -0
- package/src/compile/index.ts +86 -0
- package/src/compile/manifest.ts +10 -0
- package/src/compile/model.ts +69 -0
- package/src/compile/openapi.ts +266 -0
- package/src/compile/program.ts +40 -0
- package/src/csv-schema.ts +187 -0
- package/src/define/index.ts +162 -0
- package/src/entity-generation/delete-method.ts +37 -0
- package/src/entity-generation/form.ts +169 -0
- package/src/entity-generation/get-method.ts +80 -0
- package/src/entity-generation/imports.ts +110 -0
- package/src/entity-generation/sync-method.ts +222 -0
- package/src/entity-generation/update-method.ts +38 -0
- package/src/index.ts +77 -0
- package/src/rad-compile.ts +88 -0
- package/src/rad-wasm-build.ts +102 -0
- package/src/rad.ts +101 -0
- package/src/scan.ts +89 -0
- package/src/schema/camel-to-pastel.ts +3 -0
- package/src/schema/capitalise-first-letter.ts +3 -0
- package/src/schema/extract-response.ts +38 -0
- package/src/schema/fetch-schema.ts +12 -0
- package/src/schema/remove-non-model-paths.ts +13 -0
- package/src/schema/to-camel-case.ts +7 -0
- package/src/schema/to-pastel-case.ts +6 -0
- package/src/schema-convert.ts +61 -0
- package/src/schema-entities.ts +401 -0
- package/src/schema-fetch.ts +82 -0
- package/src/schema-full.ts +38 -0
- package/src/schema-index.ts +29 -0
- package/src/schema-params.ts +116 -0
- package/src/seed.ts +297 -0
- package/sync/engine.ts +392 -0
- package/sync/hooks.ts +450 -0
- package/sync/index.ts +8 -0
- package/sync/persistence.ts +237 -0
- package/sync/provider.tsx +91 -0
- package/sync/registry.ts +36 -0
- package/sync/store.ts +126 -0
- package/sync/transactions.ts +300 -0
- package/sync/types.ts +94 -0
- package/test/compile.spec.ts +192 -0
- package/test/csv-schema.spec.ts +143 -0
- package/test/schema-entities.spec.ts +688 -0
- package/tsconfig.json +24 -0
- package/types.ts +19 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { toPascalCase } from "../schema/to-pastel-case";
|
|
2
|
+
|
|
3
|
+
type MutationOperation = {
|
|
4
|
+
operationId: string;
|
|
5
|
+
schema: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
type MutationOperations = {
|
|
9
|
+
[path: string]: {
|
|
10
|
+
put?: MutationOperation;
|
|
11
|
+
post?: MutationOperation;
|
|
12
|
+
patch?: MutationOperation;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type GroupedMutations = {
|
|
17
|
+
[functionName: string]: {
|
|
18
|
+
groupDetailClean: string;
|
|
19
|
+
mutations: string[];
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export function generateUpdateFormMethod(
|
|
24
|
+
appName: string,
|
|
25
|
+
modelName: string,
|
|
26
|
+
mutationOperations: MutationOperations,
|
|
27
|
+
) {
|
|
28
|
+
const modelDetail = toPascalCase(modelName);
|
|
29
|
+
const importStatements = new Set<string>();
|
|
30
|
+
const groupedMutations: GroupedMutations = {};
|
|
31
|
+
|
|
32
|
+
Object.entries(mutationOperations).forEach(([path, operations]) => {
|
|
33
|
+
const pathParts = path.split("/");
|
|
34
|
+
const groupDetail = toPascalCase(pathParts.slice(3).join("_"));
|
|
35
|
+
const groupDetailClean = cleanGroupDetail(groupDetail);
|
|
36
|
+
const functionName = `useUpdate${modelDetail}${groupDetailClean}Form`;
|
|
37
|
+
|
|
38
|
+
const mutations = generateMutations(operations);
|
|
39
|
+
if (mutations) {
|
|
40
|
+
if (!groupedMutations[functionName]) {
|
|
41
|
+
groupedMutations[functionName] = {
|
|
42
|
+
groupDetailClean,
|
|
43
|
+
mutations: [],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
groupedMutations[functionName].mutations.push(mutations);
|
|
47
|
+
|
|
48
|
+
Object.values(operations).forEach((operation) => {
|
|
49
|
+
if (operation && operation.schema) {
|
|
50
|
+
importStatements.add(`Deref${operation.schema}`);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const formCodeGen = Object.entries(groupedMutations)
|
|
57
|
+
.map(([functionName, { groupDetailClean, mutations }]) =>
|
|
58
|
+
generateFormMethod(groupDetailClean, mutations.join(",\n ")),
|
|
59
|
+
)
|
|
60
|
+
.join("\n\n");
|
|
61
|
+
|
|
62
|
+
return [formCodeGen, importStatements];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function cleanGroupDetail(groupDetail: string): string {
|
|
66
|
+
return groupDetail
|
|
67
|
+
.replace("V1", "")
|
|
68
|
+
.split("_")
|
|
69
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
70
|
+
.join("")
|
|
71
|
+
.replace(/{([^}]+)}/g, "");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function generateFormMethod(
|
|
75
|
+
groupDetailClean: string,
|
|
76
|
+
mutations: string,
|
|
77
|
+
): string {
|
|
78
|
+
return `
|
|
79
|
+
export function useUpdate${groupDetailClean}Form<
|
|
80
|
+
TFormValues extends object,
|
|
81
|
+
TPostParams,
|
|
82
|
+
TPutParams,
|
|
83
|
+
TPatchParams,
|
|
84
|
+
TPostPayload,
|
|
85
|
+
TPutPayload,
|
|
86
|
+
TPatchPayload,
|
|
87
|
+
TPostResult,
|
|
88
|
+
TPutResult,
|
|
89
|
+
TPatchResult,
|
|
90
|
+
TFormUpdate extends FormUpdate,
|
|
91
|
+
TSourceId extends string | undefined,
|
|
92
|
+
>({
|
|
93
|
+
id,
|
|
94
|
+
sourceData = {},
|
|
95
|
+
formValues,
|
|
96
|
+
formatResponse,
|
|
97
|
+
transformRequest,
|
|
98
|
+
transformResponse,
|
|
99
|
+
mutations = {
|
|
100
|
+
${mutations}
|
|
101
|
+
},
|
|
102
|
+
updateType,
|
|
103
|
+
onSuccess
|
|
104
|
+
}: FormProps<
|
|
105
|
+
TFormValues,
|
|
106
|
+
TPostParams,
|
|
107
|
+
TPutParams,
|
|
108
|
+
TPatchParams,
|
|
109
|
+
TPostPayload,
|
|
110
|
+
TPutPayload,
|
|
111
|
+
TPatchPayload,
|
|
112
|
+
TPostResult,
|
|
113
|
+
TPutResult,
|
|
114
|
+
TPatchResult,
|
|
115
|
+
TFormUpdate,
|
|
116
|
+
TSourceId
|
|
117
|
+
>) {
|
|
118
|
+
return useForm<
|
|
119
|
+
TFormValues,
|
|
120
|
+
TPostParams,
|
|
121
|
+
TPutParams,
|
|
122
|
+
TPatchParams,
|
|
123
|
+
TPostPayload,
|
|
124
|
+
TPutPayload,
|
|
125
|
+
TPatchPayload,
|
|
126
|
+
TPostResult,
|
|
127
|
+
TPutResult,
|
|
128
|
+
TPatchResult,
|
|
129
|
+
TFormUpdate,
|
|
130
|
+
TSourceId
|
|
131
|
+
>({
|
|
132
|
+
id,
|
|
133
|
+
mutations,
|
|
134
|
+
formValues,
|
|
135
|
+
sourceData,
|
|
136
|
+
formatResponse,
|
|
137
|
+
transformRequest,
|
|
138
|
+
transformResponse,
|
|
139
|
+
updateType,
|
|
140
|
+
onSuccess
|
|
141
|
+
});
|
|
142
|
+
}`;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function generateMutations(operations: MutationOperations[string]): string {
|
|
146
|
+
const mutationStrings = [];
|
|
147
|
+
if (operations.post?.operationId && operations.post?.schema) {
|
|
148
|
+
mutationStrings.push(`post: {
|
|
149
|
+
// @ts-ignore
|
|
150
|
+
mutation: usePost${operations.post.operationId}(),
|
|
151
|
+
validation: Deref${operations.post.schema}
|
|
152
|
+
}`);
|
|
153
|
+
}
|
|
154
|
+
if (operations.put?.operationId && operations.put?.schema) {
|
|
155
|
+
mutationStrings.push(`put: {
|
|
156
|
+
// @ts-ignore
|
|
157
|
+
mutation: usePut${operations.put.operationId}(),
|
|
158
|
+
validation: Deref${operations.put.schema}
|
|
159
|
+
}`);
|
|
160
|
+
}
|
|
161
|
+
if (operations.patch?.operationId && operations.patch?.schema) {
|
|
162
|
+
mutationStrings.push(`patch: {
|
|
163
|
+
// @ts-ignore
|
|
164
|
+
mutation: usePatch${operations.patch.operationId}(),
|
|
165
|
+
validation: Deref${operations.patch.schema}
|
|
166
|
+
}`);
|
|
167
|
+
}
|
|
168
|
+
return mutationStrings.join(",\n ");
|
|
169
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { capitaliseFirstLetter } from "../schema/capitalise-first-letter";
|
|
2
|
+
import { toCamelCase } from "../schema/to-camel-case";
|
|
3
|
+
|
|
4
|
+
export function generateGetAPIMethod(
|
|
5
|
+
url: string,
|
|
6
|
+
model: string,
|
|
7
|
+
operationId: string,
|
|
8
|
+
responseBody: string | undefined,
|
|
9
|
+
isList: boolean,
|
|
10
|
+
pathParameters: string[],
|
|
11
|
+
requiredParameters: string[],
|
|
12
|
+
method: string,
|
|
13
|
+
requestBodySchema: string,
|
|
14
|
+
): [string, Set<string>] {
|
|
15
|
+
const operationIdUpper = capitaliseFirstLetter(operationId);
|
|
16
|
+
const paramNames =
|
|
17
|
+
method === "post" ? `I${requestBodySchema}` : `I${operationIdUpper}Params`;
|
|
18
|
+
const modelCamel = toCamelCase(model);
|
|
19
|
+
|
|
20
|
+
url = url.replace("{", "${");
|
|
21
|
+
|
|
22
|
+
const pathParameterString = pathParameters.map((item) => {
|
|
23
|
+
return `const ${item} = options?.params?.${item};`;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const importStatements = new Set<string>();
|
|
27
|
+
|
|
28
|
+
if (isList) {
|
|
29
|
+
importStatements.add(
|
|
30
|
+
`ListQueryOptions, useGetPaginatedQueryList, useUpdateQuery`,
|
|
31
|
+
);
|
|
32
|
+
importStatements.add(paramNames);
|
|
33
|
+
importStatements.add(responseBody ?? "");
|
|
34
|
+
return [
|
|
35
|
+
`
|
|
36
|
+
export function use${operationIdUpper}(
|
|
37
|
+
options?: ListQueryOptions<${paramNames}, ${responseBody}>
|
|
38
|
+
) {
|
|
39
|
+
const { isAuthenticated } = useAuth0();
|
|
40
|
+
const pathParameters: (keyof ${paramNames})[] = [${pathParameters.map((item) => `'${item}'`).join(", ")}]
|
|
41
|
+
${pathParameterString.join("\n")}
|
|
42
|
+
|
|
43
|
+
return useGetPaginatedQueryList<${paramNames}, ${responseBody}>({
|
|
44
|
+
${method}Url: \`${url}\`,
|
|
45
|
+
queryKey: ${modelCamel}QueriesKeys.${operationId},
|
|
46
|
+
pathParameters,
|
|
47
|
+
isAuthenticated,
|
|
48
|
+
options,
|
|
49
|
+
});
|
|
50
|
+
}`,
|
|
51
|
+
importStatements,
|
|
52
|
+
];
|
|
53
|
+
} else {
|
|
54
|
+
importStatements.add(`GetQueryOptions, useGetQuery`);
|
|
55
|
+
importStatements.add(paramNames);
|
|
56
|
+
importStatements.add(responseBody || "");
|
|
57
|
+
return [
|
|
58
|
+
`
|
|
59
|
+
export function use${operationIdUpper}(
|
|
60
|
+
options?: GetQueryOptions<${paramNames}>
|
|
61
|
+
) {
|
|
62
|
+
const { isAuthenticated } = useAuth0();
|
|
63
|
+
const pathParameters: string[] = [${pathParameters.map((item) => `'${item}'`).join(", ")}]
|
|
64
|
+
${pathParameterString.join("\n")}
|
|
65
|
+
const requiredParams: string[] = [${requiredParameters.map((item) => `'${item}'`).join(", ")}]
|
|
66
|
+
return useGetQuery<${paramNames}, ${responseBody}>({
|
|
67
|
+
${method}Url: \`${url}\`,
|
|
68
|
+
queryKey: ${modelCamel}QueriesKeys.${operationId},
|
|
69
|
+
//@ts-ignore
|
|
70
|
+
pathParameters,
|
|
71
|
+
//@ts-ignore
|
|
72
|
+
requiredParams,
|
|
73
|
+
isAuthenticated,
|
|
74
|
+
options,
|
|
75
|
+
});
|
|
76
|
+
}`,
|
|
77
|
+
importStatements,
|
|
78
|
+
];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
export function removeDuplicateImports(importStatements: string): string {
|
|
2
|
+
const importLines = importStatements.split("\n");
|
|
3
|
+
const uniqueImports = new Set<string>();
|
|
4
|
+
const radStackImports = new Set<string>();
|
|
5
|
+
const radSyncImports = new Set<string>();
|
|
6
|
+
const schemaImports = new Set<string>();
|
|
7
|
+
|
|
8
|
+
importLines.forEach((line) => {
|
|
9
|
+
if (line.includes("rastack/sync")) {
|
|
10
|
+
const imports =
|
|
11
|
+
line
|
|
12
|
+
.match(/\{([^}]+)\}/)?.[1]
|
|
13
|
+
.split(",")
|
|
14
|
+
.map((s) => s.trim()) || [];
|
|
15
|
+
imports.forEach((imp) => radSyncImports.add(imp));
|
|
16
|
+
} else if (line.includes("rastack/hooks")) {
|
|
17
|
+
const imports =
|
|
18
|
+
line
|
|
19
|
+
.match(/\{([^}]+)\}/)?.[1]
|
|
20
|
+
.split(",")
|
|
21
|
+
.map((s) => s.trim()) || [];
|
|
22
|
+
imports.forEach((imp) => radStackImports.add(imp));
|
|
23
|
+
} else if (line.includes("@repo/schema/schema")) {
|
|
24
|
+
const imports =
|
|
25
|
+
line
|
|
26
|
+
.match(/\{([^}]+)\}/)?.[1]
|
|
27
|
+
.split(",")
|
|
28
|
+
.map((s) => s.trim()) || [];
|
|
29
|
+
imports.forEach((imp) => schemaImports.add(imp));
|
|
30
|
+
} else {
|
|
31
|
+
uniqueImports.add(line);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
let result = Array.from(uniqueImports).join("\n") + "\n";
|
|
36
|
+
|
|
37
|
+
if (radStackImports.size > 0) {
|
|
38
|
+
result += `import { ${Array.from(radStackImports).join(", ")} } from 'rastack/hooks';\n`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (radSyncImports.size > 0) {
|
|
42
|
+
result += `import { ${Array.from(radSyncImports).join(", ")} } from 'rastack/sync';\n`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (schemaImports.size > 0) {
|
|
46
|
+
result += `import { ${Array.from(schemaImports).join(", ")} } from '@repo/schema/schema';\n`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function generateImportStatements(
|
|
53
|
+
importStatements: Set<string>,
|
|
54
|
+
modelName: string,
|
|
55
|
+
): string {
|
|
56
|
+
const entries = Array.from(importStatements);
|
|
57
|
+
const syncImports = entries
|
|
58
|
+
.filter((importStatement) => importStatement.startsWith("sync:"))
|
|
59
|
+
.map((importStatement) => importStatement.slice("sync:".length))
|
|
60
|
+
.join(", ");
|
|
61
|
+
const queryImports = entries
|
|
62
|
+
.filter(
|
|
63
|
+
(importStatement) =>
|
|
64
|
+
!importStatement.startsWith("sync:") && importStatement.includes("use"),
|
|
65
|
+
)
|
|
66
|
+
.join(", ");
|
|
67
|
+
const schemaImports = entries
|
|
68
|
+
.filter(
|
|
69
|
+
(importStatement) =>
|
|
70
|
+
importStatement.startsWith("I") || importStatement.startsWith("Deref"),
|
|
71
|
+
)
|
|
72
|
+
.join(", ");
|
|
73
|
+
|
|
74
|
+
let importData =
|
|
75
|
+
"import { useForm, FormProps, FormUpdate, useUpdateQuery } from 'rastack/hooks';\n";
|
|
76
|
+
importData = importData + 'import { useAuth0 } from "@auth0/auth0-react";\n';
|
|
77
|
+
if (queryImports) {
|
|
78
|
+
importData += `import { ${queryImports} } from 'rastack/hooks';\n`;
|
|
79
|
+
}
|
|
80
|
+
if (syncImports) {
|
|
81
|
+
importData += `import { ${syncImports} } from 'rastack/sync';\n`;
|
|
82
|
+
}
|
|
83
|
+
if (schemaImports) {
|
|
84
|
+
importData += `import { ${schemaImports} } from '@repo/schema/schema';\n`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return removeDuplicateImports(importData);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function generateKeyImports(model: string, operations: string[]) {
|
|
91
|
+
model = model.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
|
|
92
|
+
let keyData = `export const ${model}QueriesKeys = {\n`;
|
|
93
|
+
operations.forEach((item) => {
|
|
94
|
+
const snakeCase = item.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
95
|
+
keyData += ` ${item}: "${snakeCase}",\n`;
|
|
96
|
+
});
|
|
97
|
+
return `${keyData}}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function generateIndexImports(model: string, operations: string[]) {
|
|
101
|
+
return `export * from "./api"`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function generateFolderImports(models: string[]) {
|
|
105
|
+
let importData = "";
|
|
106
|
+
models.map((model) => {
|
|
107
|
+
importData += `export * from "./${model}"\n`;
|
|
108
|
+
});
|
|
109
|
+
return importData;
|
|
110
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { capitaliseFirstLetter } from "../schema/capitalise-first-letter";
|
|
2
|
+
import { toCamelCase } from "../schema/to-camel-case";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generators for sync-mode entities (operations carrying the
|
|
6
|
+
* `x-rastack-sync` OpenAPI extension emitted by rastack). Reads bind to the
|
|
7
|
+
* local store, writes enqueue optimistic transactions, and operations the
|
|
8
|
+
* fat class marked as confirmed become blocking online-only mutations.
|
|
9
|
+
*
|
|
10
|
+
* Imports prefixed with "sync:" are routed to 'rastack/sync' by
|
|
11
|
+
* generateImportStatements.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export interface EntitySyncMeta {
|
|
15
|
+
sync: boolean;
|
|
16
|
+
/** Sync.load = "lazy": excluded from initial bootstrap, loaded on first use. */
|
|
17
|
+
lazy?: boolean;
|
|
18
|
+
/** Sync.client_ids = True: rows use client-generated UUID primary keys. */
|
|
19
|
+
clientIds?: boolean;
|
|
20
|
+
confirmedOperationIds: Set<string>;
|
|
21
|
+
listUrl?: string;
|
|
22
|
+
detailUrl?: string;
|
|
23
|
+
detailParam?: string;
|
|
24
|
+
listOperationId?: string;
|
|
25
|
+
detailOperationId?: string;
|
|
26
|
+
rowSchema?: string; // e.g. "IPet"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function entityConstName(model: string): string {
|
|
30
|
+
return `${toCamelCase(model)}Entity`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function generateEntityDescriptor(
|
|
34
|
+
appName: string,
|
|
35
|
+
modelName: string,
|
|
36
|
+
meta: EntitySyncMeta,
|
|
37
|
+
): [string, Set<string>] {
|
|
38
|
+
const importStatements = new Set<string>();
|
|
39
|
+
importStatements.add("sync:defineEntity");
|
|
40
|
+
if (meta.rowSchema) {
|
|
41
|
+
importStatements.add(meta.rowSchema);
|
|
42
|
+
}
|
|
43
|
+
const rowType = meta.rowSchema || "any";
|
|
44
|
+
const listUrl =
|
|
45
|
+
meta.listUrl ||
|
|
46
|
+
(meta.detailUrl ? meta.detailUrl.replace(/\/?\{[^}]+\}.*$/, "") : "");
|
|
47
|
+
const detailUrl = meta.detailUrl
|
|
48
|
+
? meta.detailUrl.replace(/\{[^}]+\}/, "${id}")
|
|
49
|
+
: `${listUrl.replace(/\/$/, "")}/\${id}`;
|
|
50
|
+
|
|
51
|
+
const lazyLine = meta.lazy ? `\n lazy: true,` : "";
|
|
52
|
+
const clientIdsLine = meta.clientIds ? `\n clientIds: true,` : "";
|
|
53
|
+
|
|
54
|
+
return [
|
|
55
|
+
`
|
|
56
|
+
export const ${entityConstName(modelName)} = defineEntity<${rowType}>({
|
|
57
|
+
app: "${appName}",
|
|
58
|
+
model: "${modelName}",
|
|
59
|
+
listUrl: \`${listUrl}\`,
|
|
60
|
+
detailUrl: (id) => \`${detailUrl}\`,${lazyLine}${clientIdsLine}
|
|
61
|
+
});
|
|
62
|
+
`,
|
|
63
|
+
importStatements,
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function generateSyncListMethod(
|
|
68
|
+
modelName: string,
|
|
69
|
+
operationId: string,
|
|
70
|
+
rowSchema: string | undefined,
|
|
71
|
+
): [string, Set<string>] {
|
|
72
|
+
const operationIdUpper = capitaliseFirstLetter(operationId);
|
|
73
|
+
const rowType = rowSchema || "any";
|
|
74
|
+
const importStatements = new Set<string>();
|
|
75
|
+
importStatements.add("sync:useEntityList");
|
|
76
|
+
importStatements.add("sync:EntityListOptions");
|
|
77
|
+
if (rowSchema) {
|
|
78
|
+
importStatements.add(rowSchema);
|
|
79
|
+
}
|
|
80
|
+
return [
|
|
81
|
+
`
|
|
82
|
+
export function use${operationIdUpper}(
|
|
83
|
+
options?: EntityListOptions<${rowType}>
|
|
84
|
+
) {
|
|
85
|
+
return useEntityList<${rowType}>(${entityConstName(modelName)}, options);
|
|
86
|
+
}`,
|
|
87
|
+
importStatements,
|
|
88
|
+
];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function generateSyncRecordMethod(
|
|
92
|
+
modelName: string,
|
|
93
|
+
operationId: string,
|
|
94
|
+
rowSchema: string | undefined,
|
|
95
|
+
pathParameters: string[],
|
|
96
|
+
): [string, Set<string>] {
|
|
97
|
+
const operationIdUpper = capitaliseFirstLetter(operationId);
|
|
98
|
+
const rowType = rowSchema || "any";
|
|
99
|
+
const paramNames = `I${operationIdUpper}Params`;
|
|
100
|
+
const idParam = pathParameters[0];
|
|
101
|
+
const importStatements = new Set<string>();
|
|
102
|
+
importStatements.add("sync:useEntityRecord");
|
|
103
|
+
importStatements.add(`GetQueryOptions, useGetQuery`);
|
|
104
|
+
importStatements.add(paramNames);
|
|
105
|
+
if (rowSchema) {
|
|
106
|
+
importStatements.add(rowSchema);
|
|
107
|
+
}
|
|
108
|
+
return [
|
|
109
|
+
`
|
|
110
|
+
export function use${operationIdUpper}(
|
|
111
|
+
options?: GetQueryOptions<${paramNames}>
|
|
112
|
+
) {
|
|
113
|
+
const id = options?.params?.${idParam};
|
|
114
|
+
return useEntityRecord<${rowType}>(${entityConstName(modelName)}, id as string | number | undefined);
|
|
115
|
+
}`,
|
|
116
|
+
importStatements,
|
|
117
|
+
];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function generateSyncUpdateMethod(
|
|
121
|
+
url: string,
|
|
122
|
+
method: string,
|
|
123
|
+
modelName: string,
|
|
124
|
+
operationId: string,
|
|
125
|
+
requestBody: string | undefined,
|
|
126
|
+
responseBody: string | undefined,
|
|
127
|
+
pathParameters: string[],
|
|
128
|
+
): [string, Set<string>] {
|
|
129
|
+
const operationIdUpper = capitaliseFirstLetter(operationId);
|
|
130
|
+
const paramNames = `I${operationIdUpper}Params`;
|
|
131
|
+
const methodUpper = method.charAt(0).toUpperCase() + method.slice(1);
|
|
132
|
+
const isCreate = pathParameters.length === 0;
|
|
133
|
+
const idParam = pathParameters[0];
|
|
134
|
+
|
|
135
|
+
let urlStructure = url.replace(/{([^}]+)}/g, "${params.$1}");
|
|
136
|
+
urlStructure = `(params) => \`${urlStructure}\``;
|
|
137
|
+
|
|
138
|
+
const importStatements = new Set<string>();
|
|
139
|
+
importStatements.add("sync:useEntityMutation");
|
|
140
|
+
importStatements.add(paramNames);
|
|
141
|
+
if (requestBody) {
|
|
142
|
+
importStatements.add(requestBody);
|
|
143
|
+
}
|
|
144
|
+
if (responseBody) {
|
|
145
|
+
importStatements.add(responseBody);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const idLine = isCreate
|
|
149
|
+
? ""
|
|
150
|
+
: `\n id: (params) => params.${idParam} as string | number | undefined,`;
|
|
151
|
+
|
|
152
|
+
return [
|
|
153
|
+
`
|
|
154
|
+
export function use${methodUpper}${operationIdUpper}() {
|
|
155
|
+
return useEntityMutation<${paramNames}, ${requestBody || "any"}, ${responseBody || "any"}>(${entityConstName(modelName)}, {
|
|
156
|
+
op: "${isCreate ? "create" : "update"}",
|
|
157
|
+
method: "${method}",
|
|
158
|
+
url: ${urlStructure},${idLine}
|
|
159
|
+
});
|
|
160
|
+
}`,
|
|
161
|
+
importStatements,
|
|
162
|
+
];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function generateSyncDeleteMethod(
|
|
166
|
+
url: string,
|
|
167
|
+
modelName: string,
|
|
168
|
+
operationId: string,
|
|
169
|
+
): [string, Set<string>] {
|
|
170
|
+
const operationIdUpper = capitaliseFirstLetter(operationId);
|
|
171
|
+
const urlStructure = url.replace(/{[^}]+}/, "${id}");
|
|
172
|
+
const importStatements = new Set<string>();
|
|
173
|
+
importStatements.add("sync:useEntityDelete");
|
|
174
|
+
|
|
175
|
+
return [
|
|
176
|
+
`
|
|
177
|
+
export function use${operationIdUpper}() {
|
|
178
|
+
return useEntityDelete(${entityConstName(modelName)}, {
|
|
179
|
+
url: (id) => \`${urlStructure}\`,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
`,
|
|
183
|
+
importStatements,
|
|
184
|
+
];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function generateConfirmedMethod(
|
|
188
|
+
url: string,
|
|
189
|
+
method: string,
|
|
190
|
+
operationId: string,
|
|
191
|
+
requestBody: string | undefined,
|
|
192
|
+
responseBody: string | undefined,
|
|
193
|
+
): [string, Set<string>] {
|
|
194
|
+
const operationIdUpper = capitaliseFirstLetter(operationId);
|
|
195
|
+
const paramNames = `I${operationIdUpper}Params`;
|
|
196
|
+
const methodUpper = method.charAt(0).toUpperCase() + method.slice(1);
|
|
197
|
+
|
|
198
|
+
let urlStructure = url.replace(/{([^}]+)}/g, "${params.$1}");
|
|
199
|
+
urlStructure = `(params) => \`${urlStructure}\``;
|
|
200
|
+
|
|
201
|
+
const importStatements = new Set<string>();
|
|
202
|
+
importStatements.add("sync:useConfirmedMutation");
|
|
203
|
+
importStatements.add(paramNames);
|
|
204
|
+
if (requestBody) {
|
|
205
|
+
importStatements.add(requestBody);
|
|
206
|
+
}
|
|
207
|
+
if (responseBody) {
|
|
208
|
+
importStatements.add(responseBody);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return [
|
|
212
|
+
`
|
|
213
|
+
// Confirmed ("sync") operation: blocking, online-only, no optimistic apply.
|
|
214
|
+
export function use${methodUpper}${operationIdUpper}() {
|
|
215
|
+
return useConfirmedMutation<${paramNames}, ${requestBody || "any"}, ${responseBody || "any"}>({
|
|
216
|
+
method: "${method}",
|
|
217
|
+
url: ${urlStructure},
|
|
218
|
+
});
|
|
219
|
+
}`,
|
|
220
|
+
importStatements,
|
|
221
|
+
];
|
|
222
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { capitaliseFirstLetter } from "../schema/capitalise-first-letter";
|
|
2
|
+
|
|
3
|
+
export function generateUpdateAPIMethod(
|
|
4
|
+
url: string,
|
|
5
|
+
method: string,
|
|
6
|
+
operationId: string,
|
|
7
|
+
requestBody: string | undefined,
|
|
8
|
+
responseBody: string | undefined,
|
|
9
|
+
listQueryCacheKey?: string,
|
|
10
|
+
itemQueryCacheKey?: string,
|
|
11
|
+
): [string, Set<string>] {
|
|
12
|
+
const operationIdUpper = capitaliseFirstLetter(operationId);
|
|
13
|
+
const paramNames = `I${operationIdUpper}Params`;
|
|
14
|
+
|
|
15
|
+
let urlStructure = url.replace(/{([^}]+)}/g, "${params.$1}");
|
|
16
|
+
urlStructure = `(params) => \`${urlStructure}\``;
|
|
17
|
+
|
|
18
|
+
const methodUpper = method.charAt(0).toUpperCase() + method.slice(1);
|
|
19
|
+
const importStatements = new Set<string>();
|
|
20
|
+
importStatements.add(paramNames ?? "");
|
|
21
|
+
importStatements.add(requestBody ?? "");
|
|
22
|
+
importStatements.add(responseBody ?? "");
|
|
23
|
+
|
|
24
|
+
const cacheKeyLines = [
|
|
25
|
+
listQueryCacheKey ? `\n listQueryCacheKey: ${listQueryCacheKey},` : "",
|
|
26
|
+
itemQueryCacheKey ? `\n itemQueryCacheKey: ${itemQueryCacheKey},` : "",
|
|
27
|
+
].join("");
|
|
28
|
+
|
|
29
|
+
return [
|
|
30
|
+
`
|
|
31
|
+
export function use${methodUpper}${operationIdUpper}() {
|
|
32
|
+
return useUpdateQuery<${paramNames}, ${requestBody}, ${responseBody}>({
|
|
33
|
+
${method}Url: ${urlStructure},${cacheKeyLines}
|
|
34
|
+
});
|
|
35
|
+
}`,
|
|
36
|
+
importStatements,
|
|
37
|
+
];
|
|
38
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import path from "path";
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import { execSync } from "child_process";
|
|
6
|
+
|
|
7
|
+
const args = process.argv.slice(2);
|
|
8
|
+
const schemaOnly = args.includes("--schema-only");
|
|
9
|
+
|
|
10
|
+
const currentDir = process.cwd(); // Get the current working directory
|
|
11
|
+
const kodeDir = path.join(currentDir, ".kode");
|
|
12
|
+
const configFile = path.join(kodeDir, "generate-rad-sdk.json");
|
|
13
|
+
let config: any = {};
|
|
14
|
+
|
|
15
|
+
// Create the .kode directory if it doesn't exist
|
|
16
|
+
if (!fs.existsSync(kodeDir)) {
|
|
17
|
+
fs.mkdirSync(kodeDir);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// If the generate-schema-sdk-config.json file doesn't exist, create it with default values
|
|
21
|
+
if (!fs.existsSync(configFile)) {
|
|
22
|
+
config = {
|
|
23
|
+
schemaUrl: "http://127.0.0.1:8000/api/schema/",
|
|
24
|
+
};
|
|
25
|
+
fs.writeFileSync(configFile, JSON.stringify(config, null, 2));
|
|
26
|
+
} else {
|
|
27
|
+
config = JSON.parse(fs.readFileSync(configFile, "utf-8"));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const projectPath = currentDir;
|
|
31
|
+
const schemaUrl = config.schemaUrl;
|
|
32
|
+
|
|
33
|
+
// Execute the script commands and print the output
|
|
34
|
+
const schemaFetchOutput = execSync(
|
|
35
|
+
`node ${__dirname}/schema-fetch.js ${schemaUrl} ${projectPath}/packages/schema/schema/`,
|
|
36
|
+
).toString();
|
|
37
|
+
console.log(schemaFetchOutput);
|
|
38
|
+
|
|
39
|
+
const schemaConvertOutput = execSync(
|
|
40
|
+
`node ${__dirname}/schema-convert.js ${projectPath}/packages/schema/schema/`,
|
|
41
|
+
).toString();
|
|
42
|
+
console.log(schemaConvertOutput);
|
|
43
|
+
|
|
44
|
+
process.chdir(`${projectPath}/packages/schema/schema/api-schema`);
|
|
45
|
+
const schemaFullOutput = execSync(
|
|
46
|
+
`node ${__dirname}/schema-full.js ${projectPath}/packages/schema/schema/`,
|
|
47
|
+
).toString();
|
|
48
|
+
console.log(schemaFullOutput);
|
|
49
|
+
|
|
50
|
+
process.chdir(__dirname);
|
|
51
|
+
const schemaParamsOutput = execSync(
|
|
52
|
+
`node ${__dirname}/schema-params.js ${schemaUrl} ${projectPath}/packages/schema/schema/`,
|
|
53
|
+
).toString();
|
|
54
|
+
console.log(schemaParamsOutput);
|
|
55
|
+
|
|
56
|
+
const schemaIndexOutput = execSync(
|
|
57
|
+
`node ${__dirname}/schema-index.js ${projectPath}/packages/schema/schema/`,
|
|
58
|
+
).toString();
|
|
59
|
+
console.log(schemaIndexOutput);
|
|
60
|
+
|
|
61
|
+
if (!schemaOnly) {
|
|
62
|
+
// Generate entities if --schema-only flag is not provided
|
|
63
|
+
const schemaEntitiesOutput = execSync(
|
|
64
|
+
`node ${__dirname}/schema-entities.js ${schemaUrl} ${projectPath}/packages/entities/`,
|
|
65
|
+
).toString();
|
|
66
|
+
console.log(schemaEntitiesOutput);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const prettierSchemaOutput = execSync(
|
|
70
|
+
`${projectPath}/node_modules/.bin/prettier --write --config .prettierrc ${projectPath}/packages/schema/schema/`,
|
|
71
|
+
).toString();
|
|
72
|
+
console.log(prettierSchemaOutput);
|
|
73
|
+
|
|
74
|
+
const prettierEntitiesOutput = execSync(
|
|
75
|
+
`${projectPath}/node_modules/.bin/prettier --write --config .prettierrc ${projectPath}/packages/entities/`,
|
|
76
|
+
).toString();
|
|
77
|
+
console.log(prettierEntitiesOutput);
|