@contractspec/module.workspace 1.44.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 (138) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +50 -0
  3. package/dist/ai/code-generation.d.ts +28 -0
  4. package/dist/ai/code-generation.d.ts.map +1 -0
  5. package/dist/ai/code-generation.js +138 -0
  6. package/dist/ai/code-generation.js.map +1 -0
  7. package/dist/ai/spec-creation.d.ts +27 -0
  8. package/dist/ai/spec-creation.d.ts.map +1 -0
  9. package/dist/ai/spec-creation.js +102 -0
  10. package/dist/ai/spec-creation.js.map +1 -0
  11. package/dist/analysis/deps/graph.d.ts +34 -0
  12. package/dist/analysis/deps/graph.d.ts.map +1 -0
  13. package/dist/analysis/deps/graph.js +85 -0
  14. package/dist/analysis/deps/graph.js.map +1 -0
  15. package/dist/analysis/deps/parse-imports.d.ts +17 -0
  16. package/dist/analysis/deps/parse-imports.d.ts.map +1 -0
  17. package/dist/analysis/deps/parse-imports.js +31 -0
  18. package/dist/analysis/deps/parse-imports.js.map +1 -0
  19. package/dist/analysis/diff/deep-diff.d.ts +33 -0
  20. package/dist/analysis/diff/deep-diff.d.ts.map +1 -0
  21. package/dist/analysis/diff/deep-diff.js +114 -0
  22. package/dist/analysis/diff/deep-diff.js.map +1 -0
  23. package/dist/analysis/diff/semantic.d.ts +11 -0
  24. package/dist/analysis/diff/semantic.d.ts.map +1 -0
  25. package/dist/analysis/diff/semantic.js +97 -0
  26. package/dist/analysis/diff/semantic.js.map +1 -0
  27. package/dist/analysis/feature-scan.d.ts +15 -0
  28. package/dist/analysis/feature-scan.d.ts.map +1 -0
  29. package/dist/analysis/feature-scan.js +152 -0
  30. package/dist/analysis/feature-scan.js.map +1 -0
  31. package/dist/analysis/grouping.d.ts +79 -0
  32. package/dist/analysis/grouping.d.ts.map +1 -0
  33. package/dist/analysis/grouping.js +115 -0
  34. package/dist/analysis/grouping.js.map +1 -0
  35. package/dist/analysis/impact/classifier.d.ts +19 -0
  36. package/dist/analysis/impact/classifier.d.ts.map +1 -0
  37. package/dist/analysis/impact/classifier.js +135 -0
  38. package/dist/analysis/impact/classifier.js.map +1 -0
  39. package/dist/analysis/impact/index.js +2 -0
  40. package/dist/analysis/impact/rules.d.ts +35 -0
  41. package/dist/analysis/impact/rules.d.ts.map +1 -0
  42. package/dist/analysis/impact/rules.js +154 -0
  43. package/dist/analysis/impact/rules.js.map +1 -0
  44. package/dist/analysis/impact/types.d.ts +95 -0
  45. package/dist/analysis/impact/types.d.ts.map +1 -0
  46. package/dist/analysis/index.js +14 -0
  47. package/dist/analysis/snapshot/index.js +2 -0
  48. package/dist/analysis/snapshot/normalizer.d.ts +36 -0
  49. package/dist/analysis/snapshot/normalizer.d.ts.map +1 -0
  50. package/dist/analysis/snapshot/normalizer.js +66 -0
  51. package/dist/analysis/snapshot/normalizer.js.map +1 -0
  52. package/dist/analysis/snapshot/snapshot.d.ts +18 -0
  53. package/dist/analysis/snapshot/snapshot.d.ts.map +1 -0
  54. package/dist/analysis/snapshot/snapshot.js +163 -0
  55. package/dist/analysis/snapshot/snapshot.js.map +1 -0
  56. package/dist/analysis/snapshot/types.d.ts +80 -0
  57. package/dist/analysis/snapshot/types.d.ts.map +1 -0
  58. package/dist/analysis/spec-scan.d.ts +34 -0
  59. package/dist/analysis/spec-scan.d.ts.map +1 -0
  60. package/dist/analysis/spec-scan.js +349 -0
  61. package/dist/analysis/spec-scan.js.map +1 -0
  62. package/dist/analysis/validate/spec-structure.d.ts +29 -0
  63. package/dist/analysis/validate/spec-structure.d.ts.map +1 -0
  64. package/dist/analysis/validate/spec-structure.js +139 -0
  65. package/dist/analysis/validate/spec-structure.js.map +1 -0
  66. package/dist/formatter.d.ts +42 -0
  67. package/dist/formatter.d.ts.map +1 -0
  68. package/dist/formatter.js +163 -0
  69. package/dist/formatter.js.map +1 -0
  70. package/dist/index.d.ts +35 -0
  71. package/dist/index.js +33 -0
  72. package/dist/templates/app-config.d.ts +7 -0
  73. package/dist/templates/app-config.d.ts.map +1 -0
  74. package/dist/templates/app-config.js +106 -0
  75. package/dist/templates/app-config.js.map +1 -0
  76. package/dist/templates/data-view.d.ts +7 -0
  77. package/dist/templates/data-view.d.ts.map +1 -0
  78. package/dist/templates/data-view.js +69 -0
  79. package/dist/templates/data-view.js.map +1 -0
  80. package/dist/templates/event.d.ts +11 -0
  81. package/dist/templates/event.d.ts.map +1 -0
  82. package/dist/templates/event.js +41 -0
  83. package/dist/templates/event.js.map +1 -0
  84. package/dist/templates/experiment.d.ts +7 -0
  85. package/dist/templates/experiment.d.ts.map +1 -0
  86. package/dist/templates/experiment.js +88 -0
  87. package/dist/templates/experiment.js.map +1 -0
  88. package/dist/templates/handler.d.ts +20 -0
  89. package/dist/templates/handler.d.ts.map +1 -0
  90. package/dist/templates/handler.js +96 -0
  91. package/dist/templates/handler.js.map +1 -0
  92. package/dist/templates/integration-utils.js +105 -0
  93. package/dist/templates/integration-utils.js.map +1 -0
  94. package/dist/templates/integration.d.ts +7 -0
  95. package/dist/templates/integration.d.ts.map +1 -0
  96. package/dist/templates/integration.js +63 -0
  97. package/dist/templates/integration.js.map +1 -0
  98. package/dist/templates/knowledge.d.ts +7 -0
  99. package/dist/templates/knowledge.d.ts.map +1 -0
  100. package/dist/templates/knowledge.js +69 -0
  101. package/dist/templates/knowledge.js.map +1 -0
  102. package/dist/templates/migration.d.ts +7 -0
  103. package/dist/templates/migration.d.ts.map +1 -0
  104. package/dist/templates/migration.js +61 -0
  105. package/dist/templates/migration.js.map +1 -0
  106. package/dist/templates/operation.d.ts +11 -0
  107. package/dist/templates/operation.d.ts.map +1 -0
  108. package/dist/templates/operation.js +101 -0
  109. package/dist/templates/operation.js.map +1 -0
  110. package/dist/templates/presentation.d.ts +11 -0
  111. package/dist/templates/presentation.d.ts.map +1 -0
  112. package/dist/templates/presentation.js +79 -0
  113. package/dist/templates/presentation.js.map +1 -0
  114. package/dist/templates/telemetry.d.ts +7 -0
  115. package/dist/templates/telemetry.d.ts.map +1 -0
  116. package/dist/templates/telemetry.js +90 -0
  117. package/dist/templates/telemetry.js.map +1 -0
  118. package/dist/templates/utils.d.ts +27 -0
  119. package/dist/templates/utils.d.ts.map +1 -0
  120. package/dist/templates/utils.js +39 -0
  121. package/dist/templates/utils.js.map +1 -0
  122. package/dist/templates/workflow-runner.d.ts +16 -0
  123. package/dist/templates/workflow-runner.d.ts.map +1 -0
  124. package/dist/templates/workflow-runner.js +49 -0
  125. package/dist/templates/workflow-runner.js.map +1 -0
  126. package/dist/templates/workflow.d.ts +11 -0
  127. package/dist/templates/workflow.d.ts.map +1 -0
  128. package/dist/templates/workflow.js +68 -0
  129. package/dist/templates/workflow.js.map +1 -0
  130. package/dist/types/analysis-types.d.ts +126 -0
  131. package/dist/types/analysis-types.d.ts.map +1 -0
  132. package/dist/types/generation-types.d.ts +84 -0
  133. package/dist/types/generation-types.d.ts.map +1 -0
  134. package/dist/types/generation-types.js +21 -0
  135. package/dist/types/generation-types.js.map +1 -0
  136. package/dist/types/spec-types.d.ts +345 -0
  137. package/dist/types/spec-types.d.ts.map +1 -0
  138. package/package.json +55 -0
