rastack 0.0.16 → 0.0.20
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 +43876 -43013
- package/dist/.prettierrc +7 -7
- package/dist/compile/analyze.js +19 -11
- package/dist/compile/index.js +26 -17
- package/dist/compile/manifest.js +2 -3
- package/dist/compile/openapi.js +5 -8
- package/dist/compile/program.js +19 -10
- package/dist/csv-schema.js +5 -6
- package/dist/define/index.js +2 -2
- package/dist/entity-generation/delete-method.js +8 -10
- package/dist/entity-generation/form.js +80 -82
- package/dist/entity-generation/get-method.js +35 -36
- package/dist/entity-generation/imports.js +24 -20
- package/dist/entity-generation/sync-method.d.ts +1 -1
- package/dist/entity-generation/sync-method.js +45 -46
- package/dist/entity-generation/update-method.js +9 -10
- package/dist/rad-wasm-build.d.ts +1 -1
- package/dist/rad-wasm-build.js +15 -6
- package/dist/rad.js +9 -2
- package/dist/schema/camel-to-pastel.js +1 -2
- package/dist/schema/capitalise-first-letter.js +1 -2
- package/dist/schema/extract-response.js +3 -4
- package/dist/schema/fetch-schema.js +4 -16
- package/dist/schema/remove-non-model-paths.js +1 -2
- package/dist/schema/to-camel-case.js +1 -2
- package/dist/schema/to-pastel-case.js +1 -2
- package/dist/schema-convert.js +22 -33
- package/dist/schema-entities.js +177 -189
- package/dist/schema-fetch.js +46 -63
- package/dist/schema-full.js +23 -36
- package/dist/schema-index.js +16 -27
- package/dist/schema-params.js +11 -22
- package/dist/seed.js +95 -97
- package/hooks/form/form.ts +207 -207
- package/hooks/form/index.ts +8 -8
- package/hooks/form/interfaces.ts +217 -217
- package/hooks/form/structure.ts +39 -39
- package/hooks/form/validate-schema.ts +49 -49
- package/hooks/index.ts +3 -3
- package/hooks/query/api.ts +42 -42
- package/hooks/query/delete.ts +45 -45
- package/hooks/query/fetch.ts +48 -48
- package/hooks/query/index.ts +21 -21
- package/hooks/query/interfaces.ts +111 -111
- package/hooks/query/list.ts +286 -286
- package/hooks/query/update.ts +88 -88
- package/hooks/query/url.ts +31 -31
- package/hooks/real-time/index.ts +1 -1
- package/hooks/real-time/pusher.ts +43 -43
- package/jest.config.cjs +6 -6
- package/package.json +57 -57
- package/provider/index.ts +13 -13
- package/provider/provider.tsx +187 -187
- package/provider/types.ts +114 -114
- package/provider/warehouse.ts +167 -167
- package/provider/wasm.ts +124 -124
- package/runtime.ts +3 -3
- package/src/.prettierrc +7 -7
- package/src/compile/analyze.ts +224 -224
- package/src/compile/index.ts +86 -86
- package/src/compile/manifest.ts +10 -10
- package/src/compile/model.ts +69 -69
- package/src/compile/openapi.ts +266 -266
- package/src/compile/program.ts +40 -40
- package/src/csv-schema.ts +187 -187
- package/src/define/index.ts +162 -162
- package/src/entity-generation/delete-method.ts +37 -37
- package/src/entity-generation/form.ts +169 -169
- package/src/entity-generation/get-method.ts +80 -80
- package/src/entity-generation/imports.ts +110 -110
- package/src/entity-generation/sync-method.ts +222 -222
- package/src/entity-generation/update-method.ts +38 -38
- package/src/index.ts +77 -77
- package/src/rad-compile.ts +88 -88
- package/src/rad-wasm-build.ts +102 -102
- package/src/rad.ts +101 -101
- package/src/scan.ts +89 -89
- package/src/schema/camel-to-pastel.ts +3 -3
- package/src/schema/capitalise-first-letter.ts +3 -3
- package/src/schema/extract-response.ts +38 -38
- package/src/schema/fetch-schema.ts +12 -12
- package/src/schema/remove-non-model-paths.ts +13 -13
- package/src/schema/to-camel-case.ts +7 -7
- package/src/schema/to-pastel-case.ts +6 -6
- package/src/schema-convert.ts +61 -61
- package/src/schema-entities.ts +401 -401
- package/src/schema-fetch.ts +82 -82
- package/src/schema-full.ts +38 -38
- package/src/schema-index.ts +29 -29
- package/src/schema-params.ts +116 -116
- package/src/seed.ts +297 -297
- package/sync/engine.ts +392 -392
- package/sync/hooks.ts +450 -450
- package/sync/index.ts +8 -8
- package/sync/persistence.ts +237 -237
- package/sync/provider.tsx +91 -91
- package/sync/registry.ts +36 -36
- package/sync/store.ts +126 -126
- package/sync/transactions.ts +300 -300
- package/sync/types.ts +94 -94
- package/test/compile.spec.ts +192 -192
- package/test/csv-schema.spec.ts +143 -143
- package/test/schema-entities.spec.ts +688 -688
- package/tsconfig.json +24 -24
- package/types.ts +18 -18
package/src/rad.ts
CHANGED
|
@@ -1,101 +1,101 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* rad — unified CLI for React API Stack
|
|
5
|
-
*
|
|
6
|
-
* Commands:
|
|
7
|
-
* rad compile [resourcesDir] [outDir] TypeScript resources → schema.rad.json + openapi.json
|
|
8
|
-
* rad check [resourcesDir] Validate the resource graph
|
|
9
|
-
* rad list [resourcesDir] Print resources, fields and relations
|
|
10
|
-
* rad urls [resourcesDir] Print the generated /api/{app}/v1/{model}/ routes
|
|
11
|
-
* rad generate [--schema-only] Generate typed hooks from OpenAPI
|
|
12
|
-
* rad serve [--warehouse dir] Run the Rust API over an Iceberg warehouse
|
|
13
|
-
* rad admin [--warehouse dir] Run the Rust admin (Django-admin-style DB browser)
|
|
14
|
-
* rad wasm Compile the API to WebAssembly (in-browser local dev)
|
|
15
|
-
* rad scan [files...] [--fail-on-pii] Scan CSVs for PII and schema info
|
|
16
|
-
* rad seed [--env dev] [--source gs://] Load CSVs into Iceberg namespace
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import { execSync, spawnSync } from "child_process";
|
|
20
|
-
import path from "path";
|
|
21
|
-
|
|
22
|
-
const [command, ...rest] = process.argv.slice(2);
|
|
23
|
-
|
|
24
|
-
function run(script: string, args: string[] = rest) {
|
|
25
|
-
// `rad` is a local developer CLI: `script` is a fixed internal dist filename
|
|
26
|
-
// and `args` come from the developer's own shell (process.argv), not from an
|
|
27
|
-
// untrusted network request — there is no command-injection surface here.
|
|
28
|
-
// nosemgrep: javascript.lang.security.detect-child-process.detect-child-process
|
|
29
|
-
execSync(`node ${path.join(__dirname, script)} ${args.join(" ")}`, {
|
|
30
|
-
stdio: "inherit",
|
|
31
|
-
env: process.env,
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/** Invoke the compiled Rust server binary (built by `cargo build` in rust/). */
|
|
36
|
-
function runRust(subcommand: string) {
|
|
37
|
-
const repoRoot = path.resolve(__dirname, "..", "..");
|
|
38
|
-
const binary =
|
|
39
|
-
process.env.RAD_SERVER_BIN ||
|
|
40
|
-
path.join(repoRoot, "rust", "target", "release", "rad-server");
|
|
41
|
-
// Runs the locally-built Rust server binary with a fixed internal subcommand
|
|
42
|
-
// ("serve"/"admin"); `rest` is the developer's own CLI args, not untrusted
|
|
43
|
-
// input — no command-injection surface.
|
|
44
|
-
// nosemgrep: javascript.lang.security.detect-child-process.detect-child-process
|
|
45
|
-
const result = spawnSync(binary, [subcommand, ...rest], {
|
|
46
|
-
stdio: "inherit",
|
|
47
|
-
env: process.env,
|
|
48
|
-
});
|
|
49
|
-
if (result.error) {
|
|
50
|
-
console.error(
|
|
51
|
-
`Could not run the Rust server (${binary}). Build it first:\n` +
|
|
52
|
-
` cd rust && cargo build --release\n`,
|
|
53
|
-
);
|
|
54
|
-
process.exit(1);
|
|
55
|
-
}
|
|
56
|
-
process.exit(result.status ?? 0);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
switch (command) {
|
|
60
|
-
case "compile":
|
|
61
|
-
case "check":
|
|
62
|
-
case "list":
|
|
63
|
-
case "urls":
|
|
64
|
-
// rad-compile dispatches on its own first argument, so pass the command.
|
|
65
|
-
run("rad-compile.js", [command, ...rest]);
|
|
66
|
-
break;
|
|
67
|
-
case "serve":
|
|
68
|
-
runRust("serve");
|
|
69
|
-
break;
|
|
70
|
-
case "admin":
|
|
71
|
-
runRust("admin");
|
|
72
|
-
break;
|
|
73
|
-
case "wasm":
|
|
74
|
-
// Compile the API to WebAssembly for in-browser local dev.
|
|
75
|
-
run("rad-wasm-build.js", rest);
|
|
76
|
-
break;
|
|
77
|
-
case "scan":
|
|
78
|
-
run("scan.js");
|
|
79
|
-
break;
|
|
80
|
-
case "seed":
|
|
81
|
-
run("seed.js");
|
|
82
|
-
break;
|
|
83
|
-
case "generate":
|
|
84
|
-
case "gen":
|
|
85
|
-
// Proxy to existing rastack codegen pipeline
|
|
86
|
-
run("index.js");
|
|
87
|
-
break;
|
|
88
|
-
default:
|
|
89
|
-
console.error(
|
|
90
|
-
`Unknown command: "${command}"\n\n` +
|
|
91
|
-
`Usage:\n` +
|
|
92
|
-
` rad compile [resourcesDir] [outDir]\n` +
|
|
93
|
-
` rad check | list | urls [resourcesDir]\n` +
|
|
94
|
-
` rad generate [--schema-only]\n` +
|
|
95
|
-
` rad serve | admin [--warehouse dir]\n` +
|
|
96
|
-
` rad wasm Compile the API to WebAssembly (browser local dev)\n` +
|
|
97
|
-
` rad scan [files...] [--fail-on-pii]\n` +
|
|
98
|
-
` rad seed [--env dev|staging] [--source gs://bucket/ | ./myapp-dev/]\n`,
|
|
99
|
-
);
|
|
100
|
-
process.exit(1);
|
|
101
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* rad — unified CLI for React API Stack
|
|
5
|
+
*
|
|
6
|
+
* Commands:
|
|
7
|
+
* rad compile [resourcesDir] [outDir] TypeScript resources → schema.rad.json + openapi.json
|
|
8
|
+
* rad check [resourcesDir] Validate the resource graph
|
|
9
|
+
* rad list [resourcesDir] Print resources, fields and relations
|
|
10
|
+
* rad urls [resourcesDir] Print the generated /api/{app}/v1/{model}/ routes
|
|
11
|
+
* rad generate [--schema-only] Generate typed hooks from OpenAPI
|
|
12
|
+
* rad serve [--warehouse dir] Run the Rust API over an Iceberg warehouse
|
|
13
|
+
* rad admin [--warehouse dir] Run the Rust admin (Django-admin-style DB browser)
|
|
14
|
+
* rad wasm Compile the API to WebAssembly (in-browser local dev)
|
|
15
|
+
* rad scan [files...] [--fail-on-pii] Scan CSVs for PII and schema info
|
|
16
|
+
* rad seed [--env dev] [--source gs://] Load CSVs into Iceberg namespace
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { execSync, spawnSync } from "child_process";
|
|
20
|
+
import path from "path";
|
|
21
|
+
|
|
22
|
+
const [command, ...rest] = process.argv.slice(2);
|
|
23
|
+
|
|
24
|
+
function run(script: string, args: string[] = rest) {
|
|
25
|
+
// `rad` is a local developer CLI: `script` is a fixed internal dist filename
|
|
26
|
+
// and `args` come from the developer's own shell (process.argv), not from an
|
|
27
|
+
// untrusted network request — there is no command-injection surface here.
|
|
28
|
+
// nosemgrep: javascript.lang.security.detect-child-process.detect-child-process
|
|
29
|
+
execSync(`node ${path.join(__dirname, script)} ${args.join(" ")}`, {
|
|
30
|
+
stdio: "inherit",
|
|
31
|
+
env: process.env,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Invoke the compiled Rust server binary (built by `cargo build` in rust/). */
|
|
36
|
+
function runRust(subcommand: string) {
|
|
37
|
+
const repoRoot = path.resolve(__dirname, "..", "..");
|
|
38
|
+
const binary =
|
|
39
|
+
process.env.RAD_SERVER_BIN ||
|
|
40
|
+
path.join(repoRoot, "rust", "target", "release", "rad-server");
|
|
41
|
+
// Runs the locally-built Rust server binary with a fixed internal subcommand
|
|
42
|
+
// ("serve"/"admin"); `rest` is the developer's own CLI args, not untrusted
|
|
43
|
+
// input — no command-injection surface.
|
|
44
|
+
// nosemgrep: javascript.lang.security.detect-child-process.detect-child-process
|
|
45
|
+
const result = spawnSync(binary, [subcommand, ...rest], {
|
|
46
|
+
stdio: "inherit",
|
|
47
|
+
env: process.env,
|
|
48
|
+
});
|
|
49
|
+
if (result.error) {
|
|
50
|
+
console.error(
|
|
51
|
+
`Could not run the Rust server (${binary}). Build it first:\n` +
|
|
52
|
+
` cd rust && cargo build --release\n`,
|
|
53
|
+
);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
process.exit(result.status ?? 0);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
switch (command) {
|
|
60
|
+
case "compile":
|
|
61
|
+
case "check":
|
|
62
|
+
case "list":
|
|
63
|
+
case "urls":
|
|
64
|
+
// rad-compile dispatches on its own first argument, so pass the command.
|
|
65
|
+
run("rad-compile.js", [command, ...rest]);
|
|
66
|
+
break;
|
|
67
|
+
case "serve":
|
|
68
|
+
runRust("serve");
|
|
69
|
+
break;
|
|
70
|
+
case "admin":
|
|
71
|
+
runRust("admin");
|
|
72
|
+
break;
|
|
73
|
+
case "wasm":
|
|
74
|
+
// Compile the API to WebAssembly for in-browser local dev.
|
|
75
|
+
run("rad-wasm-build.js", rest);
|
|
76
|
+
break;
|
|
77
|
+
case "scan":
|
|
78
|
+
run("scan.js");
|
|
79
|
+
break;
|
|
80
|
+
case "seed":
|
|
81
|
+
run("seed.js");
|
|
82
|
+
break;
|
|
83
|
+
case "generate":
|
|
84
|
+
case "gen":
|
|
85
|
+
// Proxy to existing rastack codegen pipeline
|
|
86
|
+
run("index.js");
|
|
87
|
+
break;
|
|
88
|
+
default:
|
|
89
|
+
console.error(
|
|
90
|
+
`Unknown command: "${command}"\n\n` +
|
|
91
|
+
`Usage:\n` +
|
|
92
|
+
` rad compile [resourcesDir] [outDir]\n` +
|
|
93
|
+
` rad check | list | urls [resourcesDir]\n` +
|
|
94
|
+
` rad generate [--schema-only]\n` +
|
|
95
|
+
` rad serve | admin [--warehouse dir]\n` +
|
|
96
|
+
` rad wasm Compile the API to WebAssembly (browser local dev)\n` +
|
|
97
|
+
` rad scan [files...] [--fail-on-pii]\n` +
|
|
98
|
+
` rad seed [--env dev|staging] [--source gs://bucket/ | ./myapp-dev/]\n`,
|
|
99
|
+
);
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
package/src/scan.ts
CHANGED
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* rad scan
|
|
5
|
-
*
|
|
6
|
-
* Scans CSV files in myapp-dev/ for PII column patterns and schema issues.
|
|
7
|
-
* Exits non-zero if violations are found and --fail-on-pii is passed.
|
|
8
|
-
*
|
|
9
|
-
* Usage:
|
|
10
|
-
* rad scan [files...] [--fail-on-pii]
|
|
11
|
-
* rad scan myapp-dev/passengers.csv myapp-dev/flights.csv --fail-on-pii
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import path from "path";
|
|
15
|
-
import { discoverCsvFiles, inferSchema } from "./csv-schema";
|
|
16
|
-
|
|
17
|
-
const args = process.argv.slice(2);
|
|
18
|
-
const failOnPii = args.includes("--fail-on-pii");
|
|
19
|
-
const fileArgs = args.filter((a) => !a.startsWith("--") && a.endsWith(".csv"));
|
|
20
|
-
|
|
21
|
-
const repoRoot = process.cwd();
|
|
22
|
-
const devDir = path.join(repoRoot, "myapp-dev");
|
|
23
|
-
|
|
24
|
-
const files = fileArgs.length > 0 ? fileArgs : discoverCsvFiles(devDir);
|
|
25
|
-
|
|
26
|
-
if (files.length === 0) {
|
|
27
|
-
console.log("No CSV files found to scan.");
|
|
28
|
-
process.exit(0);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
let totalViolations = 0;
|
|
32
|
-
let hasErrors = false;
|
|
33
|
-
|
|
34
|
-
for (const csvPath of files) {
|
|
35
|
-
const rel = path.relative(repoRoot, csvPath);
|
|
36
|
-
|
|
37
|
-
let result;
|
|
38
|
-
try {
|
|
39
|
-
result = inferSchema(csvPath, repoRoot);
|
|
40
|
-
} catch (err) {
|
|
41
|
-
console.error(`ERROR ${rel}: ${(err as Error).message}`);
|
|
42
|
-
hasErrors = true;
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const { schema, piiViolations } = result;
|
|
47
|
-
|
|
48
|
-
const colSummary = schema.columns
|
|
49
|
-
.map((c) => {
|
|
50
|
-
const tags: string[] = [c.type];
|
|
51
|
-
if (c.primaryKey) tags.push("PK");
|
|
52
|
-
if (c.nullable) tags.push("nullable");
|
|
53
|
-
if (c.pii) tags.push("PII");
|
|
54
|
-
return ` ${c.name.padEnd(24)} ${tags.join(", ")}`;
|
|
55
|
-
})
|
|
56
|
-
.join("\n");
|
|
57
|
-
|
|
58
|
-
console.log(`\n${rel} [${schema.rowCount} rows, fingerprint: ${schema.fingerprint}]`);
|
|
59
|
-
console.log(colSummary);
|
|
60
|
-
|
|
61
|
-
if (piiViolations.length > 0) {
|
|
62
|
-
totalViolations += piiViolations.length;
|
|
63
|
-
console.log(`\n ⚠ PII violations (${piiViolations.length}):`);
|
|
64
|
-
for (const v of piiViolations) {
|
|
65
|
-
console.log(` ${v.column}: ${v.reason}`);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
console.log("");
|
|
71
|
-
|
|
72
|
-
if (hasErrors) {
|
|
73
|
-
console.error("Scan completed with errors.");
|
|
74
|
-
process.exit(1);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (totalViolations > 0) {
|
|
78
|
-
console.warn(
|
|
79
|
-
`Scan found ${totalViolations} PII violation(s) across ${files.length} file(s).`,
|
|
80
|
-
);
|
|
81
|
-
if (failOnPii) {
|
|
82
|
-
console.error(
|
|
83
|
-
"Failing because --fail-on-pii is set. Remove PII data or add access policies in .rad/policies/.",
|
|
84
|
-
);
|
|
85
|
-
process.exit(1);
|
|
86
|
-
}
|
|
87
|
-
} else {
|
|
88
|
-
console.log(`Scan passed. ${files.length} file(s) checked, no PII violations.`);
|
|
89
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* rad scan
|
|
5
|
+
*
|
|
6
|
+
* Scans CSV files in myapp-dev/ for PII column patterns and schema issues.
|
|
7
|
+
* Exits non-zero if violations are found and --fail-on-pii is passed.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* rad scan [files...] [--fail-on-pii]
|
|
11
|
+
* rad scan myapp-dev/passengers.csv myapp-dev/flights.csv --fail-on-pii
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import path from "path";
|
|
15
|
+
import { discoverCsvFiles, inferSchema } from "./csv-schema";
|
|
16
|
+
|
|
17
|
+
const args = process.argv.slice(2);
|
|
18
|
+
const failOnPii = args.includes("--fail-on-pii");
|
|
19
|
+
const fileArgs = args.filter((a) => !a.startsWith("--") && a.endsWith(".csv"));
|
|
20
|
+
|
|
21
|
+
const repoRoot = process.cwd();
|
|
22
|
+
const devDir = path.join(repoRoot, "myapp-dev");
|
|
23
|
+
|
|
24
|
+
const files = fileArgs.length > 0 ? fileArgs : discoverCsvFiles(devDir);
|
|
25
|
+
|
|
26
|
+
if (files.length === 0) {
|
|
27
|
+
console.log("No CSV files found to scan.");
|
|
28
|
+
process.exit(0);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let totalViolations = 0;
|
|
32
|
+
let hasErrors = false;
|
|
33
|
+
|
|
34
|
+
for (const csvPath of files) {
|
|
35
|
+
const rel = path.relative(repoRoot, csvPath);
|
|
36
|
+
|
|
37
|
+
let result;
|
|
38
|
+
try {
|
|
39
|
+
result = inferSchema(csvPath, repoRoot);
|
|
40
|
+
} catch (err) {
|
|
41
|
+
console.error(`ERROR ${rel}: ${(err as Error).message}`);
|
|
42
|
+
hasErrors = true;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const { schema, piiViolations } = result;
|
|
47
|
+
|
|
48
|
+
const colSummary = schema.columns
|
|
49
|
+
.map((c) => {
|
|
50
|
+
const tags: string[] = [c.type];
|
|
51
|
+
if (c.primaryKey) tags.push("PK");
|
|
52
|
+
if (c.nullable) tags.push("nullable");
|
|
53
|
+
if (c.pii) tags.push("PII");
|
|
54
|
+
return ` ${c.name.padEnd(24)} ${tags.join(", ")}`;
|
|
55
|
+
})
|
|
56
|
+
.join("\n");
|
|
57
|
+
|
|
58
|
+
console.log(`\n${rel} [${schema.rowCount} rows, fingerprint: ${schema.fingerprint}]`);
|
|
59
|
+
console.log(colSummary);
|
|
60
|
+
|
|
61
|
+
if (piiViolations.length > 0) {
|
|
62
|
+
totalViolations += piiViolations.length;
|
|
63
|
+
console.log(`\n ⚠ PII violations (${piiViolations.length}):`);
|
|
64
|
+
for (const v of piiViolations) {
|
|
65
|
+
console.log(` ${v.column}: ${v.reason}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
console.log("");
|
|
71
|
+
|
|
72
|
+
if (hasErrors) {
|
|
73
|
+
console.error("Scan completed with errors.");
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (totalViolations > 0) {
|
|
78
|
+
console.warn(
|
|
79
|
+
`Scan found ${totalViolations} PII violation(s) across ${files.length} file(s).`,
|
|
80
|
+
);
|
|
81
|
+
if (failOnPii) {
|
|
82
|
+
console.error(
|
|
83
|
+
"Failing because --fail-on-pii is set. Remove PII data or add access policies in .rad/policies/.",
|
|
84
|
+
);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
} else {
|
|
88
|
+
console.log(`Scan passed. ${files.length} file(s) checked, no PII violations.`);
|
|
89
|
+
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export function camelToPastel(str: string): string {
|
|
2
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
3
|
-
}
|
|
1
|
+
export function camelToPastel(str: string): string {
|
|
2
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
3
|
+
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export function capitaliseFirstLetter(str: string): string {
|
|
2
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
3
|
-
}
|
|
1
|
+
export function capitaliseFirstLetter(str: string): string {
|
|
2
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
3
|
+
}
|
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
import {
|
|
2
|
-
MediaTypeObject,
|
|
3
|
-
OperationObject,
|
|
4
|
-
ReferenceObject,
|
|
5
|
-
} from "@ts-stack/openapi-spec";
|
|
6
|
-
|
|
7
|
-
export function extractResponseBodySchema(
|
|
8
|
-
methodInformation: OperationObject,
|
|
9
|
-
): [string | undefined, boolean] {
|
|
10
|
-
let responseBodySchema: string | undefined;
|
|
11
|
-
let isList = false;
|
|
12
|
-
|
|
13
|
-
if (!methodInformation.responses) return [undefined, false];
|
|
14
|
-
|
|
15
|
-
Object.keys(methodInformation.responses).forEach((responseKey) => {
|
|
16
|
-
// @ts-ignore
|
|
17
|
-
const responseData = methodInformation.responses[responseKey];
|
|
18
|
-
if (responseData?.content) {
|
|
19
|
-
const mediaType = responseData.content[
|
|
20
|
-
"application/json"
|
|
21
|
-
] as MediaTypeObject;
|
|
22
|
-
if (mediaType?.schema) {
|
|
23
|
-
const refSchema = (mediaType.schema as ReferenceObject).$ref;
|
|
24
|
-
if (refSchema) {
|
|
25
|
-
responseBodySchema = refSchema.replace("#/components/schemas/", "");
|
|
26
|
-
if (responseBodySchema.startsWith("Paginated")) {
|
|
27
|
-
isList = true;
|
|
28
|
-
responseBodySchema = responseBodySchema
|
|
29
|
-
.replace("Paginated", "")
|
|
30
|
-
.slice(0, -4);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
return [responseBodySchema, isList];
|
|
38
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
MediaTypeObject,
|
|
3
|
+
OperationObject,
|
|
4
|
+
ReferenceObject,
|
|
5
|
+
} from "@ts-stack/openapi-spec";
|
|
6
|
+
|
|
7
|
+
export function extractResponseBodySchema(
|
|
8
|
+
methodInformation: OperationObject,
|
|
9
|
+
): [string | undefined, boolean] {
|
|
10
|
+
let responseBodySchema: string | undefined;
|
|
11
|
+
let isList = false;
|
|
12
|
+
|
|
13
|
+
if (!methodInformation.responses) return [undefined, false];
|
|
14
|
+
|
|
15
|
+
Object.keys(methodInformation.responses).forEach((responseKey) => {
|
|
16
|
+
// @ts-ignore
|
|
17
|
+
const responseData = methodInformation.responses[responseKey];
|
|
18
|
+
if (responseData?.content) {
|
|
19
|
+
const mediaType = responseData.content[
|
|
20
|
+
"application/json"
|
|
21
|
+
] as MediaTypeObject;
|
|
22
|
+
if (mediaType?.schema) {
|
|
23
|
+
const refSchema = (mediaType.schema as ReferenceObject).$ref;
|
|
24
|
+
if (refSchema) {
|
|
25
|
+
responseBodySchema = refSchema.replace("#/components/schemas/", "");
|
|
26
|
+
if (responseBodySchema.startsWith("Paginated")) {
|
|
27
|
+
isList = true;
|
|
28
|
+
responseBodySchema = responseBodySchema
|
|
29
|
+
.replace("Paginated", "")
|
|
30
|
+
.slice(0, -4);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return [responseBodySchema, isList];
|
|
38
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { ComponentsObject, PathsObject } from "@ts-stack/openapi-spec";
|
|
2
|
-
import axios from "axios";
|
|
3
|
-
|
|
4
|
-
interface OpenAPISchema {
|
|
5
|
-
paths: PathsObject;
|
|
6
|
-
components: ComponentsObject;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export async function fetchSchema(url: string): Promise<OpenAPISchema> {
|
|
10
|
-
const response = await axios.get<OpenAPISchema>(url);
|
|
11
|
-
return response.data;
|
|
12
|
-
}
|
|
1
|
+
import { ComponentsObject, PathsObject } from "@ts-stack/openapi-spec";
|
|
2
|
+
import axios from "axios";
|
|
3
|
+
|
|
4
|
+
interface OpenAPISchema {
|
|
5
|
+
paths: PathsObject;
|
|
6
|
+
components: ComponentsObject;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function fetchSchema(url: string): Promise<OpenAPISchema> {
|
|
10
|
+
const response = await axios.get<OpenAPISchema>(url);
|
|
11
|
+
return response.data;
|
|
12
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { PathsObject } from "@ts-stack/openapi-spec";
|
|
2
|
-
|
|
3
|
-
export function removeNonModelPaths(paths: PathsObject): PathsObject {
|
|
4
|
-
const filteredPaths: PathsObject = {};
|
|
5
|
-
Object.keys(paths).forEach((key) => {
|
|
6
|
-
const appName = key.split("/")[2];
|
|
7
|
-
const modelName = key.split("/")[4];
|
|
8
|
-
if (appName && modelName) {
|
|
9
|
-
filteredPaths[key] = paths[key];
|
|
10
|
-
}
|
|
11
|
-
});
|
|
12
|
-
return filteredPaths;
|
|
13
|
-
}
|
|
1
|
+
import { PathsObject } from "@ts-stack/openapi-spec";
|
|
2
|
+
|
|
3
|
+
export function removeNonModelPaths(paths: PathsObject): PathsObject {
|
|
4
|
+
const filteredPaths: PathsObject = {};
|
|
5
|
+
Object.keys(paths).forEach((key) => {
|
|
6
|
+
const appName = key.split("/")[2];
|
|
7
|
+
const modelName = key.split("/")[4];
|
|
8
|
+
if (appName && modelName) {
|
|
9
|
+
filteredPaths[key] = paths[key];
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
return filteredPaths;
|
|
13
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export function toCamelCase(str: string): string {
|
|
2
|
-
const pastel = str
|
|
3
|
-
.split("-")
|
|
4
|
-
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
5
|
-
.join("");
|
|
6
|
-
return pastel.charAt(0).toLowerCase() + pastel.slice(1);
|
|
7
|
-
}
|
|
1
|
+
export function toCamelCase(str: string): string {
|
|
2
|
+
const pastel = str
|
|
3
|
+
.split("-")
|
|
4
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
5
|
+
.join("");
|
|
6
|
+
return pastel.charAt(0).toLowerCase() + pastel.slice(1);
|
|
7
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export function toPascalCase(str: string): string {
|
|
2
|
-
return str
|
|
3
|
-
.split("-")
|
|
4
|
-
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
5
|
-
.join("");
|
|
6
|
-
}
|
|
1
|
+
export function toPascalCase(str: string): string {
|
|
2
|
+
return str
|
|
3
|
+
.split("-")
|
|
4
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
5
|
+
.join("");
|
|
6
|
+
}
|