@m6d/cortex-cli 0.12.1 → 0.14.0

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 (76) hide show
  1. package/README.md +193 -128
  2. package/package.json +16 -5
  3. package/src/cli.ts +19 -12
  4. package/src/commands/apply.ts +186 -0
  5. package/src/commands/{cc.ts → management.ts} +173 -112
  6. package/src/commands/new.ts +83 -84
  7. package/src/commands/setup.ts +610 -0
  8. package/src/contracts/charts/chart-spec.ts +1 -1
  9. package/src/contracts/client-tools/index.ts +3 -3
  10. package/src/contracts/{runtime → datasets}/datasets.ts +7 -42
  11. package/src/contracts/datasets/index.ts +3 -0
  12. package/src/contracts/egress-policy/index.ts +16 -0
  13. package/src/contracts/{egress → egress-policy}/scrub.ts +34 -5
  14. package/src/contracts/{egress → egress-policy}/url.ts +22 -9
  15. package/src/contracts/errors/index.ts +38 -0
  16. package/src/contracts/json/index.ts +5 -0
  17. package/src/contracts/manifest/index.ts +973 -0
  18. package/src/contracts/manifest-file/index.ts +16 -0
  19. package/src/contracts/markdown/index.ts +1 -1
  20. package/src/contracts/pattern/index.ts +276 -0
  21. package/src/contracts/server/index.ts +349 -0
  22. package/src/contracts/server/reserved-names.fixtures.json +18 -0
  23. package/src/contracts/triggers/index.ts +112 -0
  24. package/src/contracts/wire/index.ts +67 -33
  25. package/src/{cc → management}/client.ts +33 -7
  26. package/src/{cc → management}/flags.ts +2 -1
  27. package/src/{cc → management}/profiles.ts +10 -8
  28. package/src/scaffold/files.ts +448 -284
  29. package/src/scaffold/ports.ts +12 -24
  30. package/src/scaffold/templates/cspell.json.txt +41 -0
  31. package/src/scaffold/templates/datasets.ts.txt +52 -0
  32. package/src/scaffold/templates/dockerignore.txt +6 -0
  33. package/src/scaffold/templates/fallowrc.jsonc.txt +8 -12
  34. package/src/scaffold/templates/gitignore.txt +1 -1
  35. package/src/scaffold/templates/server.ts.txt +40 -0
  36. package/src/scaffold/templates/setup.ts.txt +44 -0
  37. package/src/ui/prompts.ts +24 -38
  38. package/src/ui/report.ts +5 -15
  39. package/tsconfig.json +2 -3
  40. package/src/commands/graph.ts +0 -228
  41. package/src/commands/swagger.ts +0 -247
  42. package/src/config/load.ts +0 -122
  43. package/src/config/validate.ts +0 -46
  44. package/src/contracts/credentials/index.ts +0 -183
  45. package/src/contracts/egress/index.ts +0 -120
  46. package/src/contracts/graph/clients/embed.ts +0 -62
  47. package/src/contracts/graph/clients/neo4j.ts +0 -85
  48. package/src/contracts/graph/helpers.ts +0 -55
  49. package/src/contracts/graph/index.ts +0 -30
  50. package/src/contracts/graph/schema.ts +0 -65
  51. package/src/contracts/graph/types.ts +0 -125
  52. package/src/contracts/rich-text/index.ts +0 -220
  53. package/src/contracts/runtime/api.ts +0 -345
  54. package/src/contracts/runtime/auth.ts +0 -41
  55. package/src/contracts/runtime/grammar.ts +0 -169
  56. package/src/contracts/runtime/index.ts +0 -15
  57. package/src/contracts/runtime/models.ts +0 -50
  58. package/src/contracts/runtime/signature.ts +0 -255
  59. package/src/contracts/runtime/sync.ts +0 -196
  60. package/src/contracts/runtime/triggers.ts +0 -267
  61. package/src/graph/expand-domains.ts +0 -286
  62. package/src/graph/generate-cypher.ts +0 -201
  63. package/src/graph/seed.ts +0 -199
  64. package/src/graph/validate.ts +0 -78
  65. package/src/scaffold/features.ts +0 -190
  66. package/src/scaffold/templates/domains-README.md.txt +0 -16
  67. package/src/scaffold/templates/index.ts.txt +0 -18
  68. package/src/swagger/extract-endpoints.ts +0 -269
  69. package/src/swagger/generated-block.ts +0 -74
  70. package/src/swagger/openapi-schema.ts +0 -168
  71. package/src/swagger/parse-response.ts +0 -88
  72. package/src/swagger/types.ts +0 -25
  73. /package/src/contracts/{runtime → datasets}/dataset-sql-guide.ts +0 -0
  74. /package/src/contracts/{runtime → datasets}/dataset-sql.ts +0 -0
  75. /package/src/contracts/{runtime → datasets}/datasets.fixtures.json +0 -0
  76. /package/src/{cc → management}/output.ts +0 -0
