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.
Files changed (140) hide show
  1. package/CHANGELOG.md +43013 -0
  2. package/dist/.prettierrc +7 -0
  3. package/dist/compile/analyze.d.ts +12 -0
  4. package/dist/compile/analyze.js +233 -0
  5. package/dist/compile/index.d.ts +19 -0
  6. package/dist/compile/index.js +107 -0
  7. package/dist/compile/manifest.d.ts +4 -0
  8. package/dist/compile/manifest.js +12 -0
  9. package/dist/compile/model.d.ts +54 -0
  10. package/dist/compile/model.js +7 -0
  11. package/dist/compile/openapi.d.ts +11 -0
  12. package/dist/compile/openapi.js +233 -0
  13. package/dist/compile/program.d.ts +10 -0
  14. package/dist/compile/program.js +61 -0
  15. package/dist/csv-schema.d.ts +28 -0
  16. package/dist/csv-schema.js +162 -0
  17. package/dist/define/index.d.ts +117 -0
  18. package/dist/define/index.js +74 -0
  19. package/dist/entity-generation/delete-method.d.ts +1 -0
  20. package/dist/entity-generation/delete-method.js +31 -0
  21. package/dist/entity-generation/form.d.ts +13 -0
  22. package/dist/entity-generation/form.js +136 -0
  23. package/dist/entity-generation/get-method.d.ts +1 -0
  24. package/dist/entity-generation/get-method.js +67 -0
  25. package/dist/entity-generation/imports.d.ts +5 -0
  26. package/dist/entity-generation/imports.js +91 -0
  27. package/dist/entity-generation/sync-method.d.ts +30 -0
  28. package/dist/entity-generation/sync-method.js +158 -0
  29. package/dist/entity-generation/update-method.d.ts +1 -0
  30. package/dist/entity-generation/update-method.js +29 -0
  31. package/dist/index.d.ts +2 -0
  32. package/dist/index.js +53 -0
  33. package/dist/rad-compile.d.ts +11 -0
  34. package/dist/rad-compile.js +82 -0
  35. package/dist/rad-wasm-build.d.ts +14 -0
  36. package/dist/rad-wasm-build.js +74 -0
  37. package/dist/rad.d.ts +17 -0
  38. package/dist/rad.js +88 -0
  39. package/dist/scan.d.ts +12 -0
  40. package/dist/scan.js +79 -0
  41. package/dist/schema/camel-to-pastel.d.ts +1 -0
  42. package/dist/schema/camel-to-pastel.js +7 -0
  43. package/dist/schema/capitalise-first-letter.d.ts +1 -0
  44. package/dist/schema/capitalise-first-letter.js +7 -0
  45. package/dist/schema/extract-response.d.ts +2 -0
  46. package/dist/schema/extract-response.js +30 -0
  47. package/dist/schema/fetch-schema.d.ts +7 -0
  48. package/dist/schema/fetch-schema.js +23 -0
  49. package/dist/schema/remove-non-model-paths.d.ts +2 -0
  50. package/dist/schema/remove-non-model-paths.js +15 -0
  51. package/dist/schema/to-camel-case.d.ts +1 -0
  52. package/dist/schema/to-camel-case.js +11 -0
  53. package/dist/schema/to-pastel-case.d.ts +1 -0
  54. package/dist/schema/to-pastel-case.js +10 -0
  55. package/dist/schema-convert.d.ts +1 -0
  56. package/dist/schema-convert.js +63 -0
  57. package/dist/schema-entities.d.ts +14 -0
  58. package/dist/schema-entities.js +293 -0
  59. package/dist/schema-fetch.d.ts +1 -0
  60. package/dist/schema-fetch.js +81 -0
  61. package/dist/schema-full.d.ts +1 -0
  62. package/dist/schema-full.js +45 -0
  63. package/dist/schema-index.d.ts +1 -0
  64. package/dist/schema-index.js +34 -0
  65. package/dist/schema-params.d.ts +1 -0
  66. package/dist/schema-params.js +100 -0
  67. package/dist/seed.d.ts +17 -0
  68. package/dist/seed.js +252 -0
  69. package/hooks/form/form.ts +207 -0
  70. package/hooks/form/index.ts +8 -0
  71. package/hooks/form/interfaces.ts +217 -0
  72. package/hooks/form/structure.ts +39 -0
  73. package/hooks/form/validate-schema.ts +49 -0
  74. package/hooks/index.ts +3 -0
  75. package/hooks/query/api.ts +42 -0
  76. package/hooks/query/delete.ts +45 -0
  77. package/hooks/query/fetch.ts +48 -0
  78. package/hooks/query/index.ts +21 -0
  79. package/hooks/query/interfaces.ts +111 -0
  80. package/hooks/query/list.ts +286 -0
  81. package/hooks/query/update.ts +88 -0
  82. package/hooks/query/url.ts +31 -0
  83. package/hooks/real-time/index.ts +1 -0
  84. package/hooks/real-time/pusher.ts +43 -0
  85. package/jest.config.cjs +6 -0
  86. package/package.json +57 -0
  87. package/provider/index.ts +13 -0
  88. package/provider/provider.tsx +187 -0
  89. package/provider/types.ts +114 -0
  90. package/provider/warehouse.ts +167 -0
  91. package/provider/wasm.ts +124 -0
  92. package/runtime.ts +3 -0
  93. package/src/.prettierrc +7 -0
  94. package/src/compile/analyze.ts +224 -0
  95. package/src/compile/index.ts +86 -0
  96. package/src/compile/manifest.ts +10 -0
  97. package/src/compile/model.ts +69 -0
  98. package/src/compile/openapi.ts +266 -0
  99. package/src/compile/program.ts +40 -0
  100. package/src/csv-schema.ts +187 -0
  101. package/src/define/index.ts +162 -0
  102. package/src/entity-generation/delete-method.ts +37 -0
  103. package/src/entity-generation/form.ts +169 -0
  104. package/src/entity-generation/get-method.ts +80 -0
  105. package/src/entity-generation/imports.ts +110 -0
  106. package/src/entity-generation/sync-method.ts +222 -0
  107. package/src/entity-generation/update-method.ts +38 -0
  108. package/src/index.ts +77 -0
  109. package/src/rad-compile.ts +88 -0
  110. package/src/rad-wasm-build.ts +102 -0
  111. package/src/rad.ts +101 -0
  112. package/src/scan.ts +89 -0
  113. package/src/schema/camel-to-pastel.ts +3 -0
  114. package/src/schema/capitalise-first-letter.ts +3 -0
  115. package/src/schema/extract-response.ts +38 -0
  116. package/src/schema/fetch-schema.ts +12 -0
  117. package/src/schema/remove-non-model-paths.ts +13 -0
  118. package/src/schema/to-camel-case.ts +7 -0
  119. package/src/schema/to-pastel-case.ts +6 -0
  120. package/src/schema-convert.ts +61 -0
  121. package/src/schema-entities.ts +401 -0
  122. package/src/schema-fetch.ts +82 -0
  123. package/src/schema-full.ts +38 -0
  124. package/src/schema-index.ts +29 -0
  125. package/src/schema-params.ts +116 -0
  126. package/src/seed.ts +297 -0
  127. package/sync/engine.ts +392 -0
  128. package/sync/hooks.ts +450 -0
  129. package/sync/index.ts +8 -0
  130. package/sync/persistence.ts +237 -0
  131. package/sync/provider.tsx +91 -0
  132. package/sync/registry.ts +36 -0
  133. package/sync/store.ts +126 -0
  134. package/sync/transactions.ts +300 -0
  135. package/sync/types.ts +94 -0
  136. package/test/compile.spec.ts +192 -0
  137. package/test/csv-schema.spec.ts +143 -0
  138. package/test/schema-entities.spec.ts +688 -0
  139. package/tsconfig.json +24 -0
  140. package/types.ts +19 -0