@@ -0,0 +1,18 @@
1
+ import { ContractSnapshot, SnapshotOptions } from "./types.js";
2
+
3
+ //#region src/analysis/snapshot/snapshot.d.ts
4
+
5
+ /**
6
+ * Generate a contract snapshot from spec source files.
7
+ *
8
+ * @param specs - Array of { path, content } for each spec file
9
+ * @param options - Snapshot generation options
10
+ * @returns Canonical contract snapshot
11
+ */
12
+ declare function generateSnapshot(specs: {
13
+ path: string;
14
+ content: string;
15
+ }[], options?: SnapshotOptions): ContractSnapshot;
16
+ //#endregion
17
+ export { generateSnapshot };
18
+ //# sourceMappingURL=snapshot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.d.ts","names":[],"sources":["../../../src/analysis/snapshot/snapshot.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;iBA2BgB,gBAAA;;;eAEL,kBACR"}
@@ -0,0 +1,163 @@
1
+ import { scanSpecSource } from "../spec-scan.js";
2
+ import { computeHash, sortFields, sortSpecs } from "./normalizer.js";
3
+
4
+ //#region src/analysis/snapshot/snapshot.ts
5
+ /**
6
+ * Contract snapshot generation.
7
+ *
8
+ * Generates canonical, deterministic snapshots from spec source files
9
+ * for comparison and impact detection.
10
+ */
11
+ /**
12
+ * Generate a contract snapshot from spec source files.
13
+ *
14
+ * @param specs - Array of { path, content } for each spec file
15
+ * @param options - Snapshot generation options
16
+ * @returns Canonical contract snapshot
17
+ */
18
+ function generateSnapshot(specs, options = {}) {
19
+ const snapshots = [];
20
+ for (const { path, content } of specs) {
21
+ const scanned = scanSpecSource(content, path);
22
+ if (options.types && !options.types.includes(scanned.specType)) continue;
23
+ if (scanned.specType === "operation" && scanned.key && scanned.version !== void 0) {
24
+ const opSnapshot = createOperationSnapshot(scanned, content);
25
+ if (opSnapshot) snapshots.push(opSnapshot);
26
+ } else if (scanned.specType === "event" && scanned.key && scanned.version !== void 0) {
27
+ const eventSnapshot = createEventSnapshot(scanned, content);
28
+ if (eventSnapshot) snapshots.push(eventSnapshot);
29
+ }
30
+ }
31
+ const sortedSpecs = sortSpecs(snapshots);
32
+ const hash = computeHash({ specs: sortedSpecs });
33
+ return {
34
+ version: 1,
35
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
36
+ specs: sortedSpecs,
37
+ hash
38
+ };
39
+ }
40
+ /**
41
+ * Create an operation snapshot from scanned spec data.
42
+ */
43
+ function createOperationSnapshot(scanned, content) {
44
+ if (!scanned.key || scanned.version === void 0) return null;
45
+ const io = extractIoFromSource(content);
46
+ const http = extractHttpBinding(content);
47
+ return {
48
+ type: "operation",
49
+ key: scanned.key,
50
+ version: scanned.version,
51
+ kind: scanned.kind === "command" || scanned.kind === "query" ? scanned.kind : "command",
52
+ stability: scanned.stability ?? "experimental",
53
+ http: http ?? void 0,
54
+ io,
55
+ authLevel: extractAuthLevel(content),
56
+ emittedEvents: scanned.emittedEvents
57
+ };
58
+ }
59
+ /**
60
+ * Create an event snapshot from scanned spec data.
61
+ */
62
+ function createEventSnapshot(scanned, content) {
63
+ if (!scanned.key || scanned.version === void 0) return null;
64
+ const payload = extractPayloadFromSource(content);
65
+ return {
66
+ type: "event",
67
+ key: scanned.key,
68
+ version: scanned.version,
69
+ stability: scanned.stability ?? "experimental",
70
+ payload
71
+ };
72
+ }
73
+ /**
74
+ * Extract IO schema from source code.
75
+ * This is a heuristic extraction - not full Zod introspection.
76
+ */
77
+ function extractIoFromSource(content) {
78
+ const input = extractSchemaFields(content, "input");
79
+ const output = extractSchemaFields(content, "output");
80
+ return {
81
+ input: sortFields(input),
82
+ output: sortFields(output)
83
+ };
84
+ }
85
+ /**
86
+ * Extract payload schema from event source code.
87
+ */
88
+ function extractPayloadFromSource(content) {
89
+ return sortFields(extractSchemaFields(content, "payload"));
90
+ }
91
+ /**
92
+ * Extract schema fields from a specific section of the source.
93
+ */
94
+ function extractSchemaFields(content, section) {
95
+ const fields = {};
96
+ const sectionPattern = new RegExp(`${section}\\s*:\\s*z\\.object\\(\\{([^}]+)\\}`, "s");
97
+ const sectionMatch = content.match(sectionPattern);
98
+ if (!sectionMatch?.[1]) return fields;
99
+ const sectionContent = sectionMatch[1];
100
+ const fieldPattern = /(\w+)\s*:\s*z\.(\w+)\((.*?)\)/g;
101
+ let match;
102
+ while ((match = fieldPattern.exec(sectionContent)) !== null) {
103
+ const [, fieldName, zodType] = match;
104
+ if (!fieldName || !zodType) continue;
105
+ const isOptional = sectionContent.includes(`${fieldName}:`) && sectionContent.slice(sectionContent.indexOf(`${fieldName}:`)).includes(".optional()");
106
+ const isNullable = sectionContent.includes(`${fieldName}:`) && sectionContent.slice(sectionContent.indexOf(`${fieldName}:`)).includes(".nullable()");
107
+ fields[fieldName] = {
108
+ name: fieldName,
109
+ type: mapZodTypeToFieldType(zodType),
110
+ required: !isOptional,
111
+ nullable: isNullable
112
+ };
113
+ }
114
+ return fields;
115
+ }
116
+ /**
117
+ * Map Zod type to FieldType.
118
+ */
119
+ function mapZodTypeToFieldType(zodType) {
120
+ return {
121
+ string: "string",
122
+ number: "number",
123
+ boolean: "boolean",
124
+ object: "object",
125
+ array: "array",
126
+ enum: "enum",
127
+ union: "union",
128
+ literal: "literal",
129
+ date: "date",
130
+ coerce: "unknown"
131
+ }[zodType] ?? "unknown";
132
+ }
133
+ /**
134
+ * Extract HTTP binding from source code.
135
+ */
136
+ function extractHttpBinding(content) {
137
+ const methodMatch = content.match(/method\s*:\s*['"](\w+)['"]/);
138
+ const pathMatch = content.match(/path\s*:\s*['"]([^'"]+)['"]/);
139
+ if (methodMatch?.[1] && pathMatch?.[1]) {
140
+ const method = methodMatch[1].toUpperCase();
141
+ if ([
142
+ "GET",
143
+ "POST",
144
+ "PUT",
145
+ "PATCH",
146
+ "DELETE"
147
+ ].includes(method)) return {
148
+ method,
149
+ path: pathMatch[1]
150
+ };
151
+ }
152
+ return null;
153
+ }
154
+ /**
155
+ * Extract auth level from source code.
156
+ */
157
+ function extractAuthLevel(content) {
158
+ return content.match(/auth\s*:\s*['"](\w+)['"]/)?.[1];
159
+ }
160
+
161
+ //#endregion
162
+ export { generateSnapshot };
163
+ //# sourceMappingURL=snapshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.js","names":["snapshots: SpecSnapshot[]","fields: Record<string, FieldSnapshot>"],"sources":["../../../src/analysis/snapshot/snapshot.ts"],"sourcesContent":["/**\n * Contract snapshot generation.\n *\n * Generates canonical, deterministic snapshots from spec source files\n * for comparison and impact detection.\n */\n\nimport { scanSpecSource } from '../spec-scan';\nimport { computeHash, sortSpecs, sortFields } from './normalizer';\nimport type {\n ContractSnapshot,\n EventSnapshot,\n FieldSnapshot,\n FieldType,\n IoSnapshot,\n OperationSnapshot,\n SnapshotOptions,\n SpecSnapshot,\n} from './types';\n\n/**\n * Generate a contract snapshot from spec source files.\n *\n * @param specs - Array of { path, content } for each spec file\n * @param options - Snapshot generation options\n * @returns Canonical contract snapshot\n */\nexport function generateSnapshot(\n specs: { path: string; content: string }[],\n options: SnapshotOptions = {}\n): ContractSnapshot {\n const snapshots: SpecSnapshot[] = [];\n\n for (const { path, content } of specs) {\n const scanned = scanSpecSource(content, path);\n\n // Filter by types if specified\n if (\n options.types &&\n !options.types.includes(scanned.specType as 'operation' | 'event')\n ) {\n continue;\n }\n\n if (\n scanned.specType === 'operation' &&\n scanned.key &&\n scanned.version !== undefined\n ) {\n const opSnapshot = createOperationSnapshot(scanned, content);\n if (opSnapshot) {\n snapshots.push(opSnapshot);\n }\n } else if (\n scanned.specType === 'event' &&\n scanned.key &&\n scanned.version !== undefined\n ) {\n const eventSnapshot = createEventSnapshot(scanned, content);\n if (eventSnapshot) {\n snapshots.push(eventSnapshot);\n }\n }\n }\n\n const sortedSpecs = sortSpecs(snapshots);\n const hash = computeHash({ specs: sortedSpecs });\n\n return {\n version: 1,\n generatedAt: new Date().toISOString(),\n specs: sortedSpecs,\n hash,\n };\n}\n\n/**\n * Create an operation snapshot from scanned spec data.\n */\nfunction createOperationSnapshot(\n scanned: ReturnType<typeof scanSpecSource>,\n content: string\n): OperationSnapshot | null {\n if (!scanned.key || scanned.version === undefined) {\n return null;\n }\n\n const io = extractIoFromSource(content);\n const http = extractHttpBinding(content);\n\n return {\n type: 'operation',\n key: scanned.key,\n version: scanned.version,\n kind:\n scanned.kind === 'command' || scanned.kind === 'query'\n ? scanned.kind\n : 'command',\n stability: scanned.stability ?? 'experimental',\n http: http ?? undefined,\n io,\n authLevel: extractAuthLevel(content),\n emittedEvents: scanned.emittedEvents,\n };\n}\n\n/**\n * Create an event snapshot from scanned spec data.\n */\nfunction createEventSnapshot(\n scanned: ReturnType<typeof scanSpecSource>,\n content: string\n): EventSnapshot | null {\n if (!scanned.key || scanned.version === undefined) {\n return null;\n }\n\n const payload = extractPayloadFromSource(content);\n\n return {\n type: 'event',\n key: scanned.key,\n version: scanned.version,\n stability: scanned.stability ?? 'experimental',\n payload,\n };\n}\n\n/**\n * Extract IO schema from source code.\n * This is a heuristic extraction - not full Zod introspection.\n */\nfunction extractIoFromSource(content: string): IoSnapshot {\n const input = extractSchemaFields(content, 'input');\n const output = extractSchemaFields(content, 'output');\n\n return {\n input: sortFields(input) as Record<string, FieldSnapshot>,\n output: sortFields(output) as Record<string, FieldSnapshot>,\n };\n}\n\n/**\n * Extract payload schema from event source code.\n */\nfunction extractPayloadFromSource(\n content: string\n): Record<string, FieldSnapshot> {\n const fields = extractSchemaFields(content, 'payload');\n return sortFields(fields) as Record<string, FieldSnapshot>;\n}\n\n/**\n * Extract schema fields from a specific section of the source.\n */\nfunction extractSchemaFields(\n content: string,\n section: 'input' | 'output' | 'payload'\n): Record<string, FieldSnapshot> {\n const fields: Record<string, FieldSnapshot> = {};\n\n // Look for z.object({ ... }) patterns within the section\n const sectionPattern = new RegExp(\n `${section}\\\\s*:\\\\s*z\\\\.object\\\\(\\\\{([^}]+)\\\\}`,\n 's'\n );\n const sectionMatch = content.match(sectionPattern);\n\n if (!sectionMatch?.[1]) {\n return fields;\n }\n\n const sectionContent = sectionMatch[1];\n\n // Match field definitions: fieldName: z.string(), z.number(), etc.\n const fieldPattern = /(\\w+)\\s*:\\s*z\\.(\\w+)\\((.*?)\\)/g;\n let match;\n\n while ((match = fieldPattern.exec(sectionContent)) !== null) {\n const [, fieldName, zodType] = match;\n if (!fieldName || !zodType) continue;\n\n const isOptional =\n sectionContent.includes(`${fieldName}:`) &&\n sectionContent\n .slice(sectionContent.indexOf(`${fieldName}:`))\n .includes('.optional()');\n const isNullable =\n sectionContent.includes(`${fieldName}:`) &&\n sectionContent\n .slice(sectionContent.indexOf(`${fieldName}:`))\n .includes('.nullable()');\n\n fields[fieldName] = {\n name: fieldName,\n type: mapZodTypeToFieldType(zodType),\n required: !isOptional,\n nullable: isNullable,\n };\n }\n\n return fields;\n}\n\n/**\n * Map Zod type to FieldType.\n */\nfunction mapZodTypeToFieldType(zodType: string): FieldType {\n const mapping: Record<string, FieldType> = {\n string: 'string',\n number: 'number',\n boolean: 'boolean',\n object: 'object',\n array: 'array',\n enum: 'enum',\n union: 'union',\n literal: 'literal',\n date: 'date',\n coerce: 'unknown',\n };\n return mapping[zodType] ?? 'unknown';\n}\n\n/**\n * Extract HTTP binding from source code.\n */\nfunction extractHttpBinding(content: string): {\n method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n path: string;\n} | null {\n // Look for http: { method: 'X', path: 'Y' } pattern\n const methodMatch = content.match(/method\\s*:\\s*['\"](\\w+)['\"]/);\n const pathMatch = content.match(/path\\s*:\\s*['\"]([^'\"]+)['\"]/);\n\n if (methodMatch?.[1] && pathMatch?.[1]) {\n const method = methodMatch[1].toUpperCase();\n if (['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) {\n return {\n method: method as 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',\n path: pathMatch[1],\n };\n }\n }\n\n return null;\n}\n\n/**\n * Extract auth level from source code.\n */\nfunction extractAuthLevel(content: string): string | undefined {\n const authMatch = content.match(/auth\\s*:\\s*['\"](\\w+)['\"]/);\n return authMatch?.[1];\n}\n"],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ //#region src/analysis/snapshot/types.d.ts
2
+ /**
3
+ * Canonical contract snapshot types.
4
+ *
5
+ * These types define the normalized, deterministic representation
6
+ * of contracts for comparison and impact detection.
7
+ */
8
+ /** Field type in a schema */
9
+ type FieldType = 'string' | 'number' | 'boolean' | 'object' | 'array' | 'enum' | 'union' | 'literal' | 'date' | 'unknown';
10
+ /** Schema field definition */
11
+ interface FieldSnapshot {
12
+ name: string;
13
+ type: FieldType;
14
+ required: boolean;
15
+ nullable: boolean;
16
+ description?: string;
17
+ enumValues?: string[];
18
+ literalValue?: unknown;
19
+ items?: FieldSnapshot;
20
+ properties?: Record<string, FieldSnapshot>;
21
+ unionTypes?: FieldSnapshot[];
22
+ }
23
+ /** IO schema snapshot */
24
+ interface IoSnapshot {
25
+ input: Record<string, FieldSnapshot>;
26
+ output: Record<string, FieldSnapshot>;
27
+ }
28
+ /** HTTP binding snapshot */
29
+ interface HttpBindingSnapshot {
30
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
31
+ path: string;
32
+ }
33
+ /** Operation snapshot */
34
+ interface OperationSnapshot {
35
+ type: 'operation';
36
+ key: string;
37
+ version: number;
38
+ kind: 'command' | 'query';
39
+ stability: string;
40
+ http?: HttpBindingSnapshot;
41
+ io: IoSnapshot;
42
+ authLevel?: string;
43
+ emittedEvents?: {
44
+ key: string;
45
+ version: number;
46
+ }[];
47
+ }
48
+ /** Event payload snapshot */
49
+ interface EventSnapshot {
50
+ type: 'event';
51
+ key: string;
52
+ version: number;
53
+ stability: string;
54
+ payload: Record<string, FieldSnapshot>;
55
+ }
56
+ /** Spec snapshot union type */
57
+ type SpecSnapshot = OperationSnapshot | EventSnapshot;
58
+ /** Full contract snapshot for a workspace */
59
+ interface ContractSnapshot {
60
+ /** Schema version for forward compatibility */
61
+ version: 1;
62
+ /** Generation timestamp (ISO 8601) */
63
+ generatedAt: string;
64
+ /** Git commit SHA if available */
65
+ commitSha?: string;
66
+ /** All specs in the workspace */
67
+ specs: SpecSnapshot[];
68
+ /** Content hash for quick comparison */
69
+ hash: string;
70
+ }
71
+ /** Options for snapshot generation */
72
+ interface SnapshotOptions {
73
+ /** Glob pattern for spec discovery */
74
+ pattern?: string;
75
+ /** Include only specific spec types */
76
+ types?: ('operation' | 'event')[];
77
+ }
78
+ //#endregion
79
+ export { ContractSnapshot, EventSnapshot, FieldSnapshot, FieldType, HttpBindingSnapshot, IoSnapshot, OperationSnapshot, SnapshotOptions, SpecSnapshot };
80
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/analysis/snapshot/types.ts"],"sourcesContent":[],"mappings":";;AAQA;AAaA;;;;;AAUe,KAvBH,SAAA,GAuBG,QAAA,GAAA,QAAA,GAAA,SAAA,GAAA,QAAA,GAAA,OAAA,GAAA,MAAA,GAAA,OAAA,GAAA,SAAA,GAAA,MAAA,GAAA,SAAA;;AAIE,UAdA,aAAA,CAcU;EACH,IAAA,EAAA,MAAA;EAAf,IAAA,EAbD,SAaC;EACgB,QAAA,EAAA,OAAA;EAAf,QAAA,EAAA,OAAA;EAAM,WAAA,CAAA,EAAA,MAAA;EAIC,UAAA,CAAA,EAAA,MAAA,EAAA;EAMA,YAAA,CAAA,EAAA,OAAiB;EAajB,KAAA,CAAA,EA/BP,aA+BoB;EASlB,UAAA,CAAA,EAvCG,MAuCS,CAAA,MAAA,EAvCM,aAuCH,CAAA;EAGV,UAAA,CAAA,EAzCF,aAyCkB,EAAA;AAcjC;;UAnDiB,UAAA;SACR,eAAe;UACd,eAAe;;;UAIR,mBAAA;;;;;UAMA,iBAAA;;;;;;SAMR;MACH;;;;;;;;UAMW,aAAA;;;;;WAKN,eAAe;;;KAId,YAAA,GAAe,oBAAoB;;UAG9B,gBAAA;;;;;;;;SAQR;;;;;UAMQ,eAAA"}
@@ -0,0 +1,34 @@
1
+ import { AnalyzedSpecType, RefInfo, SpecScanResult } from "../types/analysis-types.js";
2
+
3
+ //#region src/analysis/spec-scan.d.ts
4
+
5
+ /**
6
+ * Infer spec type from file path based on naming conventions.
7
+ * Supports all contract types from @contractspec/lib.contracts.
8
+ */
9
+ declare function inferSpecTypeFromFilePath(filePath: string): AnalyzedSpecType;
10
+ /**
11
+ * Scan spec source code to extract metadata without executing it.
12
+ */
13
+ declare function scanSpecSource(code: string, filePath: string): SpecScanResult;
14
+ /**
15
+ * Extract emitted event refs from operation spec source.
16
+ * Looks for sideEffects.emits array entries.
17
+ */
18
+ declare function extractEmittedEvents(code: string): RefInfo[] | undefined;
19
+ /**
20
+ * Extract policy refs from operation spec source.
21
+ */
22
+ declare function extractPolicyRefs(code: string): RefInfo[] | undefined;
23
+ /**
24
+ * Extract test spec refs.
25
+ */
26
+ declare function extractTestRefs(code: string): RefInfo[] | undefined;
27
+ /**
28
+ * Scan spec source code to extract ALL specs from a file.
29
+ * This function finds multiple spec definitions in a single file.
30
+ */
31
+ declare function scanAllSpecsFromSource(code: string, filePath: string): SpecScanResult[];
32
+ //#endregion
33
+ export { extractEmittedEvents, extractPolicyRefs, extractTestRefs, inferSpecTypeFromFilePath, scanAllSpecsFromSource, scanSpecSource };
34
+ //# sourceMappingURL=spec-scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-scan.d.ts","names":[],"sources":["../../src/analysis/spec-scan.ts"],"sourcesContent":[],"mappings":";;;;AAoHA;AAgCA;AA0BA;AAoMA;iBAjWgB,yBAAA,oBAA6C;;;;iBA8C7C,cAAA,kCAAgD;;;;;iBAqDhD,oBAAA,gBAAoC;;;;iBAgCpC,iBAAA,gBAAiC;;;;iBA0BjC,eAAA,gBAA+B;;;;;iBAoM/B,sBAAA,kCAGb"}
@@ -0,0 +1,349 @@
1
+ //#region src/analysis/spec-scan.ts
2
+ /**
3
+ * Infer spec type from file path based on naming conventions.
4
+ * Supports all contract types from @contractspec/lib.contracts.
5
+ */
6
+ function inferSpecTypeFromFilePath(filePath) {
7
+ if (filePath.includes(".operations.") || filePath.includes("/operations/") || filePath.includes(".operation.") || filePath.includes("/operation/")) return "operation";
8
+ if (filePath.includes(".event.") || filePath.includes("/events/") || filePath.endsWith("/events.ts")) return "event";
9
+ if (filePath.includes(".presentation.") || filePath.includes("/presentations/") || filePath.endsWith("/presentations.ts")) return "presentation";
10
+ if (filePath.includes(".feature.")) return "feature";
11
+ if (filePath.includes(".capability.")) return "capability";
12
+ if (filePath.includes(".data-view.")) return "data-view";
13
+ if (filePath.includes(".form.")) return "form";
14
+ if (filePath.includes(".migration.")) return "migration";
15
+ if (filePath.includes(".workflow.")) return "workflow";
16
+ if (filePath.includes(".experiment.")) return "experiment";
17
+ if (filePath.includes(".integration.")) return "integration";
18
+ if (filePath.includes(".knowledge.")) return "knowledge";
19
+ if (filePath.includes(".telemetry.")) return "telemetry";
20
+ if (filePath.includes(".app-config.")) return "app-config";
21
+ if (filePath.includes(".policy.")) return "policy";
22
+ if (filePath.includes(".test-spec.")) return "test-spec";
23
+ return "unknown";
24
+ }
25
+ /**
26
+ * Scan spec source code to extract metadata without executing it.
27
+ */
28
+ function scanSpecSource(code, filePath) {
29
+ const specType = inferSpecTypeFromFilePath(filePath);
30
+ const key = matchStringField(code, "key");
31
+ const description = matchStringField(code, "description");
32
+ const stabilityRaw = matchStringField(code, "stability");
33
+ const stability = isStability(stabilityRaw) ? stabilityRaw : void 0;
34
+ const owners = matchStringArrayField(code, "owners");
35
+ const tags = matchStringArrayField(code, "tags");
36
+ const version = matchNumberField(code, "version");
37
+ const kind = inferOperationKind(code);
38
+ const hasMeta = /meta\s*:\s*{/.test(code);
39
+ const hasIo = /\bio\s*:\s*{/.test(code);
40
+ const hasPolicy = /\bpolicy\s*:\s*{/.test(code);
41
+ const hasPayload = /\bpayload\s*:\s*{/.test(code);
42
+ const hasContent = /\bcontent\s*:\s*{/.test(code);
43
+ const hasDefinition = /\bdefinition\s*:\s*{/.test(code);
44
+ const emittedEvents = specType === "operation" ? extractEmittedEvents(code) : void 0;
45
+ const policyRefs = specType === "operation" ? extractPolicyRefs(code) : void 0;
46
+ const testRefs = extractTestRefs(code);
47
+ return {
48
+ filePath,
49
+ specType,
50
+ key: key ?? void 0,
51
+ description: description ?? void 0,
52
+ stability,
53
+ owners,
54
+ tags,
55
+ version: version ?? void 0,
56
+ kind,
57
+ hasMeta,
58
+ hasIo,
59
+ hasPolicy,
60
+ hasPayload,
61
+ hasContent,
62
+ hasDefinition,
63
+ emittedEvents,
64
+ policyRefs,
65
+ testRefs
66
+ };
67
+ }
68
+ /**
69
+ * Extract emitted event refs from operation spec source.
70
+ * Looks for sideEffects.emits array entries.
71
+ */
72
+ function extractEmittedEvents(code) {
73
+ const events = [];
74
+ const inlinePattern = /\{\s*key:\s*['"]([^'"]+)['"]\s*,\s*version:\s*(\d+)/g;
75
+ let match;
76
+ while ((match = inlinePattern.exec(code)) !== null) if (match[1] && match[2]) events.push({
77
+ key: match[1],
78
+ version: Number(match[2])
79
+ });
80
+ const refPattern = /\{\s*ref:\s*(\w+)/g;
81
+ while ((match = refPattern.exec(code)) !== null) if (match[1] && !match[1].startsWith("when")) {}
82
+ return events.length > 0 ? events : void 0;
83
+ }
84
+ /**
85
+ * Extract policy refs from operation spec source.
86
+ */
87
+ function extractPolicyRefs(code) {
88
+ const policies = [];
89
+ const policyPattern = /\{\s*key:\s*['"]([^'"]+)['"]\s*,\s*version:\s*(\d+)/g;
90
+ const policySectionMatch = code.match(/policies\s*:\s*\[([\s\S]*?)\]/);
91
+ if (policySectionMatch?.[1]) {
92
+ let match;
93
+ while ((match = policyPattern.exec(policySectionMatch[1])) !== null) if (match[1] && match[2]) policies.push({
94
+ key: match[1],
95
+ version: Number(match[2])
96
+ });
97
+ }
98
+ return policies.length > 0 ? policies : void 0;
99
+ }
100
+ /**
101
+ * Extract test spec refs.
102
+ */
103
+ function extractTestRefs(code) {
104
+ const tests = [];
105
+ const testsSectionMatch = code.match(/tests\s*:\s*\[([\s\S]*?)\]/);
106
+ if (testsSectionMatch?.[1]) {
107
+ const refPattern = /\{\s*key:\s*['"]([^'"]+)['"]\s*,\s*version:\s*(\d+)/g;
108
+ let match;
109
+ while ((match = refPattern.exec(testsSectionMatch[1])) !== null) if (match[1] && match[2]) tests.push({
110
+ key: match[1],
111
+ version: Number(match[2])
112
+ });
113
+ }
114
+ return tests.length > 0 ? tests : void 0;
115
+ }
116
+ function matchStringField(code, field) {
117
+ const regex = /* @__PURE__ */ new RegExp(`${escapeRegex(field)}\\s*:\\s*['"]([^'"]+)['"]`);
118
+ return code.match(regex)?.[1] ?? null;
119
+ }
120
+ function matchNumberField(code, field) {
121
+ const regex = /* @__PURE__ */ new RegExp(`${escapeRegex(field)}\\s*:\\s*(\\d+)`);
122
+ const match = code.match(regex);
123
+ if (!match?.[1]) return null;
124
+ const parsed = Number(match[1]);
125
+ return Number.isFinite(parsed) ? parsed : null;
126
+ }
127
+ function matchStringArrayField(code, field) {
128
+ const regex = /* @__PURE__ */ new RegExp(`${escapeRegex(field)}\\s*:\\s*\\[([\\s\\S]*?)\\]`);
129
+ const match = code.match(regex);
130
+ if (!match?.[1]) return void 0;
131
+ const inner = match[1];
132
+ const items = Array.from(inner.matchAll(/['"]([^'"]+)['"]/g)).map((m) => m[1]).filter((value) => typeof value === "string" && value.length > 0);
133
+ return items.length > 0 ? items : void 0;
134
+ }
135
+ function isStability(value) {
136
+ return value === "experimental" || value === "beta" || value === "stable" || value === "deprecated";
137
+ }
138
+ function escapeRegex(value) {
139
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
140
+ }
141
+ /**
142
+ * Infer operation kind from source code.
143
+ * First checks for defineCommand/defineQuery usage (which set kind automatically),
144
+ * then falls back to explicit kind field.
145
+ */
146
+ function inferOperationKind(code) {
147
+ if (/defineCommand\s*\(/.test(code)) return "command";
148
+ if (/defineQuery\s*\(/.test(code)) return "query";
149
+ const kindRaw = matchStringField(code, "kind");
150
+ return kindRaw === "command" || kindRaw === "query" ? kindRaw : "unknown";
151
+ }
152
+ /**
153
+ * Infer operation kind from a specific code block.
154
+ */
155
+ function inferOperationKindFromBlock(block) {
156
+ if (/defineCommand\s*\(/.test(block)) return "command";
157
+ if (/defineQuery\s*\(/.test(block)) return "query";
158
+ const kindRaw = matchStringField(block, "kind");
159
+ return kindRaw === "command" || kindRaw === "query" ? kindRaw : "unknown";
160
+ }
161
+ /**
162
+ * Extract key and version from a meta block.
163
+ */
164
+ function extractMetaFromBlock(block) {
165
+ const key = matchStringField(block, "key");
166
+ const version = matchNumberField(block, "version");
167
+ if (key && version !== null) return {
168
+ key,
169
+ version
170
+ };
171
+ return null;
172
+ }
173
+ /**
174
+ * Define function patterns for all spec types.
175
+ */
176
+ const DEFINE_FUNCTION_PATTERNS = [
177
+ {
178
+ pattern: /defineCommand\s*\(\s*\{/g,
179
+ type: "operation"
180
+ },
181
+ {
182
+ pattern: /defineQuery\s*\(\s*\{/g,
183
+ type: "operation"
184
+ },
185
+ {
186
+ pattern: /defineEvent\s*\(\s*\{/g,
187
+ type: "event"
188
+ },
189
+ {
190
+ pattern: /:\s*PresentationSpec\s*=\s*\{/g,
191
+ type: "presentation"
192
+ },
193
+ {
194
+ pattern: /:\s*PresentationSpec\s*=\s*\{/g,
195
+ type: "presentation"
196
+ },
197
+ {
198
+ pattern: /:\s*PresentationDescriptor\s*=\s*\{/g,
199
+ type: "presentation"
200
+ },
201
+ {
202
+ pattern: /definePresentation\s*\(\s*\{/g,
203
+ type: "presentation"
204
+ },
205
+ {
206
+ pattern: /defineCapability\s*\(\s*\{/g,
207
+ type: "capability"
208
+ },
209
+ {
210
+ pattern: /defineWorkflow\s*\(\s*\{/g,
211
+ type: "workflow"
212
+ },
213
+ {
214
+ pattern: /defineExperiment\s*\(\s*\{/g,
215
+ type: "experiment"
216
+ },
217
+ {
218
+ pattern: /defineIntegration\s*\(\s*\{/g,
219
+ type: "integration"
220
+ },
221
+ {
222
+ pattern: /defineKnowledge\s*\(\s*\{/g,
223
+ type: "knowledge"
224
+ },
225
+ {
226
+ pattern: /defineTelemetry\s*\(\s*\{/g,
227
+ type: "telemetry"
228
+ },
229
+ {
230
+ pattern: /defineAppConfig\s*\(\s*\{/g,
231
+ type: "app-config"
232
+ },
233
+ {
234
+ pattern: /definePolicy\s*\(\s*\{/g,
235
+ type: "policy"
236
+ },
237
+ {
238
+ pattern: /defineTestSpec\s*\(\s*\{/g,
239
+ type: "test-spec"
240
+ },
241
+ {
242
+ pattern: /defineDataView\s*\(\s*\{/g,
243
+ type: "data-view"
244
+ },
245
+ {
246
+ pattern: /defineForm\s*\(\s*\{/g,
247
+ type: "form"
248
+ },
249
+ {
250
+ pattern: /defineMigration\s*\(\s*\{/g,
251
+ type: "migration"
252
+ }
253
+ ];
254
+ /**
255
+ * Find matching closing brace for an opening brace.
256
+ * Returns the index of the closing brace or -1 if not found.
257
+ */
258
+ function findMatchingBrace(code, startIndex) {
259
+ let depth = 0;
260
+ let inString = false;
261
+ let stringChar = "";
262
+ for (let i = startIndex; i < code.length; i++) {
263
+ const char = code[i];
264
+ const prevChar = i > 0 ? code[i - 1] : "";
265
+ if ((char === "\"" || char === "'" || char === "`") && prevChar !== "\\") {
266
+ if (!inString) {
267
+ inString = true;
268
+ stringChar = char;
269
+ } else if (char === stringChar) inString = false;
270
+ continue;
271
+ }
272
+ if (inString) continue;
273
+ if (char === "{") depth++;
274
+ else if (char === "}") {
275
+ depth--;
276
+ if (depth === 0) return i;
277
+ }
278
+ }
279
+ return -1;
280
+ }
281
+ /**
282
+ * Scan spec source code to extract ALL specs from a file.
283
+ * This function finds multiple spec definitions in a single file.
284
+ */
285
+ function scanAllSpecsFromSource(code, filePath) {
286
+ const results = [];
287
+ const baseSpecType = inferSpecTypeFromFilePath(filePath);
288
+ const processedPositions = /* @__PURE__ */ new Set();
289
+ for (const { pattern, type } of DEFINE_FUNCTION_PATTERNS) {
290
+ pattern.lastIndex = 0;
291
+ let match;
292
+ while ((match = pattern.exec(code)) !== null) {
293
+ const startPos = match.index;
294
+ if (processedPositions.has(startPos)) continue;
295
+ processedPositions.add(startPos);
296
+ const openBracePos = code.indexOf("{", startPos);
297
+ if (openBracePos === -1) continue;
298
+ const closeBracePos = findMatchingBrace(code, openBracePos);
299
+ if (closeBracePos === -1) continue;
300
+ const block = code.slice(openBracePos, closeBracePos + 1);
301
+ const meta = extractMetaFromBlock(block);
302
+ if (!meta) continue;
303
+ const description = matchStringField(block, "description");
304
+ const stabilityRaw = matchStringField(block, "stability");
305
+ const stability = isStability(stabilityRaw) ? stabilityRaw : void 0;
306
+ const owners = matchStringArrayField(block, "owners");
307
+ const tags = matchStringArrayField(block, "tags");
308
+ const hasMeta = /meta\s*:\s*{/.test(block);
309
+ const hasIo = /\bio\s*:\s*{/.test(block);
310
+ const hasPolicy = /\bpolicy\s*:\s*{/.test(block);
311
+ const hasPayload = /\bpayload\s*:\s*{/.test(block);
312
+ const hasContent = /\bcontent\s*:\s*{/.test(block);
313
+ const hasDefinition = /\bdefinition\s*:\s*{/.test(block);
314
+ const kind = type === "operation" ? inferOperationKindFromBlock(block) : "unknown";
315
+ const emittedEvents = type === "operation" ? extractEmittedEvents(block) : void 0;
316
+ const policyRefs = type === "operation" ? extractPolicyRefs(block) : void 0;
317
+ const testRefs = extractTestRefs(block);
318
+ results.push({
319
+ filePath,
320
+ specType: type,
321
+ key: meta.key,
322
+ version: meta.version,
323
+ description: description ?? void 0,
324
+ stability,
325
+ owners,
326
+ tags,
327
+ kind,
328
+ hasMeta,
329
+ hasIo,
330
+ hasPolicy,
331
+ hasPayload,
332
+ hasContent,
333
+ hasDefinition,
334
+ emittedEvents,
335
+ policyRefs,
336
+ testRefs
337
+ });
338
+ }
339
+ }
340
+ if (results.length === 0 && baseSpecType !== "unknown") {
341
+ const fallback = scanSpecSource(code, filePath);
342
+ if (fallback.key && fallback.version !== void 0) results.push(fallback);
343
+ }
344
+ return results;
345
+ }
346
+
347
+ //#endregion
348
+ export { extractEmittedEvents, extractPolicyRefs, extractTestRefs, inferSpecTypeFromFilePath, scanAllSpecsFromSource, scanSpecSource };
349
+ //# sourceMappingURL=spec-scan.js.map