@@ -1,168 +0,0 @@
1
- /**
2
- * Reading an OpenAPI document: following $ref, deciding a schema's type, and turning
3
- * a schema into the flat property list the generator emits. Nothing here knows what
4
- * the properties are for.
5
- */
6
- import { isRecord } from "@cortex/contracts/json";
7
- import type { Obj, Prop } from "./types";
8
-
9
- const MAX_DEPTH = 8;
10
-
11
- export function obj(v: unknown) {
12
- return isRecord(v) ? v : {};
13
- }
14
-
15
- function strings(v: unknown) {
16
- return Array.isArray(v) ? v.filter((x): x is string => typeof x === "string") : [];
17
- }
18
-
19
- /** The first prop of each name. */
20
- export function dedupe(items: Prop[]) {
21
- const seen = new Set<string>();
22
- return items.filter((prop) => {
23
- if (seen.has(prop.name)) return false;
24
- seen.add(prop.name);
25
- return true;
26
- });
27
- }
28
-
29
- function resolvePointer(doc: unknown, ref: string) {
30
- if (!ref.startsWith("#/")) return null;
31
- let cur: unknown = doc;
32
- for (const part of ref.slice(2).split("/")) {
33
- const key = part.replace(/~1/g, "/").replace(/~0/g, "~");
34
- if (!isRecord(cur) || !(key in cur)) return null;
35
- cur = cur[key];
36
- }
37
- return cur;
38
- }
39
-
40
- export function deref(doc: unknown, v: unknown) {
41
- if (!isRecord(v)) return {};
42
- let x = v;
43
- const seen = new Set<string>();
44
- while (typeof x.$ref === "string") {
45
- if (seen.has(x.$ref)) return {};
46
- seen.add(x.$ref);
47
- x = obj(resolvePointer(doc, x.$ref));
48
- }
49
- return x;
50
- }
51
-
52
- export function schemaType(s: Obj) {
53
- const t = Array.isArray(s.type)
54
- ? (s.type as unknown[]).find((x) => typeof x === "string" && x !== "null")
55
- : s.type;
56
- if (typeof t === "string") return t;
57
- if (isRecord(s.properties)) return "object";
58
- if (isRecord(s.items)) return "array";
59
- return "";
60
- }
61
-
62
- function isJsonNodeRef(v: unknown) {
63
- return (
64
- isRecord(v) &&
65
- typeof v.$ref === "string" &&
66
- /\/System\.Text\.Json\.Nodes\.JsonNode$/.test(v.$ref)
67
- );
68
- }
69
-
70
- function isJsonNodeSchema(s: Obj) {
71
- const p = obj(s.properties);
72
- return (
73
- schemaType(s) === "object" &&
74
- "options" in p &&
75
- "parent" in p &&
76
- "root" in p &&
77
- isJsonNodeRef(p.parent) &&
78
- isJsonNodeRef(p.root)
79
- );
80
- }
81
-
82
- function scalarType(s: Obj) {
83
- if (Array.isArray(s.enum) && s.enum.every((v) => typeof v === "string")) {
84
- return s.enum.map((v) => `'${String(v).replace(/'/g, "\\'")}'`).join(" | ");
85
- }
86
- const t = schemaType(s);
87
- const f = typeof s.format === "string" ? s.format : "";
88
- if (f === "uuid") return "uuid";
89
- if (f === "date") return "date";
90
- if (f === "date-time") return "datetime";
91
- if (t === "integer" || t === "number") return "number";
92
- if (t === "boolean") return "boolean";
93
- if (t === "string") return "string";
94
- if (t === "object") return "object";
95
- return "unknown";
96
- }
97
-
98
- export function toProps(doc: unknown, schemaIn: unknown, depth = 0) {
99
- if (depth > MAX_DEPTH) return [];
100
- const s = deref(doc, schemaIn);
101
- if (isJsonNodeRef(schemaIn) || isJsonNodeSchema(s)) return [];
102
- const req = new Set(strings(s.required));
103
- return dedupe(
104
- Object.entries(obj(s.properties)).map(([name, child]) =>
105
- toProp(doc, name, child, req.has(name), depth + 1),
106
- ),
107
- );
108
- }
109
-
110
- export function toProp(
111
- doc: unknown,
112
- name: string,
113
- schemaIn: unknown,
114
- required: boolean,
115
- depth = 0,
116
- // eslint-disable-next-line m6d/no-explicit-return-type -- mutually recursive with toProps, so the return type cannot be inferred
117
- ): Prop {
118
- if (isJsonNodeRef(schemaIn)) return { name, required, type: "object" };
119
-
120
- const s = deref(doc, schemaIn);
121
- const req = required && s.nullable !== true;
122
-
123
- if (isJsonNodeSchema(s)) return { name, required: req, type: "object" };
124
-
125
- const t = schemaType(s);
126
- if (t === "array") {
127
- const item = deref(doc, s.items);
128
- if (schemaType(item) === "object") {
129
- const properties = toProps(doc, item, depth + 1);
130
- return {
131
- name,
132
- required: req,
133
- type: "object",
134
- isArray: true,
135
- ...(properties.length ? { properties } : {}),
136
- };
137
- }
138
- return { name, required: req, type: scalarType(item), isArray: true };
139
- }
140
-
141
- if (t === "object") {
142
- const properties = toProps(doc, s, depth + 1);
143
- return {
144
- name,
145
- required: req,
146
- type: "object",
147
- ...(properties.length ? { properties } : {}),
148
- };
149
- }
150
-
151
- return { name, required: req, type: scalarType(s) };
152
- }
153
-
154
- /**
155
- * The schema under `application/json`, else any JSON type, else the first
156
- * type, along with every type offered.
157
- */
158
- export function pickContent(contentIn: unknown) {
159
- const content = obj(contentIn);
160
- const mimes = Object.keys(content);
161
- const [first] = mimes;
162
- if (first === undefined) return null;
163
- const mime =
164
- mimes.find((x) => x.toLowerCase() === "application/json") ??
165
- mimes.find((x) => x.toLowerCase().includes("json")) ??
166
- first;
167
- return { schema: obj(content[mime]).schema, mimes };
168
- }
@@ -1,88 +0,0 @@
1
- /**
2
- * Deciding what an endpoint answers with: which statuses it uses, and what shape the
3
- * success body takes. The shape is what drives code generation, so a paged envelope
4
- * is reported as the rows it wraps rather than as its own page fields.
5
- */
6
- import type { Obj, Prop, ResponseKind } from "./types";
7
- import { deref, obj, pickContent, schemaType, toProp, toProps } from "./openapi-schema";
8
-
9
- type ResponseBody = { responseKind: ResponseKind; response: Prop[] };
10
-
11
- const NO_BODY = { responseKind: "none", response: [] } satisfies ResponseBody;
12
-
13
- const FILE_MIME = /(application\/octet-stream|application\/pdf|application\/vnd\.|text\/csv)/i;
14
-
15
- /** An object contributes its own fields; anything else stands as a single `item`. */
16
- function itemProps(doc: unknown, item: Obj) {
17
- return schemaType(item) === "object" ? toProps(doc, item) : [toProp(doc, "item", item, true)];
18
- }
19
-
20
- function isFileResponse(mimes: string[], schema: Obj) {
21
- if (mimes.some((mime) => FILE_MIME.test(mime))) return true;
22
-
23
- return (
24
- schemaType(schema) === "string" &&
25
- typeof schema.format === "string" &&
26
- ["binary", "base64"].includes(schema.format)
27
- );
28
- }
29
-
30
- /** A paged envelope is described by the items it wraps, not by its own page fields. */
31
- function parseObjectBody(doc: unknown, schema: Obj) {
32
- const props = obj(schema.properties);
33
- const hasPaging = ["totalCount", "pageNumber", "pageSize", "totalPages"].some(
34
- (k) => k in props,
35
- );
36
- const list = deref(doc, props.items ?? props.data ?? props.results);
37
-
38
- if (hasPaging && schemaType(list) === "array") {
39
- return {
40
- responseKind: "paginated",
41
- response: itemProps(doc, deref(doc, list.items)),
42
- } satisfies ResponseBody;
43
- }
44
-
45
- return { responseKind: "object", response: toProps(doc, schema) } satisfies ResponseBody;
46
- }
47
-
48
- function parseResponseBody(doc: unknown, schema: Obj, mimes: string[]) {
49
- if (isFileResponse(mimes, schema))
50
- return { responseKind: "file", response: [] } satisfies ResponseBody;
51
-
52
- const type = schemaType(schema);
53
- if (type === "array") {
54
- return {
55
- responseKind: "array",
56
- response: itemProps(doc, deref(doc, schema.items)),
57
- } satisfies ResponseBody;
58
- }
59
- if (type === "object") return parseObjectBody(doc, schema);
60
- if (Object.keys(schema).length) {
61
- return {
62
- responseKind: "object",
63
- response: [toProp(doc, "value", schema, true)],
64
- } satisfies ResponseBody;
65
- }
66
-
67
- return NO_BODY;
68
- }
69
-
70
- export function parseResponse(doc: unknown, responsesIn: unknown) {
71
- const responses = obj(responsesIn);
72
- const statuses = Object.keys(responses)
73
- .map(Number)
74
- .filter(Number.isInteger)
75
- .sort((a, b) => a - b);
76
- const successStatus = statuses.find((s) => s >= 200 && s < 300) ?? 200;
77
- const errorStatuses = [...new Set(statuses.filter((s) => s >= 400))];
78
-
79
- // 204 says there is no body at all, so there is nothing further to read.
80
- if (successStatus === 204) return { successStatus, errorStatuses, ...NO_BODY };
81
-
82
- const success = deref(doc, responses[String(successStatus)]);
83
- const content = pickContent(success.content);
84
- if (!content) return { successStatus, errorStatuses, ...NO_BODY };
85
-
86
- const body = parseResponseBody(doc, deref(doc, content.schema), content.mimes);
87
- return { successStatus, errorStatuses, ...body };
88
- }
@@ -1,25 +0,0 @@
1
- /**
2
- * The shapes shared across endpoint extraction: what an OpenAPI document looks like
3
- * on the way in, and what one generated endpoint looks like on the way out.
4
- */
5
- export type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
6
- export type ResponseKind = "object" | "array" | "paginated" | "file" | "none";
7
- export type Obj = Record<string, unknown>;
8
- export type Prop = {
9
- name: string;
10
- required: boolean;
11
- type: string;
12
- isArray?: boolean;
13
- properties?: Prop[];
14
- };
15
- export type Endpoint = {
16
- key: string;
17
- method: HttpMethod;
18
- path: string;
19
- params: Prop[];
20
- body: Prop[];
21
- response: Prop[];
22
- responseKind: ResponseKind;
23
- successStatus: number;
24
- errorStatuses: number[];
25
- };
File without changes