package/dist/seed.js ADDED
@@ -0,0 +1,252 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * rad seed
5
+ *
6
+ * Loads CSV files from a local directory or GCS bucket into the target
7
+ * Iceberg namespace. Performs schema inference, PII check, and idempotent
8
+ * table creation before writing rows.
9
+ *
10
+ * Usage:
11
+ * rad seed [--env dev|staging] [--source gs://bucket/ | --source ./myapp-dev/]
12
+ *
13
+ * Environment variables (set via CI secrets or .env):
14
+ * GCP_PROJECT GCP project ID
15
+ * ICEBERG_WAREHOUSE gs://... path to Iceberg warehouse root
16
+ * ICEBERG_CATALOG_URI sqlite:///... or http://... catalog URI
17
+ */
18
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
19
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
20
+ return new (P || (P = Promise))(function (resolve, reject) {
21
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
22
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
23
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
24
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
25
+ });
26
+ };
27
+ var __importDefault = (this && this.__importDefault) || function (mod) {
28
+ return (mod && mod.__esModule) ? mod : { "default": mod };
29
+ };
30
+ var _a, _b;
31
+ Object.defineProperty(exports, "__esModule", { value: true });
32
+ const fs_1 = __importDefault(require("fs"));
33
+ const path_1 = __importDefault(require("path"));
34
+ const child_process_1 = require("child_process");
35
+ const csv_schema_1 = require("./csv-schema");
36
+ // ---------------------------------------------------------------------------
37
+ // Args
38
+ // ---------------------------------------------------------------------------
39
+ const args = process.argv.slice(2);
40
+ function getArg(flag) {
41
+ const i = args.indexOf(flag);
42
+ return i !== -1 && args[i + 1] ? args[i + 1] : null;
43
+ }
44
+ const env = (_a = getArg("--env")) !== null && _a !== void 0 ? _a : "dev";
45
+ const source = (_b = getArg("--source")) !== null && _b !== void 0 ? _b : path_1.default.join(process.cwd(), "myapp-dev");
46
+ const repoRoot = process.cwd();
47
+ // ---------------------------------------------------------------------------
48
+ // Step 1: resolve CSV files
49
+ // ---------------------------------------------------------------------------
50
+ function resolveFiles() {
51
+ if (source.startsWith("gs://")) {
52
+ return downloadFromGcs(source);
53
+ }
54
+ return (0, csv_schema_1.discoverCsvFiles)(source);
55
+ }
56
+ function downloadFromGcs(gcsUri) {
57
+ const tmpDir = path_1.default.join(repoRoot, ".rad", "tmp", "seed");
58
+ fs_1.default.mkdirSync(tmpDir, { recursive: true });
59
+ console.log(`Downloading CSVs from ${gcsUri} ...`);
60
+ try {
61
+ (0, child_process_1.execSync)(`gcloud storage cp "${gcsUri}*.csv" "${tmpDir}/" --quiet`, {
62
+ stdio: "inherit",
63
+ });
64
+ }
65
+ catch (_a) {
66
+ // gcloud storage cp uses gsutil-style globs — fall back
67
+ (0, child_process_1.execSync)(`gsutil -m cp "${gcsUri}*.csv" "${tmpDir}/"`, { stdio: "inherit" });
68
+ }
69
+ return (0, csv_schema_1.discoverCsvFiles)(tmpDir);
70
+ }
71
+ // ---------------------------------------------------------------------------
72
+ // Step 2: validate schemas (PII check — hard fail in CI)
73
+ // ---------------------------------------------------------------------------
74
+ function validateSchema(csvPath) {
75
+ const { schema, piiViolations } = (0, csv_schema_1.inferSchema)(csvPath, repoRoot);
76
+ if (piiViolations.length > 0) {
77
+ const list = piiViolations.map((v) => ` ${v.column}: ${v.reason}`).join("\n");
78
+ throw new Error(`PII detected in ${path_1.default.basename(csvPath)}:\n${list}\n` +
79
+ `Remove PII data before seeding. Run "rad scan" to review.`);
80
+ }
81
+ return schema;
82
+ }
83
+ // ---------------------------------------------------------------------------
84
+ // Step 3: emit Iceberg DDL for a table
85
+ // ---------------------------------------------------------------------------
86
+ function icebergType(col) {
87
+ switch (col) {
88
+ case "integer":
89
+ return "long";
90
+ case "float":
91
+ return "double";
92
+ case "boolean":
93
+ return "boolean";
94
+ case "date":
95
+ return "date";
96
+ case "datetime":
97
+ return "timestamp";
98
+ default:
99
+ return "string";
100
+ }
101
+ }
102
+ function buildCreateTableSql(schema, namespace) {
103
+ const cols = schema.columns
104
+ .map((c) => {
105
+ const type = icebergType(c.type);
106
+ const nullable = c.nullable ? "" : " NOT NULL";
107
+ return ` ${c.name} ${type}${nullable}`;
108
+ })
109
+ .join(",\n");
110
+ return (`CREATE TABLE IF NOT EXISTS ${namespace}.${schema.tableName} (\n` +
111
+ `${cols}\n` +
112
+ `) USING iceberg\n` +
113
+ `TBLPROPERTIES (\n` +
114
+ ` 'write.format.default'='parquet',\n` +
115
+ ` 'write.metadata.fingerprint'='${schema.fingerprint}'\n` +
116
+ `);`);
117
+ }
118
+ // ---------------------------------------------------------------------------
119
+ // Step 4: write schema fingerprints to registry
120
+ // ---------------------------------------------------------------------------
121
+ function writeFingerprint(schema) {
122
+ const registryDir = path_1.default.join(repoRoot, ".rad", "registry");
123
+ fs_1.default.mkdirSync(registryDir, { recursive: true });
124
+ const registryPath = path_1.default.join(registryDir, `${schema.tableName}.json`);
125
+ let existing = {};
126
+ if (fs_1.default.existsSync(registryPath)) {
127
+ existing = JSON.parse(fs_1.default.readFileSync(registryPath, "utf-8"));
128
+ }
129
+ const prev = existing.fingerprint;
130
+ const changed = prev && prev !== schema.fingerprint;
131
+ const record = {
132
+ tableName: schema.tableName,
133
+ fingerprint: schema.fingerprint,
134
+ columns: schema.columns,
135
+ rowCount: schema.rowCount,
136
+ seededAt: new Date().toISOString(),
137
+ env,
138
+ previousFingerprint: prev !== null && prev !== void 0 ? prev : null,
139
+ };
140
+ fs_1.default.writeFileSync(registryPath, JSON.stringify(record, null, 2));
141
+ if (changed) {
142
+ console.warn(` ⚠ Schema change detected for "${schema.tableName}": ` +
143
+ `${prev} → ${schema.fingerprint}. A migration may be required.`);
144
+ }
145
+ }
146
+ // ---------------------------------------------------------------------------
147
+ // Step 5: build a Python seed script and run it via django-iceberg
148
+ // ---------------------------------------------------------------------------
149
+ function buildPythonSeedScript(schemas, csvPaths) {
150
+ var _a, _b;
151
+ const namespace = env;
152
+ const warehouse = (_a = process.env.ICEBERG_WAREHOUSE) !== null && _a !== void 0 ? _a : "data/warehouse";
153
+ const catalogUri = (_b = process.env.ICEBERG_CATALOG_URI) !== null && _b !== void 0 ? _b : "sqlite:///data/catalog.db";
154
+ const tableBlocks = schemas
155
+ .map((schema, i) => {
156
+ const ddl = buildCreateTableSql(schema, namespace).replace(/`/g, "\\`");
157
+ const csvPathEscaped = csvPaths[i].replace(/\\/g, "/");
158
+ return `
159
+ # --- ${schema.tableName} ---
160
+ ddl = """${ddl}"""
161
+ cat.execute(ddl)
162
+ df = pl.read_csv("${csvPathEscaped}", infer_schema_length=1000)
163
+ tbl = cat.load_table("${namespace}.${schema.tableName}")
164
+ tbl.overwrite(df.to_arrow())
165
+ print(f" Seeded ${schema.tableName}: ${schema.rowCount} rows")
166
+ `;
167
+ })
168
+ .join("\n");
169
+ return `
170
+ import polars as pl
171
+ from pyiceberg.catalog import load_catalog
172
+
173
+ cat = load_catalog(
174
+ "default",
175
+ **{
176
+ "uri": "${catalogUri}",
177
+ "warehouse": "${warehouse}",
178
+ }
179
+ )
180
+
181
+ try:
182
+ cat.create_namespace("${namespace}")
183
+ except Exception:
184
+ pass # namespace already exists
185
+
186
+ ${tableBlocks}
187
+
188
+ print("Seed complete.")
189
+ `.trim();
190
+ }
191
+ // ---------------------------------------------------------------------------
192
+ // Main
193
+ // ---------------------------------------------------------------------------
194
+ function main() {
195
+ return __awaiter(this, void 0, void 0, function* () {
196
+ var _a;
197
+ console.log(`\nrad seed env=${env} source=${source}\n`);
198
+ const files = resolveFiles();
199
+ if (files.length === 0) {
200
+ console.log("No CSV files found. Nothing to seed.");
201
+ process.exit(0);
202
+ }
203
+ console.log(`Found ${files.length} CSV file(s):\n`);
204
+ const schemas = [];
205
+ for (const csvPath of files) {
206
+ const rel = path_1.default.relative(repoRoot, csvPath);
207
+ process.stdout.write(` Validating ${rel} ... `);
208
+ try {
209
+ const schema = validateSchema(csvPath);
210
+ schemas.push(schema);
211
+ console.log(`OK [${schema.columns.length} cols, ${schema.rowCount} rows]`);
212
+ }
213
+ catch (err) {
214
+ console.error(`FAIL\n${err.message}`);
215
+ process.exit(1);
216
+ }
217
+ }
218
+ // Write fingerprints before seeding so CI can detect schema drift
219
+ for (const schema of schemas) {
220
+ writeFingerprint(schema);
221
+ }
222
+ // Emit DDL summary
223
+ const ddlDir = path_1.default.join(repoRoot, ".rad", "ddl");
224
+ fs_1.default.mkdirSync(ddlDir, { recursive: true });
225
+ for (const schema of schemas) {
226
+ const ddl = buildCreateTableSql(schema, env);
227
+ fs_1.default.writeFileSync(path_1.default.join(ddlDir, `${schema.tableName}.sql`), ddl + "\n");
228
+ }
229
+ console.log(`\nDDL written to .rad/ddl/`);
230
+ // Run Python seed via pyiceberg if available
231
+ const seedScript = buildPythonSeedScript(schemas, files);
232
+ const scriptPath = path_1.default.join(repoRoot, ".rad", "tmp", "seed.py");
233
+ fs_1.default.mkdirSync(path_1.default.dirname(scriptPath), { recursive: true });
234
+ fs_1.default.writeFileSync(scriptPath, seedScript);
235
+ const pythonBin = (_a = process.env.PYTHON) !== null && _a !== void 0 ? _a : "python";
236
+ try {
237
+ console.log(`\nSeeding Iceberg namespace "${env}" ...\n`);
238
+ (0, child_process_1.execSync)(`${pythonBin} "${scriptPath}"`, { stdio: "inherit" });
239
+ }
240
+ catch (_b) {
241
+ console.error("\nPython seed step failed. Ensure pyiceberg and polars are installed:\n" +
242
+ " pip install pyiceberg polars pyarrow\n\n" +
243
+ `Generated seed script: ${scriptPath}`);
244
+ process.exit(1);
245
+ }
246
+ console.log("\nDone.");
247
+ });
248
+ }
249
+ main().catch((err) => {
250
+ console.error(err);
251
+ process.exit(1);
252
+ });
@@ -0,0 +1,207 @@
1
+ import { useFormik, FormikErrors } from "formik";
2
+ import {
3
+ FormFieldStructure,
4
+ FormProps,
5
+ FormStructure,
6
+ FormUpdate,
7
+ FormValuesType,
8
+ } from "./interfaces";
9
+ import { FlattenedRecord } from "../../types";
10
+ import { formikJSONSchemaValidation } from "./validate-schema";
11
+ import { getFieldStructure } from "./structure";
12
+
13
+ export function useForm<
14
+ TFormValues extends object,
15
+ TPostParams,
16
+ TPutParams,
17
+ TPatchParams,
18
+ TPostPayload,
19
+ TPutPayload,
20
+ TPatchPayload,
21
+ TPostResult,
22
+ TPutResult,
23
+ TPatchResult,
24
+ TFormUpdate extends FormUpdate,
25
+ TSourceId extends string | undefined,
26
+ >({
27
+ id,
28
+ sourceData,
29
+ formValues,
30
+ formatResponse,
31
+ transformRequest,
32
+ transformResponse,
33
+ mutations,
34
+ updateType,
35
+ onSuccess,
36
+ }: FormProps<
37
+ TFormValues,
38
+ TPostParams,
39
+ TPutParams,
40
+ TPatchParams,
41
+ TPostPayload,
42
+ TPutPayload,
43
+ TPatchPayload,
44
+ TPostResult,
45
+ TPutResult,
46
+ TPatchResult,
47
+ TFormUpdate,
48
+ TSourceId
49
+ >): FormStructure<
50
+ TFormValues,
51
+ FormValuesType<
52
+ TFormUpdate,
53
+ TSourceId,
54
+ TPutParams,
55
+ TPutPayload,
56
+ TPostParams,
57
+ TPostPayload,
58
+ TPatchParams,
59
+ TPatchPayload
60
+ >
61
+ > {
62
+ const updateMethod = id ? (updateType === "put" ? "put" : "patch") : "post";
63
+
64
+ const formatRequestValues = (values: TFormValues) => {
65
+ if (formatResponse) {
66
+ return (Object.keys(values) as Array<keyof TFormValues>).reduce(
67
+ (acc, key) => {
68
+ const formattedValue = formatResponse[key](values[key]);
69
+ return {
70
+ ...acc,
71
+ ...formattedValue,
72
+ };
73
+ },
74
+ {} as FormValuesType<
75
+ TFormUpdate,
76
+ TSourceId,
77
+ TPutParams,
78
+ TPutPayload,
79
+ TPostParams,
80
+ TPostPayload,
81
+ TPatchParams,
82
+ TPatchPayload
83
+ >,
84
+ );
85
+ } else if (transformRequest) {
86
+ return transformRequest(values);
87
+ } else {
88
+ return values;
89
+ }
90
+ };
91
+
92
+ const isSourceDataLoading = sourceData
93
+ ? Object.values(sourceData).some((source) =>
94
+ // @ts-ignore create a base type for list and fetch data extending react query. This will ensure that the isLoading property is available
95
+ source ? source.isLoading : false,
96
+ )
97
+ : false;
98
+
99
+ const formik = useFormik<TFormValues>({
100
+ initialValues: formValues,
101
+ validate: (values) => {
102
+ if (mutations) {
103
+ const payload = formatRequestValues(values);
104
+ return formikJSONSchemaValidation(
105
+ payload,
106
+ mutations[updateMethod]?.validation,
107
+ );
108
+ } else {
109
+ return {} as FormikErrors<TFormValues>;
110
+ }
111
+ },
112
+ onSubmit: (values, formikHelpers) => {
113
+ const mutation = mutations ? mutations[updateMethod].mutation : undefined;
114
+ const formattedResponse = formatRequestValues(values);
115
+ if (mutation) {
116
+ mutation.mutate(
117
+ {
118
+ //@ts-ignore
119
+ params: {
120
+ id,
121
+ ...formattedResponse,
122
+ },
123
+ //@ts-ignore
124
+ payload: formattedResponse,
125
+ },
126
+ {
127
+ onSuccess: (result) => {
128
+ if (onSuccess) {
129
+ // @ts-ignore
130
+ onSuccess(result);
131
+ }
132
+ formikHelpers.setSubmitting(false);
133
+ },
134
+ onError: () => {
135
+ formikHelpers.setSubmitting(false);
136
+ },
137
+ },
138
+ );
139
+ }
140
+ },
141
+ enableReinitialize: true,
142
+ });
143
+
144
+ const isSubmitDisabled =
145
+ formik.isValid === undefined ? false : !formik.isValid;
146
+ const isSubmitting = formik.isSubmitting;
147
+ const submitForm = () => {
148
+ formik.handleSubmit();
149
+ };
150
+
151
+ const getFormField = <
152
+ TFieldName extends Extract<keyof FlattenedRecord<TFormValues>, string>,
153
+ >(
154
+ fieldName: TFieldName,
155
+ ): FormFieldStructure<FlattenedRecord<TFormValues>[TFieldName]> => {
156
+ const { value: value, checked: isCheckedValue } =
157
+ formik.getFieldProps(fieldName);
158
+ const { touched: isTouched, error: errorMessage } =
159
+ formik.getFieldMeta(fieldName);
160
+ const { setValue: setFormValue, setTouched } =
161
+ formik.getFieldHelpers(fieldName);
162
+
163
+ let { type, isRequired } = getFieldStructure(
164
+ mutations ? mutations[updateMethod]?.validation : undefined,
165
+ fieldName,
166
+ );
167
+
168
+ const error = errorMessage ?? "";
169
+ const isError = errorMessage !== undefined;
170
+ const isDisabled = false;
171
+ const isChecked = isCheckedValue ?? false;
172
+ const setValue = (value: FlattenedRecord<TFormValues>[TFieldName]) => {
173
+ if (type === "number" && value) {
174
+ const numericValue = Number(value);
175
+ if (!isNaN(numericValue)) {
176
+ setFormValue(numericValue);
177
+ }
178
+ } else {
179
+ setFormValue(value);
180
+ }
181
+ };
182
+ return {
183
+ type,
184
+ value,
185
+ error,
186
+ isChecked,
187
+ isError,
188
+ isDisabled,
189
+ isRequired,
190
+ setValue,
191
+ setTouched,
192
+ };
193
+ };
194
+
195
+ return {
196
+ formValues: formik.values,
197
+ // @ts-ignore
198
+ responseValues: formatRequestValues(formik.values),
199
+ errors: formik.errors,
200
+ getFormField,
201
+ onSubmit: submitForm,
202
+ resetForm: formik.resetForm,
203
+ isSubmitDisabled,
204
+ isSourceDataLoading,
205
+ isSubmitting,
206
+ };
207
+ }
@@ -0,0 +1,8 @@
1
+ export { useForm } from "./form";
2
+ export type {
3
+ FormStructure,
4
+ FormUpdateMethod,
5
+ SetupUpdateEndpointReturn,
6
+ FormProps,
7
+ FormUpdate,
8
+ } from "./interfaces";
@@ -0,0 +1,217 @@
1
+ import { FlattenedRecord } from "../../types";
2
+ import { SchemaObject } from "ajv";
3
+ import { IUpdateQueryResult } from "../../hooks";
4
+ import { FormikValues } from "formik";
5
+
6
+ export interface FormFieldStructure<TValue> {
7
+ value: TValue;
8
+ error: string;
9
+ type: FormType;
10
+ isChecked: boolean;
11
+ isError: boolean;
12
+ isDisabled: boolean;
13
+ isRequired: boolean;
14
+ setValue: (value: TValue) => void;
15
+ setTouched: (isTouched: boolean) => void;
16
+ }
17
+
18
+ interface FormMutation<
19
+ TPostParams,
20
+ TPutParams,
21
+ TPatchParams,
22
+ TPostPayload,
23
+ TPutPayload,
24
+ TPatchPayload,
25
+ TPostResult,
26
+ TPutResult,
27
+ TPatchResult,
28
+ > {
29
+ post: {
30
+ mutation?: IUpdateQueryResult<TPostParams, TPostPayload, TPostResult>;
31
+ validation?: SchemaObject | undefined;
32
+ };
33
+ put: {
34
+ mutation?: IUpdateQueryResult<TPutParams, TPutPayload, TPutResult>;
35
+ validation?: SchemaObject | undefined;
36
+ };
37
+ patch: {
38
+ mutation?: IUpdateQueryResult<TPatchParams, TPatchPayload, TPatchResult>;
39
+ validation?: SchemaObject | undefined;
40
+ };
41
+ }
42
+
43
+ export type FormUpdate = "post_or_patch" | "put";
44
+
45
+ export interface FormProps<
46
+ TFormValues extends object,
47
+ TPostParams,
48
+ TPutParams,
49
+ TPatchParams,
50
+ TPostPayload,
51
+ TPutPayload,
52
+ TPatchPayload,
53
+ TPostResult,
54
+ TPutResult,
55
+ TPatchResult,
56
+ TFormUpdate extends FormUpdate,
57
+ TSourceId extends string | undefined,
58
+ > {
59
+ id?: TSourceId & string;
60
+ sourceData?: SourceData;
61
+ formValues: TFormValues & object;
62
+ formatResponse?: ForwardMapping<
63
+ TFormValues,
64
+ TFormUpdate extends "put"
65
+ ? TPutParams & TPutResult
66
+ : TSourceId extends undefined
67
+ ? TPostParams & TPostResult
68
+ : TPatchParams & TPatchResult
69
+ >;
70
+ transformRequest?: ForwardMap<
71
+ TFormValues,
72
+ TFormUpdate extends "put"
73
+ ? TPutParams & TPutResult
74
+ : TSourceId extends undefined
75
+ ? TPostParams & TPostResult
76
+ : TPatchParams & TPatchResult
77
+ >;
78
+ transformResponse?: ReverseMap<
79
+ TFormValues,
80
+ TFormUpdate extends "put"
81
+ ? TPutParams & TPutResult
82
+ : TSourceId extends undefined
83
+ ? TPostParams & TPostResult
84
+ : TPatchParams & TPatchResult
85
+ >;
86
+ updateType?: TFormUpdate;
87
+ mutations?: FormMutation<
88
+ TPostParams,
89
+ TPutParams,
90
+ TPatchParams,
91
+ TPostPayload,
92
+ TPutPayload,
93
+ TPatchPayload,
94
+ TPostResult,
95
+ TPutResult,
96
+ TPatchResult
97
+ >;
98
+ onSuccess?: (
99
+ values: TFormUpdate extends "put"
100
+ ? TPutResult
101
+ : TSourceId extends undefined
102
+ ? TPostResult
103
+ : TPatchResult,
104
+ ) => void;
105
+ }
106
+
107
+ export interface FormStructure<TFormValues, TResponse> {
108
+ formValues: TFormValues | undefined;
109
+ responseValues: TResponse | undefined;
110
+ errors: any;
111
+ onSubmit: () => void;
112
+ resetForm: () => void;
113
+ isSubmitDisabled: boolean;
114
+ isSourceDataLoading: boolean;
115
+ isSubmitting: boolean;
116
+ getFormField: <TFieldName extends keyof FlattenedRecord<TFormValues>>(
117
+ fieldName: TFieldName,
118
+ ) => FormFieldStructure<FlattenedRecord<TFormValues>[TFieldName]>;
119
+ }
120
+
121
+ export type FormType = "number" | "text" | "array";
122
+
123
+ export interface SetupUpdateEndpointReturn<TParams, TPayload, TResult> {
124
+ mutation: IUpdateQueryResult<TParams, TPayload, TResult>;
125
+ validation: SchemaObject;
126
+ }
127
+
128
+ export type FormUpdateMethod = "post_or_patch" | "put";
129
+
130
+ export interface SourceData {
131
+ [key: string]: unknown;
132
+ }
133
+
134
+ export type ForwardMapping<TFormValues, TResponse> = {
135
+ [key in keyof TFormValues]: (value: TFormValues[key]) => {
136
+ [key in keyof TResponse]: TResponse[keyof TResponse];
137
+ };
138
+ };
139
+
140
+ export type ReverseMapping<TFormValues, TResponse> = {
141
+ [key in keyof TResponse]: (value: TResponse[key]) => {
142
+ [key in keyof TFormValues]: TFormValues[keyof TFormValues];
143
+ };
144
+ };
145
+
146
+ export type ForwardMap<TFormValues, TResponse> = (value: TFormValues) => {
147
+ [key in keyof TResponse]: TResponse[keyof TResponse];
148
+ };
149
+
150
+ export type ReverseMap<TFormValues, TResponse> = (value: TResponse) => {
151
+ [key in keyof TFormValues]: TFormValues[keyof TFormValues];
152
+ };
153
+
154
+ type EnsureObject<T> = T extends object ? T : Record<string, never>;
155
+
156
+ export type FormValuesType<
157
+ TFormUpdate extends "put" | "post_or_patch",
158
+ TSourceId extends string | undefined,
159
+ TPutParams,
160
+ TPutPayload,
161
+ TPostParams,
162
+ TPostPayload,
163
+ TPatchParams,
164
+ TPatchPayload,
165
+ > = FormikValues &
166
+ EnsureObject<
167
+ TFormUpdate extends "put"
168
+ ? TPutParams & TPutPayload
169
+ : TSourceId extends undefined
170
+ ? TPostParams & TPostPayload
171
+ : TPatchParams & TPatchPayload
172
+ >;
173
+
174
+ export type FormParamsType<
175
+ TFormUpdate extends "put" | "post_or_patch",
176
+ TSourceId extends string | undefined,
177
+ TPutParams,
178
+ TPostParams,
179
+ TPatchParams,
180
+ > = FormikValues &
181
+ EnsureObject<
182
+ TFormUpdate extends "put"
183
+ ? TPutParams
184
+ : TSourceId extends undefined
185
+ ? TPostParams
186
+ : TPatchParams
187
+ >;
188
+
189
+ export type FormPayloadType<
190
+ TFormUpdate extends "put" | "post_or_patch",
191
+ TSourceId extends string | undefined,
192
+ TPutPayload,
193
+ TPostPayload,
194
+ TPatchPayload,
195
+ > = FormikValues &
196
+ EnsureObject<
197
+ TFormUpdate extends "put"
198
+ ? TPutPayload
199
+ : TSourceId extends undefined
200
+ ? TPostPayload
201
+ : TPatchPayload
202
+ >;
203
+
204
+ export type FormResultType<
205
+ TFormUpdate extends "put" | "post_or_patch",
206
+ TSourceId extends string | undefined,
207
+ TPutResult,
208
+ TPostResult,
209
+ TPatchResult,
210
+ > = FormikValues &
211
+ EnsureObject<
212
+ TFormUpdate extends "put"
213
+ ? TPutResult
214
+ : TSourceId extends undefined
215
+ ? TPostResult
216
+ : TPatchResult
217
+ >;