@contractspec/bundle.workspace 1.44.1 → 1.45.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.
- package/README.md +5 -1
- package/dist/_virtual/rolldown_runtime.js +1 -19
- package/dist/ai/agents/cursor-agent.js +4 -4
- package/dist/ai/agents/cursor-agent.js.map +1 -1
- package/dist/ai/client.d.ts +14 -0
- package/dist/ai/client.d.ts.map +1 -1
- package/dist/ai/client.js +27 -1
- package/dist/ai/client.js.map +1 -1
- package/dist/ai/index.d.ts +1 -9
- package/dist/ai/index.js +1 -20
- package/dist/ai/prompts/index.js +1 -1
- package/dist/index.d.ts +42 -19
- package/dist/index.js +39 -150
- package/dist/services/agent-guide/agent-guide-service.js +3 -3
- package/dist/services/agent-guide/agent-guide-service.js.map +1 -1
- package/dist/services/ci-check/ci-check-service.d.ts.map +1 -1
- package/dist/services/ci-check/ci-check-service.js +69 -2
- package/dist/services/ci-check/ci-check-service.js.map +1 -1
- package/dist/services/ci-check/types.d.ts +1 -1
- package/dist/services/ci-check/types.d.ts.map +1 -1
- package/dist/services/ci-check/types.js +4 -2
- package/dist/services/ci-check/types.js.map +1 -1
- package/dist/services/config.d.ts +1 -11
- package/dist/services/config.d.ts.map +1 -1
- package/dist/services/config.js +2 -16
- package/dist/services/config.js.map +1 -1
- package/dist/services/create/ai-generator.d.ts +84 -0
- package/dist/services/create/ai-generator.d.ts.map +1 -0
- package/dist/services/create/ai-generator.js +178 -0
- package/dist/services/create/ai-generator.js.map +1 -0
- package/dist/services/create/index.d.ts +27 -0
- package/dist/services/create/index.d.ts.map +1 -0
- package/dist/services/create/index.js +36 -0
- package/dist/services/create/index.js.map +1 -0
- package/dist/services/create/templates.d.ts +21 -0
- package/dist/services/create/templates.d.ts.map +1 -0
- package/dist/services/create/templates.js +37 -0
- package/dist/services/create/templates.js.map +1 -0
- package/dist/services/docs/docs-service.d.ts +19 -0
- package/dist/services/docs/docs-service.d.ts.map +1 -0
- package/dist/services/docs/docs-service.js +41 -0
- package/dist/services/docs/docs-service.js.map +1 -0
- package/dist/services/docs/index.d.ts +1 -0
- package/dist/services/docs/index.js +1 -0
- package/dist/services/doctor/checks/cli.js +3 -3
- package/dist/services/doctor/checks/cli.js.map +1 -1
- package/dist/services/doctor/checks/index.js +1 -0
- package/dist/services/doctor/checks/layers.js +139 -0
- package/dist/services/doctor/checks/layers.js.map +1 -0
- package/dist/services/doctor/doctor-service.d.ts.map +1 -1
- package/dist/services/doctor/doctor-service.js +2 -0
- package/dist/services/doctor/doctor-service.js.map +1 -1
- package/dist/services/doctor/types.d.ts +1 -1
- package/dist/services/doctor/types.d.ts.map +1 -1
- package/dist/services/doctor/types.js +4 -2
- package/dist/services/doctor/types.js.map +1 -1
- package/dist/services/formatter.d.ts +15 -0
- package/dist/services/formatter.d.ts.map +1 -0
- package/dist/services/formatter.js +26 -0
- package/dist/services/formatter.js.map +1 -0
- package/dist/services/impact/formatters.d.ts +5 -5
- package/dist/services/impact/formatters.d.ts.map +1 -1
- package/dist/services/impact/formatters.js.map +1 -1
- package/dist/services/impact/impact-detection-service.js +6 -6
- package/dist/services/impact/impact-detection-service.js.map +1 -1
- package/dist/services/impact/types.d.ts +3 -3
- package/dist/services/implementation/resolver.js +1 -1
- package/dist/services/implementation/resolver.js.map +1 -1
- package/dist/services/implementation/types.d.ts +1 -1
- package/dist/services/index.d.ts +31 -5
- package/dist/services/index.js +30 -4
- package/dist/services/integrity-diagram.js +1 -1
- package/dist/services/integrity-diagram.js.map +1 -1
- package/dist/services/integrity.d.ts +1 -1
- package/dist/services/integrity.js.map +1 -1
- package/dist/services/layer-discovery.d.ts +77 -0
- package/dist/services/layer-discovery.d.ts.map +1 -0
- package/dist/services/layer-discovery.js +121 -0
- package/dist/services/layer-discovery.js.map +1 -0
- package/dist/services/llm/index.d.ts +28 -0
- package/dist/services/llm/index.d.ts.map +1 -0
- package/dist/services/llm/index.js +187 -0
- package/dist/services/llm/index.js.map +1 -0
- package/dist/services/llm/verify-static.d.ts +26 -0
- package/dist/services/llm/verify-static.d.ts.map +1 -0
- package/dist/services/llm/verify-static.js +82 -0
- package/dist/services/llm/verify-static.js.map +1 -0
- package/dist/services/openapi/import-service.d.ts.map +1 -1
- package/dist/services/openapi/import-service.js +98 -4
- package/dist/services/openapi/import-service.js.map +1 -1
- package/dist/services/openapi/sync-service.js +1 -1
- package/dist/services/setup/config-generators.js +1 -1
- package/dist/services/setup/config-generators.js.map +1 -1
- package/dist/services/sync.d.ts +2 -1
- package/dist/services/sync.d.ts.map +1 -1
- package/dist/services/sync.js +2 -1
- package/dist/services/sync.js.map +1 -1
- package/dist/services/test/index.d.ts +1 -0
- package/dist/services/test/index.js +1 -0
- package/dist/services/test/test-service.d.ts +22 -0
- package/dist/services/test/test-service.d.ts.map +1 -0
- package/dist/services/test/test-service.js +81 -0
- package/dist/services/test/test-service.js.map +1 -0
- package/dist/services/validate/blueprint-validator.d.ts +23 -0
- package/dist/services/validate/blueprint-validator.d.ts.map +1 -0
- package/dist/services/validate/blueprint-validator.js +50 -0
- package/dist/services/validate/blueprint-validator.js.map +1 -0
- package/dist/services/validate/implementation-agent-validator.d.ts +20 -0
- package/dist/services/validate/implementation-agent-validator.d.ts.map +1 -0
- package/dist/services/validate/implementation-agent-validator.js +42 -0
- package/dist/services/validate/implementation-agent-validator.js.map +1 -0
- package/dist/services/{validate-implementation.d.ts → validate/implementation-validator.d.ts} +3 -3
- package/dist/services/validate/implementation-validator.d.ts.map +1 -0
- package/dist/services/{validate-implementation.js → validate/implementation-validator.js} +2 -2
- package/dist/services/validate/implementation-validator.js.map +1 -0
- package/dist/services/validate/index.d.ts +5 -0
- package/dist/services/validate/index.js +5 -0
- package/dist/services/{validate.d.ts → validate/spec-validator.d.ts} +5 -4
- package/dist/services/validate/spec-validator.d.ts.map +1 -0
- package/dist/services/{validate.js → validate/spec-validator.js} +6 -4
- package/dist/services/validate/spec-validator.js.map +1 -0
- package/dist/services/validate/tenant-validator.d.ts +21 -0
- package/dist/services/validate/tenant-validator.d.ts.map +1 -0
- package/dist/services/validate/tenant-validator.js +165 -0
- package/dist/services/validate/tenant-validator.js.map +1 -0
- package/dist/services/watch.js +2 -1
- package/dist/services/watch.js.map +1 -1
- package/dist/templates/data-view.template.js +3 -3
- package/dist/templates/data-view.template.js.map +1 -1
- package/dist/templates/event.template.js +3 -1
- package/dist/templates/event.template.js.map +1 -1
- package/dist/templates/operation.template.js +3 -1
- package/dist/templates/operation.template.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/utils/module-loader.js +41 -0
- package/dist/utils/module-loader.js.map +1 -0
- package/package.json +9 -9
- package/dist/ai/index.d.ts.map +0 -1
- package/dist/ai/index.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/services/test.d.ts +0 -15
- package/dist/services/test.d.ts.map +0 -1
- package/dist/services/test.js +0 -30
- package/dist/services/test.js.map +0 -1
- package/dist/services/validate-implementation.d.ts.map +0 -1
- package/dist/services/validate-implementation.js.map +0 -1
- package/dist/services/validate.d.ts.map +0 -1
- package/dist/services/validate.js.map +0 -1
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { FsAdapter } from "../../ports/fs.js";
|
|
2
|
+
import { AppBlueprintSpec, TenantAppConfig, validateConfig } from "@contractspec/lib.contracts";
|
|
3
|
+
|
|
4
|
+
//#region src/services/validate/tenant-validator.d.ts
|
|
5
|
+
interface TenantValidationResult {
|
|
6
|
+
config?: TenantAppConfig;
|
|
7
|
+
report?: ReturnType<typeof validateConfig>;
|
|
8
|
+
valid: boolean;
|
|
9
|
+
errors: string[];
|
|
10
|
+
}
|
|
11
|
+
interface TenantValidationContext {
|
|
12
|
+
connections?: string[] | string;
|
|
13
|
+
integrationRegistrars?: string[] | string;
|
|
14
|
+
translationCatalog?: string;
|
|
15
|
+
}
|
|
16
|
+
declare function validateTenantConfig(blueprint: AppBlueprintSpec, tenantPath: string, contextOptions: TenantValidationContext, adapters: {
|
|
17
|
+
fs: FsAdapter;
|
|
18
|
+
}): Promise<TenantValidationResult>;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { TenantValidationContext, TenantValidationResult, validateTenantConfig };
|
|
21
|
+
//# sourceMappingURL=tenant-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-validator.d.ts","names":[],"sources":["../../../src/services/validate/tenant-validator.ts"],"sourcesContent":[],"mappings":";;;;UAaiB,sBAAA;WACN;EADM,MAAA,CAAA,EAEN,UAFM,CAAA,OAEY,cAFU,CAAA;EAC5B,KAAA,EAAA,OAAA;EACkB,MAAA,EAAA,MAAA,EAAA;;AAAR,UAKJ,uBAAA,CALI;EAKJ,WAAA,CAAA,EAAA,MAAA,EAAA,GAAA,MAAuB;EAMlB,qBAAA,CAAA,EAAoB,MAAA,EAAA,GAAA,MAAA;EAC7B,kBAAA,CAAA,EAAA,MAAA;;AAGK,iBAJI,oBAAA,CAIJ,SAAA,EAHL,gBAGK,EAAA,UAAA,EAAA,MAAA,EAAA,cAAA,EADA,uBACA,EAAA,QAAA,EAAA;EACP,EAAA,EADO,SACP;CAAR,CAAA,EAAA,OAAA,CAAQ,sBAAR,CAAA"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { resolve } from "path";
|
|
2
|
+
import { pathToFileURL } from "url";
|
|
3
|
+
import { validateConfig } from "@contractspec/lib.contracts";
|
|
4
|
+
import { readFile } from "fs/promises";
|
|
5
|
+
|
|
6
|
+
//#region src/services/validate/tenant-validator.ts
|
|
7
|
+
async function validateTenantConfig(blueprint, tenantPath, contextOptions, adapters) {
|
|
8
|
+
const { fs } = adapters;
|
|
9
|
+
const resolvedPath = resolve(process.cwd(), tenantPath);
|
|
10
|
+
if (!await fs.exists(resolvedPath)) return {
|
|
11
|
+
valid: false,
|
|
12
|
+
errors: [`Tenant config file not found: ${resolvedPath}`]
|
|
13
|
+
};
|
|
14
|
+
try {
|
|
15
|
+
const tenant = await loadTenantConfig(resolvedPath);
|
|
16
|
+
const connections = await loadIntegrationConnections(contextOptions.connections, fs);
|
|
17
|
+
const catalog = await loadTranslationCatalog(contextOptions.translationCatalog, fs);
|
|
18
|
+
const integrationSpecs = await loadIntegrationRegistrars(contextOptions.integrationRegistrars);
|
|
19
|
+
const context = {};
|
|
20
|
+
if (connections.length > 0) context.tenantConnections = connections;
|
|
21
|
+
if (catalog) context.translationCatalogs = {
|
|
22
|
+
blueprint: [catalog],
|
|
23
|
+
platform: []
|
|
24
|
+
};
|
|
25
|
+
if (integrationSpecs) context.integrationSpecs = integrationSpecs;
|
|
26
|
+
const report = validateConfig(blueprint, tenant, context);
|
|
27
|
+
return {
|
|
28
|
+
config: tenant,
|
|
29
|
+
report,
|
|
30
|
+
valid: report.valid,
|
|
31
|
+
errors: report.errors.map((e) => `[${e.code}] ${e.path}: ${e.message}`)
|
|
32
|
+
};
|
|
33
|
+
} catch (error) {
|
|
34
|
+
return {
|
|
35
|
+
valid: false,
|
|
36
|
+
errors: [error instanceof Error ? error.message : String(error)]
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function loadTenantConfig(tenantPath) {
|
|
41
|
+
if (tenantPath.endsWith(".json")) {
|
|
42
|
+
const raw = await readFile(tenantPath, "utf-8");
|
|
43
|
+
const json = JSON.parse(raw);
|
|
44
|
+
if (!isTenantConfig(json)) throw new Error("Tenant config JSON does not match the expected structure (missing meta).");
|
|
45
|
+
return json;
|
|
46
|
+
}
|
|
47
|
+
const mod = await loadModule(tenantPath);
|
|
48
|
+
const candidates = Object.values(mod).filter(isTenantConfig);
|
|
49
|
+
if (candidates.length === 0) throw new Error("Tenant config module does not export a TenantAppConfig.");
|
|
50
|
+
return candidates[0];
|
|
51
|
+
}
|
|
52
|
+
function isTenantConfig(value) {
|
|
53
|
+
return typeof value === "object" && value !== null && "meta" in value && typeof value.meta?.tenantId === "string";
|
|
54
|
+
}
|
|
55
|
+
async function loadModule(modulePath) {
|
|
56
|
+
try {
|
|
57
|
+
return await import(pathToFileURL(modulePath).href);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
throw new Error(`Failed to load module at ${modulePath}: ${error}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function normalizePathOption(value) {
|
|
63
|
+
if (!value) return [];
|
|
64
|
+
return (Array.isArray(value) ? value : value.split(",")).map((entry) => entry.trim()).filter(Boolean);
|
|
65
|
+
}
|
|
66
|
+
async function loadIntegrationConnections(value, fs) {
|
|
67
|
+
const paths = normalizePathOption(value);
|
|
68
|
+
if (!paths.length) return [];
|
|
69
|
+
const results = [];
|
|
70
|
+
for (const path of paths) {
|
|
71
|
+
const resolved = resolve(process.cwd(), path);
|
|
72
|
+
if (!await fs.exists(resolved)) {
|
|
73
|
+
console.warn(`Warning: Connection file not found: ${resolved}`);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (resolved.endsWith(".json")) {
|
|
77
|
+
const raw = await readFile(resolved, "utf-8");
|
|
78
|
+
const parsed = JSON.parse(raw);
|
|
79
|
+
results.push(...collectConnections(parsed));
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const mod = await loadModule(resolved);
|
|
83
|
+
results.push(...collectConnections(mod));
|
|
84
|
+
}
|
|
85
|
+
return results;
|
|
86
|
+
}
|
|
87
|
+
function collectConnections(value) {
|
|
88
|
+
if (Array.isArray(value)) {
|
|
89
|
+
const connections = value.filter(isIntegrationConnection);
|
|
90
|
+
if (connections.length) return connections;
|
|
91
|
+
}
|
|
92
|
+
if (isIntegrationConnection(value)) return [value];
|
|
93
|
+
if (value && typeof value === "object") {
|
|
94
|
+
const collected = Object.values(value).flatMap((entry) => collectConnections(entry));
|
|
95
|
+
if (collected.length) return collected;
|
|
96
|
+
}
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
function isIntegrationConnection(value) {
|
|
100
|
+
return typeof value === "object" && value !== null && "meta" in value && typeof value.meta?.id === "string" && typeof value.secretRef === "string";
|
|
101
|
+
}
|
|
102
|
+
async function loadTranslationCatalog(path, fs) {
|
|
103
|
+
if (!path) return void 0;
|
|
104
|
+
const resolved = resolve(process.cwd(), path);
|
|
105
|
+
if (!await fs.exists(resolved)) return void 0;
|
|
106
|
+
if (resolved.endsWith(".json")) {
|
|
107
|
+
const raw = await readFile(resolved, "utf-8");
|
|
108
|
+
const parsed = JSON.parse(raw);
|
|
109
|
+
if (isBlueprintTranslationCatalog(parsed)) return normaliseTranslationCatalog(parsed);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const mod = await loadModule(resolved);
|
|
113
|
+
const catalogs = Object.values(mod).filter(isBlueprintTranslationCatalog);
|
|
114
|
+
if (catalogs.length === 0) return void 0;
|
|
115
|
+
return normaliseTranslationCatalog(catalogs[0]);
|
|
116
|
+
}
|
|
117
|
+
function isBlueprintTranslationCatalog(value) {
|
|
118
|
+
return typeof value === "object" && value !== null && "meta" in value && typeof value.meta?.key === "string" && typeof value.meta?.version === "number" && Array.isArray(value.entries);
|
|
119
|
+
}
|
|
120
|
+
function normaliseTranslationCatalog(catalog) {
|
|
121
|
+
const supportedLocales = catalog.supportedLocales && catalog.supportedLocales.length > 0 ? catalog.supportedLocales : [catalog.defaultLocale];
|
|
122
|
+
return {
|
|
123
|
+
...catalog,
|
|
124
|
+
supportedLocales
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
async function loadIntegrationRegistrars(value) {
|
|
128
|
+
const entries = normalizePathOption(value);
|
|
129
|
+
if (!entries.length) return void 0;
|
|
130
|
+
const { IntegrationSpecRegistry } = await import("@contractspec/lib.contracts");
|
|
131
|
+
const registry = new IntegrationSpecRegistry();
|
|
132
|
+
for (const entry of entries) {
|
|
133
|
+
const { modulePath, exportName } = parseRegistrarEntry(entry);
|
|
134
|
+
if (!modulePath) continue;
|
|
135
|
+
const resolved = resolve(process.cwd(), modulePath);
|
|
136
|
+
try {
|
|
137
|
+
const registrar = pickRegistrar(await loadModule(resolved), exportName);
|
|
138
|
+
if (registrar) await registrar(registry);
|
|
139
|
+
} catch (e) {
|
|
140
|
+
console.warn(`Failed to load registrar from ${resolved}: ${e}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return registry;
|
|
144
|
+
}
|
|
145
|
+
function parseRegistrarEntry(entry) {
|
|
146
|
+
if (!entry) return { modulePath: null };
|
|
147
|
+
const [modulePathRaw, exportNameRaw] = entry.split("#");
|
|
148
|
+
return {
|
|
149
|
+
modulePath: modulePathRaw?.trim() ?? null,
|
|
150
|
+
exportName: exportNameRaw?.trim()
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
function pickRegistrar(mod, exportName) {
|
|
154
|
+
if (exportName) {
|
|
155
|
+
const candidate = mod[exportName];
|
|
156
|
+
if (typeof candidate === "function") return candidate;
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (typeof mod.default === "function") return mod.default;
|
|
160
|
+
for (const value of Object.values(mod)) if (typeof value === "function") return value;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
//#endregion
|
|
164
|
+
export { validateTenantConfig };
|
|
165
|
+
//# sourceMappingURL=tenant-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-validator.js","names":["context: Parameters<typeof validateTenantConfigSpecs>[2]","validateTenantConfigSpecs","results: IntegrationConnection[]"],"sources":["../../../src/services/validate/tenant-validator.ts"],"sourcesContent":["import { resolve } from 'path';\nimport { readFile } from 'fs/promises';\nimport { pathToFileURL } from 'url';\nimport {\n validateConfig as validateTenantConfigSpecs,\n type AppBlueprintSpec,\n type TenantAppConfig,\n type IntegrationSpecRegistry,\n type BlueprintTranslationCatalog,\n type IntegrationConnection,\n} from '@contractspec/lib.contracts';\nimport type { FsAdapter } from '../../ports/fs';\n\nexport interface TenantValidationResult {\n config?: TenantAppConfig;\n report?: ReturnType<typeof validateTenantConfigSpecs>;\n valid: boolean;\n errors: string[];\n}\n\nexport interface TenantValidationContext {\n connections?: string[] | string;\n integrationRegistrars?: string[] | string;\n translationCatalog?: string;\n}\n\nexport async function validateTenantConfig(\n blueprint: AppBlueprintSpec,\n tenantPath: string,\n contextOptions: TenantValidationContext,\n adapters: { fs: FsAdapter }\n): Promise<TenantValidationResult> {\n const { fs } = adapters;\n const resolvedPath = resolve(process.cwd(), tenantPath);\n\n if (!(await fs.exists(resolvedPath))) {\n return {\n valid: false,\n errors: [`Tenant config file not found: ${resolvedPath}`],\n };\n }\n\n try {\n const tenant = await loadTenantConfig(resolvedPath);\n const connections = await loadIntegrationConnections(\n contextOptions.connections,\n fs\n );\n const catalog = await loadTranslationCatalog(\n contextOptions.translationCatalog,\n fs\n );\n const integrationSpecs = await loadIntegrationRegistrars(\n contextOptions.integrationRegistrars\n );\n\n const context: Parameters<typeof validateTenantConfigSpecs>[2] = {};\n if (connections.length > 0) {\n context.tenantConnections = connections;\n }\n if (catalog) {\n context.translationCatalogs = {\n blueprint: [catalog],\n platform: [],\n };\n }\n if (integrationSpecs) {\n context.integrationSpecs = integrationSpecs;\n }\n\n const report = validateTenantConfigSpecs(blueprint, tenant, context);\n\n return {\n config: tenant,\n report,\n valid: report.valid,\n errors: report.errors.map((e) => `[${e.code}] ${e.path}: ${e.message}`),\n };\n } catch (error) {\n return {\n valid: false,\n errors: [error instanceof Error ? error.message : String(error)],\n };\n }\n}\n\n// Helpers\n\nasync function loadTenantConfig(tenantPath: string): Promise<TenantAppConfig> {\n if (tenantPath.endsWith('.json')) {\n const raw = await readFile(tenantPath, 'utf-8');\n const json = JSON.parse(raw);\n if (!isTenantConfig(json)) {\n throw new Error(\n 'Tenant config JSON does not match the expected structure (missing meta).'\n );\n }\n return json;\n }\n\n const mod = await loadModule(tenantPath);\n const candidates = Object.values(mod).filter(isTenantConfig);\n if (candidates.length === 0) {\n throw new Error('Tenant config module does not export a TenantAppConfig.');\n }\n return candidates[0] as TenantAppConfig;\n}\n\nfunction isTenantConfig(value: unknown): value is TenantAppConfig {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'meta' in value &&\n typeof (value as TenantAppConfig).meta?.tenantId === 'string'\n );\n}\n\n// Basic module loader\nasync function loadModule(\n modulePath: string\n): Promise<Record<string, unknown>> {\n try {\n const url = pathToFileURL(modulePath).href;\n const mod = await import(url);\n return mod;\n } catch (error) {\n throw new Error(`Failed to load module at ${modulePath}: ${error}`);\n }\n}\n\n// --- Connection Loaders ---\n\nfunction normalizePathOption(value?: string | string[]): string[] {\n if (!value) return [];\n const values = Array.isArray(value) ? value : value.split(',');\n return values.map((entry) => entry.trim()).filter(Boolean);\n}\n\nasync function loadIntegrationConnections(\n value: string | string[] | undefined,\n fs: FsAdapter\n): Promise<IntegrationConnection[]> {\n const paths = normalizePathOption(value);\n if (!paths.length) return [];\n\n const results: IntegrationConnection[] = [];\n for (const path of paths) {\n const resolved = resolve(process.cwd(), path);\n if (!(await fs.exists(resolved))) {\n console.warn(`Warning: Connection file not found: ${resolved}`);\n continue;\n }\n\n if (resolved.endsWith('.json')) {\n const raw = await readFile(resolved, 'utf-8');\n const parsed = JSON.parse(raw);\n results.push(...collectConnections(parsed));\n continue;\n }\n\n const mod = await loadModule(resolved);\n results.push(...collectConnections(mod));\n }\n return results;\n}\n\nfunction collectConnections(value: unknown): IntegrationConnection[] {\n if (Array.isArray(value)) {\n const connections = value.filter(isIntegrationConnection);\n if (connections.length) return connections;\n }\n if (isIntegrationConnection(value)) {\n return [value];\n }\n if (value && typeof value === 'object') {\n const entries = Object.values(value as Record<string, unknown>);\n const collected = entries.flatMap((entry) => collectConnections(entry));\n if (collected.length) return collected;\n }\n return [];\n}\n\nfunction isIntegrationConnection(\n value: unknown\n): value is IntegrationConnection {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'meta' in value &&\n typeof (value as IntegrationConnection).meta?.id === 'string' &&\n typeof (value as IntegrationConnection).secretRef === 'string'\n );\n}\n\n// --- Translation Catalog Loaders ---\n\nasync function loadTranslationCatalog(\n path: string | undefined,\n fs: FsAdapter\n): Promise<BlueprintTranslationCatalog | undefined> {\n if (!path) return undefined;\n const resolved = resolve(process.cwd(), path);\n if (!(await fs.exists(resolved))) return undefined;\n\n if (resolved.endsWith('.json')) {\n const raw = await readFile(resolved, 'utf-8');\n const parsed = JSON.parse(raw);\n if (isBlueprintTranslationCatalog(parsed)) {\n return normaliseTranslationCatalog(parsed);\n }\n return undefined;\n }\n\n const mod = await loadModule(resolved);\n const catalogs = Object.values(mod).filter(isBlueprintTranslationCatalog);\n if (catalogs.length === 0) return undefined;\n return normaliseTranslationCatalog(\n catalogs[0] as BlueprintTranslationCatalog\n );\n}\n\nfunction isBlueprintTranslationCatalog(\n value: unknown\n): value is BlueprintTranslationCatalog {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'meta' in value &&\n typeof (value as BlueprintTranslationCatalog).meta?.key === 'string' &&\n typeof (value as BlueprintTranslationCatalog).meta?.version === 'number' &&\n Array.isArray((value as BlueprintTranslationCatalog).entries)\n );\n}\n\nfunction normaliseTranslationCatalog(\n catalog: BlueprintTranslationCatalog\n): BlueprintTranslationCatalog {\n const supportedLocales =\n catalog.supportedLocales && catalog.supportedLocales.length > 0\n ? catalog.supportedLocales\n : [catalog.defaultLocale];\n return {\n ...catalog,\n supportedLocales,\n };\n}\n\n// --- Registrar Loaders ---\n// Important: This needs IntegrationSpecRegistry which is a Class.\n// We only import type in signature, but need constructor.\n// Imports fixed at top.\n\nasync function loadIntegrationRegistrars(\n value?: string | string[]\n): Promise<IntegrationSpecRegistry | undefined> {\n const entries = normalizePathOption(value);\n if (!entries.length) return undefined;\n\n // We need to import the Class dynamically or have it available.\n // It is imported from @contractspec/lib.contracts\n const { IntegrationSpecRegistry } =\n await import('@contractspec/lib.contracts');\n const registry = new IntegrationSpecRegistry();\n\n for (const entry of entries) {\n const { modulePath, exportName } = parseRegistrarEntry(entry);\n if (!modulePath) continue;\n const resolved = resolve(process.cwd(), modulePath);\n // Logic simplified for brevity, assume module exists or handled by catch in loadModule\n try {\n const mod = await loadModule(resolved);\n const registrar = pickRegistrar(mod, exportName);\n if (registrar) {\n await registrar(registry);\n }\n } catch (e) {\n console.warn(`Failed to load registrar from ${resolved}: ${e}`);\n }\n }\n return registry;\n}\n\nfunction parseRegistrarEntry(entry: string): {\n modulePath: string | null;\n exportName?: string;\n} {\n if (!entry) return { modulePath: null };\n const [modulePathRaw, exportNameRaw] = entry.split('#');\n const modulePath = modulePathRaw?.trim() ?? null;\n const exportName = exportNameRaw?.trim();\n return { modulePath, exportName };\n}\n\nfunction pickRegistrar(\n mod: Record<string, unknown>,\n exportName?: string\n): ((registry: IntegrationSpecRegistry) => void | Promise<void>) | undefined {\n if (exportName) {\n const candidate = mod[exportName];\n if (typeof candidate === 'function') {\n return candidate as (\n registry: IntegrationSpecRegistry\n ) => void | Promise<void>;\n }\n return undefined;\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (typeof (mod as any).default === 'function') {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (mod as any).default;\n }\n for (const value of Object.values(mod)) {\n if (typeof value === 'function') {\n return value as (\n registry: IntegrationSpecRegistry\n ) => void | Promise<void>;\n }\n }\n return undefined;\n}\n"],"mappings":"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"}
|
package/dist/services/watch.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watch.js","names":["validateFn: WatchValidateFn","buildFn: WatchBuildFn"],"sources":["../../src/services/watch.ts"],"sourcesContent":["/**\n * Watch service.\n *\n * Provides a reusable watcher that can trigger validate/build on spec changes.\n */\n\nimport type { WorkspaceConfig } from '@contractspec/module.workspace';\nimport type { LoggerAdapter } from '../ports/logger';\nimport type { Watcher, WatcherAdapter, WatchOptions } from '../ports/watcher';\nimport type { FsAdapter } from '../ports/fs';\nimport { buildSpec } from './build';\nimport { validateSpec } from './validate';\n\nexport interface WatchSpecsOptions extends WatchOptions {\n runValidate?: boolean;\n runBuild?: boolean;\n dryRun?: boolean;\n}\n\nexport type WatchBuildFn = (specPath: string) => Promise<void>;\nexport type WatchValidateFn = (specPath: string) => Promise<void>;\n\nexport function watchSpecs(\n adapters: { watcher: WatcherAdapter; fs: FsAdapter; logger: LoggerAdapter },\n config: WorkspaceConfig,\n options: WatchSpecsOptions,\n overrides?: {\n validate?: WatchValidateFn;\n build?: WatchBuildFn;\n }\n): Watcher {\n const { watcher, fs, logger } = adapters;\n const w = watcher.watch(options);\n\n const validateFn: WatchValidateFn =\n overrides?.validate ??\n (async (specPath) => {\n await validateSpec(specPath, { fs, logger });\n });\n\n const buildFn: WatchBuildFn =\n overrides?.build ??\n (async (specPath) => {\n await buildSpec(specPath, { fs, logger }, config);\n });\n\n w.on(async (event) => {\n if (event.type !== 'change') return;\n\n logger.info('watchSpecs.changed', { path: event.path });\n\n if (options.runValidate) {\n await validateFn(event.path);\n }\n\n if (options.runBuild) {\n if (options.dryRun) {\n logger.info('[dry-run] watchSpecs skipped build', { path: event.path });\n } else {\n await buildFn(event.path);\n }\n }\n });\n\n return w;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"watch.js","names":["validateFn: WatchValidateFn","buildFn: WatchBuildFn"],"sources":["../../src/services/watch.ts"],"sourcesContent":["/**\n * Watch service.\n *\n * Provides a reusable watcher that can trigger validate/build on spec changes.\n */\n\nimport type { WorkspaceConfig } from '@contractspec/module.workspace';\nimport type { LoggerAdapter } from '../ports/logger';\nimport type { Watcher, WatcherAdapter, WatchOptions } from '../ports/watcher';\nimport type { FsAdapter } from '../ports/fs';\nimport { buildSpec } from './build';\nimport { validateSpec } from './validate';\n\nexport interface WatchSpecsOptions extends WatchOptions {\n runValidate?: boolean;\n runBuild?: boolean;\n dryRun?: boolean;\n}\n\nexport type WatchBuildFn = (specPath: string) => Promise<void>;\nexport type WatchValidateFn = (specPath: string) => Promise<void>;\n\nexport function watchSpecs(\n adapters: { watcher: WatcherAdapter; fs: FsAdapter; logger: LoggerAdapter },\n config: WorkspaceConfig,\n options: WatchSpecsOptions,\n overrides?: {\n validate?: WatchValidateFn;\n build?: WatchBuildFn;\n }\n): Watcher {\n const { watcher, fs, logger } = adapters;\n const w = watcher.watch(options);\n\n const validateFn: WatchValidateFn =\n overrides?.validate ??\n (async (specPath) => {\n await validateSpec(specPath, { fs, logger });\n });\n\n const buildFn: WatchBuildFn =\n overrides?.build ??\n (async (specPath) => {\n await buildSpec(specPath, { fs, logger }, config);\n });\n\n w.on(async (event) => {\n if (event.type !== 'change') return;\n\n logger.info('watchSpecs.changed', { path: event.path });\n\n if (options.runValidate) {\n await validateFn(event.path);\n }\n\n if (options.runBuild) {\n if (options.dryRun) {\n logger.info('[dry-run] watchSpecs skipped build', { path: event.path });\n } else {\n await buildFn(event.path);\n }\n }\n });\n\n return w;\n}\n"],"mappings":";;;;;AAsBA,SAAgB,WACd,UACA,QACA,SACA,WAIS;CACT,MAAM,EAAE,SAAS,IAAI,WAAW;CAChC,MAAM,IAAI,QAAQ,MAAM,QAAQ;CAEhC,MAAMA,aACJ,WAAW,aACV,OAAO,aAAa;AACnB,QAAM,aAAa,UAAU;GAAE;GAAI;GAAQ,CAAC;;CAGhD,MAAMC,UACJ,WAAW,UACV,OAAO,aAAa;AACnB,QAAM,UAAU,UAAU;GAAE;GAAI;GAAQ,EAAE,OAAO;;AAGrD,GAAE,GAAG,OAAO,UAAU;AACpB,MAAI,MAAM,SAAS,SAAU;AAE7B,SAAO,KAAK,sBAAsB,EAAE,MAAM,MAAM,MAAM,CAAC;AAEvD,MAAI,QAAQ,YACV,OAAM,WAAW,MAAM,KAAK;AAG9B,MAAI,QAAQ,SACV,KAAI,QAAQ,OACV,QAAO,KAAK,sCAAsC,EAAE,MAAM,MAAM,MAAM,CAAC;MAEvE,OAAM,QAAQ,MAAM,KAAK;GAG7B;AAEF,QAAO"}
|
|
@@ -32,7 +32,7 @@ export const ${viewVarName}: DataViewSpec = {
|
|
|
32
32
|
},
|
|
33
33
|
${itemOperation}
|
|
34
34
|
refreshEvents: [
|
|
35
|
-
// { name: 'entity.updated', version: 1 },
|
|
35
|
+
// { name: 'entity.updated', version: '1.0.0' },
|
|
36
36
|
],
|
|
37
37
|
},
|
|
38
38
|
view: {
|
|
@@ -52,8 +52,8 @@ ${fields}
|
|
|
52
52
|
],
|
|
53
53
|
},
|
|
54
54
|
states: {
|
|
55
|
-
// empty: { name: 'app.data.empty', version: 1 },
|
|
56
|
-
// error: { name: 'app.data.error', version: 1 },
|
|
55
|
+
// empty: { name: 'app.data.empty', version: '1.0.0' },
|
|
56
|
+
// error: { name: 'app.data.error', version: '1.0.0' },
|
|
57
57
|
},
|
|
58
58
|
};
|
|
59
59
|
`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-view.template.js","names":[],"sources":["../../src/templates/data-view.template.ts"],"sourcesContent":["import type { DataViewSpecData } from '../types';\n\nexport function generateDataViewSpec(data: DataViewSpecData): string {\n const viewVarName =\n toPascalCase(data.name.split('.').pop() ?? 'DataView') + 'DataView';\n\n const fields = data.fields\n .map(\n (field) => ` {\n key: '${field.key}',\n label: '${escape(field.label)}',\n dataPath: '${field.dataPath}',\n ${field.format ? `format: '${field.format}',` : ''}\n ${field.sortable ? 'sortable: true,' : ''}\n ${field.filterable ? 'filterable: true,' : ''}\n }`\n )\n .join(',\\n');\n\n const secondaryFields = data.secondaryFields?.length\n ? `secondaryFields: [${data.secondaryFields\n .map((key) => `'${key}'`)\n .join(', ')}],`\n : '';\n\n const itemOperation = data.itemOperation\n ? `item: { name: '${data.itemOperation.name}', version: ${data.itemOperation.version} },`\n : '';\n\n return `import type { DataViewSpec } from '@contractspec/lib.contracts/data-views';\n\nexport const ${viewVarName}: DataViewSpec = {\n meta: {\n key: '${data.name}',\n version: ${data.version},\n entity: '${data.entity}',\n title: '${escape(data.title)}',\n description: '${escape(\n data.description || 'Describe the purpose of this data view.'\n )}',\n domain: '${escape(data.domain || data.entity)}',\n owners: [${data.owners.map((owner) => `'${owner}'`).join(', ')}],\n tags: [${data.tags.map((tag) => `'${tag}'`).join(', ')}],\n stability: '${data.stability}',\n },\n source: {\n primary: {\n name: '${data.primaryOperation.name}',\n version: ${data.primaryOperation.version},\n },\n ${itemOperation}\n refreshEvents: [\n // { name: 'entity.updated', version: 1 },\n ],\n },\n view: {\n kind: '${data.kind}',\n fields: [\n${fields}\n ],\n ${data.primaryField ? `primaryField: '${data.primaryField}',` : ''}\n ${secondaryFields}\n filters: [\n // Example filter:\n // { key: 'search', label: 'Search', field: 'fullName', type: 'search' },\n ],\n actions: [\n // Example action:\n // { key: 'open', label: 'Open', kind: 'navigation' },\n ],\n },\n states: {\n // empty: { name: 'app.data.empty', version: 1 },\n // error: { name: 'app.data.error', version: 1 },\n },\n};\n`;\n}\n\nfunction toPascalCase(value: string): string {\n return value\n .split(/[-_.]/)\n .filter(Boolean)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join('');\n}\n\nfunction escape(value: string): string {\n return value.replace(/'/g, \"\\\\'\");\n}\n"],"mappings":";AAEA,SAAgB,qBAAqB,MAAgC;CACnE,MAAM,cACJ,aAAa,KAAK,KAAK,MAAM,IAAI,CAAC,KAAK,IAAI,WAAW,GAAG;CAE3D,MAAM,SAAS,KAAK,OACjB,KACE,UAAU;gBACD,MAAM,IAAI;kBACR,OAAO,MAAM,MAAM,CAAC;qBACjB,MAAM,SAAS;UAC1B,MAAM,SAAS,YAAY,MAAM,OAAO,MAAM,GAAG;UACjD,MAAM,WAAW,oBAAoB,GAAG;UACxC,MAAM,aAAa,sBAAsB,GAAG;SAEjD,CACA,KAAK,MAAM;CAEd,MAAM,kBAAkB,KAAK,iBAAiB,SAC1C,qBAAqB,KAAK,gBACvB,KAAK,QAAQ,IAAI,IAAI,GAAG,CACxB,KAAK,KAAK,CAAC,MACd;CAEJ,MAAM,gBAAgB,KAAK,gBACvB,kBAAkB,KAAK,cAAc,KAAK,cAAc,KAAK,cAAc,QAAQ,OACnF;AAEJ,QAAO;;eAEM,YAAY;;YAEf,KAAK,KAAK;eACP,KAAK,QAAQ;eACb,KAAK,OAAO;cACb,OAAO,KAAK,MAAM,CAAC;oBACb,OACd,KAAK,eAAe,0CACrB,CAAC;eACS,OAAO,KAAK,UAAU,KAAK,OAAO,CAAC;eACnC,KAAK,OAAO,KAAK,UAAU,IAAI,MAAM,GAAG,CAAC,KAAK,KAAK,CAAC;aACtD,KAAK,KAAK,KAAK,QAAQ,IAAI,IAAI,GAAG,CAAC,KAAK,KAAK,CAAC;kBACzC,KAAK,UAAU;;;;eAIlB,KAAK,iBAAiB,KAAK;iBACzB,KAAK,iBAAiB,QAAQ;;MAEzC,cAAc;;;;;;aAMP,KAAK,KAAK;;EAErB,OAAO;;MAEH,KAAK,eAAe,kBAAkB,KAAK,aAAa,MAAM,GAAG;MACjE,gBAAgB;;;;;;;;;;;;;;;;;AAkBtB,SAAS,aAAa,OAAuB;AAC3C,QAAO,MACJ,MAAM,QAAQ,CACd,OAAO,QAAQ,CACf,KAAK,SAAS,KAAK,OAAO,EAAE,CAAC,aAAa,GAAG,KAAK,MAAM,EAAE,CAAC,CAC3D,KAAK,GAAG;;AAGb,SAAS,OAAO,OAAuB;AACrC,QAAO,MAAM,QAAQ,MAAM,MAAM"}
|
|
1
|
+
{"version":3,"file":"data-view.template.js","names":[],"sources":["../../src/templates/data-view.template.ts"],"sourcesContent":["import type { DataViewSpecData } from '../types';\n\nexport function generateDataViewSpec(data: DataViewSpecData): string {\n const viewVarName =\n toPascalCase(data.name.split('.').pop() ?? 'DataView') + 'DataView';\n\n const fields = data.fields\n .map(\n (field) => ` {\n key: '${field.key}',\n label: '${escape(field.label)}',\n dataPath: '${field.dataPath}',\n ${field.format ? `format: '${field.format}',` : ''}\n ${field.sortable ? 'sortable: true,' : ''}\n ${field.filterable ? 'filterable: true,' : ''}\n }`\n )\n .join(',\\n');\n\n const secondaryFields = data.secondaryFields?.length\n ? `secondaryFields: [${data.secondaryFields\n .map((key) => `'${key}'`)\n .join(', ')}],`\n : '';\n\n const itemOperation = data.itemOperation\n ? `item: { name: '${data.itemOperation.name}', version: ${data.itemOperation.version} },`\n : '';\n\n return `import type { DataViewSpec } from '@contractspec/lib.contracts/data-views';\n\nexport const ${viewVarName}: DataViewSpec = {\n meta: {\n key: '${data.name}',\n version: ${data.version},\n entity: '${data.entity}',\n title: '${escape(data.title)}',\n description: '${escape(\n data.description || 'Describe the purpose of this data view.'\n )}',\n domain: '${escape(data.domain || data.entity)}',\n owners: [${data.owners.map((owner) => `'${owner}'`).join(', ')}],\n tags: [${data.tags.map((tag) => `'${tag}'`).join(', ')}],\n stability: '${data.stability}',\n },\n source: {\n primary: {\n name: '${data.primaryOperation.name}',\n version: ${data.primaryOperation.version},\n },\n ${itemOperation}\n refreshEvents: [\n // { name: 'entity.updated', version: '1.0.0' },\n ],\n },\n view: {\n kind: '${data.kind}',\n fields: [\n${fields}\n ],\n ${data.primaryField ? `primaryField: '${data.primaryField}',` : ''}\n ${secondaryFields}\n filters: [\n // Example filter:\n // { key: 'search', label: 'Search', field: 'fullName', type: 'search' },\n ],\n actions: [\n // Example action:\n // { key: 'open', label: 'Open', kind: 'navigation' },\n ],\n },\n states: {\n // empty: { name: 'app.data.empty', version: '1.0.0' },\n // error: { name: 'app.data.error', version: '1.0.0' },\n },\n};\n`;\n}\n\nfunction toPascalCase(value: string): string {\n return value\n .split(/[-_.]/)\n .filter(Boolean)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join('');\n}\n\nfunction escape(value: string): string {\n return value.replace(/'/g, \"\\\\'\");\n}\n"],"mappings":";AAEA,SAAgB,qBAAqB,MAAgC;CACnE,MAAM,cACJ,aAAa,KAAK,KAAK,MAAM,IAAI,CAAC,KAAK,IAAI,WAAW,GAAG;CAE3D,MAAM,SAAS,KAAK,OACjB,KACE,UAAU;gBACD,MAAM,IAAI;kBACR,OAAO,MAAM,MAAM,CAAC;qBACjB,MAAM,SAAS;UAC1B,MAAM,SAAS,YAAY,MAAM,OAAO,MAAM,GAAG;UACjD,MAAM,WAAW,oBAAoB,GAAG;UACxC,MAAM,aAAa,sBAAsB,GAAG;SAEjD,CACA,KAAK,MAAM;CAEd,MAAM,kBAAkB,KAAK,iBAAiB,SAC1C,qBAAqB,KAAK,gBACvB,KAAK,QAAQ,IAAI,IAAI,GAAG,CACxB,KAAK,KAAK,CAAC,MACd;CAEJ,MAAM,gBAAgB,KAAK,gBACvB,kBAAkB,KAAK,cAAc,KAAK,cAAc,KAAK,cAAc,QAAQ,OACnF;AAEJ,QAAO;;eAEM,YAAY;;YAEf,KAAK,KAAK;eACP,KAAK,QAAQ;eACb,KAAK,OAAO;cACb,OAAO,KAAK,MAAM,CAAC;oBACb,OACd,KAAK,eAAe,0CACrB,CAAC;eACS,OAAO,KAAK,UAAU,KAAK,OAAO,CAAC;eACnC,KAAK,OAAO,KAAK,UAAU,IAAI,MAAM,GAAG,CAAC,KAAK,KAAK,CAAC;aACtD,KAAK,KAAK,KAAK,QAAQ,IAAI,IAAI,GAAG,CAAC,KAAK,KAAK,CAAC;kBACzC,KAAK,UAAU;;;;eAIlB,KAAK,iBAAiB,KAAK;iBACzB,KAAK,iBAAiB,QAAQ;;MAEzC,cAAc;;;;;;aAMP,KAAK,KAAK;;EAErB,OAAO;;MAEH,KAAK,eAAe,kBAAkB,KAAK,aAAa,MAAM,GAAG;MACjE,gBAAgB;;;;;;;;;;;;;;;;;AAkBtB,SAAS,aAAa,OAAuB;AAC3C,QAAO,MACJ,MAAM,QAAQ,CACd,OAAO,QAAQ,CACf,KAAK,SAAS,KAAK,OAAO,EAAE,CAAC,aAAa,GAAG,KAAK,MAAM,EAAE,CAAC,CAC3D,KAAK,GAAG;;AAGb,SAAS,OAAO,OAAuB;AACrC,QAAO,MAAM,QAAQ,MAAM,MAAM"}
|
|
@@ -21,7 +21,9 @@ export const ${payloadSchemaName} = new SchemaModel({
|
|
|
21
21
|
|
|
22
22
|
export const ${eventVarName} = defineEvent({
|
|
23
23
|
name: '${name}',
|
|
24
|
-
|
|
24
|
+
name: '${name}',
|
|
25
|
+
version: '${version}',
|
|
26
|
+
description: '${description}',
|
|
25
27
|
description: '${description}',
|
|
26
28
|
stability: '${stability}',
|
|
27
29
|
owners: [${owners.map((o) => `'${o}'`).join(", ")}],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event.template.js","names":[],"sources":["../../src/templates/event.template.ts"],"sourcesContent":["import type { EventSpecData } from '../types';\n\n/**\n * Generate event spec TypeScript code\n */\nexport function generateEventSpec(data: EventSpecData): string {\n const { name, version, description, stability, owners, tags, piiFields } =\n data;\n\n const eventVarName = toPascalCase(name.replace(/\\./g, '_')) + 'V' + version;\n const payloadSchemaName = eventVarName + 'Payload';\n\n return `import { defineEvent } from '@contractspec/lib.contracts';\nimport { ScalarTypeEnum, SchemaModel } from '@contractspec/lib.schema';\n\n// TODO: Define event payload schema\nexport const ${payloadSchemaName} = new SchemaModel({\n name: '${payloadSchemaName}',\n description: 'Payload for ${name}',\n fields: {\n // Add your payload fields here\n // example: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },\n },\n});\n\nexport const ${eventVarName} = defineEvent({\n name: '${name}',\n version: ${version},\n description: '${description}',\n stability: '${stability}',\n owners: [${owners.map((o) => `'${o}'`).join(', ')}],\n tags: [${tags.map((t) => `'${t}'`).join(', ')}],\n ${piiFields.length > 0 ? `pii: [${piiFields.map((f) => `'${f}'`).join(', ')}],` : '// pii: [],'}\n payload: ${payloadSchemaName},\n});\n`;\n}\n\nfunction toPascalCase(str: string): string {\n return str\n .split(/[-_.]/)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join('');\n}\n"],"mappings":";;;;AAKA,SAAgB,kBAAkB,MAA6B;CAC7D,MAAM,EAAE,MAAM,SAAS,aAAa,WAAW,QAAQ,MAAM,cAC3D;CAEF,MAAM,eAAe,aAAa,KAAK,QAAQ,OAAO,IAAI,CAAC,GAAG,MAAM;CACpE,MAAM,oBAAoB,eAAe;AAEzC,QAAO;;;;eAIM,kBAAkB;WACtB,kBAAkB;8BACC,KAAK;;;;;;;eAOpB,aAAa;WACjB,KAAK;
|
|
1
|
+
{"version":3,"file":"event.template.js","names":[],"sources":["../../src/templates/event.template.ts"],"sourcesContent":["import type { EventSpecData } from '../types';\n\n/**\n * Generate event spec TypeScript code\n */\nexport function generateEventSpec(data: EventSpecData): string {\n const { name, version, description, stability, owners, tags, piiFields } =\n data;\n\n const eventVarName = toPascalCase(name.replace(/\\./g, '_')) + 'V' + version;\n const payloadSchemaName = eventVarName + 'Payload';\n\n return `import { defineEvent } from '@contractspec/lib.contracts';\nimport { ScalarTypeEnum, SchemaModel } from '@contractspec/lib.schema';\n\n// TODO: Define event payload schema\nexport const ${payloadSchemaName} = new SchemaModel({\n name: '${payloadSchemaName}',\n description: 'Payload for ${name}',\n fields: {\n // Add your payload fields here\n // example: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },\n },\n});\n\nexport const ${eventVarName} = defineEvent({\n name: '${name}',\n name: '${name}',\n version: '${version}',\n description: '${description}',\n description: '${description}',\n stability: '${stability}',\n owners: [${owners.map((o) => `'${o}'`).join(', ')}],\n tags: [${tags.map((t) => `'${t}'`).join(', ')}],\n ${piiFields.length > 0 ? `pii: [${piiFields.map((f) => `'${f}'`).join(', ')}],` : '// pii: [],'}\n payload: ${payloadSchemaName},\n});\n`;\n}\n\nfunction toPascalCase(str: string): string {\n return str\n .split(/[-_.]/)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join('');\n}\n"],"mappings":";;;;AAKA,SAAgB,kBAAkB,MAA6B;CAC7D,MAAM,EAAE,MAAM,SAAS,aAAa,WAAW,QAAQ,MAAM,cAC3D;CAEF,MAAM,eAAe,aAAa,KAAK,QAAQ,OAAO,IAAI,CAAC,GAAG,MAAM;CACpE,MAAM,oBAAoB,eAAe;AAEzC,QAAO;;;;eAIM,kBAAkB;WACtB,kBAAkB;8BACC,KAAK;;;;;;;eAOpB,aAAa;WACjB,KAAK;WACL,KAAK;cACF,QAAQ;kBACJ,YAAY;kBACZ,YAAY;gBACd,UAAU;aACb,OAAO,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,CAAC;WACzC,KAAK,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,CAAC;IAC5C,UAAU,SAAS,IAAI,SAAS,UAAU,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,CAAC,MAAM,cAAc;aACrF,kBAAkB;;;;AAK/B,SAAS,aAAa,KAAqB;AACzC,QAAO,IACJ,MAAM,QAAQ,CACd,KAAK,SAAS,KAAK,OAAO,EAAE,CAAC,aAAa,GAAG,KAAK,MAAM,EAAE,CAAC,CAC3D,KAAK,GAAG"}
|
|
@@ -33,7 +33,9 @@ export const ${outputSchemaName} = new SchemaModel({
|
|
|
33
33
|
export const ${specVarName} = define${capitalize(kind)}({
|
|
34
34
|
meta: {
|
|
35
35
|
key: '${name}',
|
|
36
|
-
|
|
36
|
+
key: '${name}',
|
|
37
|
+
version: '${version}',
|
|
38
|
+
stability: '${stability}',
|
|
37
39
|
stability: '${stability}',
|
|
38
40
|
owners: [${owners.map((o) => `'${o}'`).join(", ")}],
|
|
39
41
|
tags: [${tags.map((t) => `'${t}'`).join(", ")}],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operation.template.js","names":[],"sources":["../../src/templates/operation.template.ts"],"sourcesContent":["import type { OperationSpecData } from '../types';\n\n/**\n * Generate operation spec TypeScript code\n */\nexport function generateOperationSpec(data: OperationSpecData): string {\n const {\n name,\n version,\n kind,\n description,\n goal,\n context,\n stability,\n owners,\n tags,\n auth,\n flags,\n } = data;\n\n const specVarName = toPascalCase(name.split('.').pop() || 'Unknown') + 'Spec';\n const inputSchemaName = specVarName.replace('Spec', 'Input');\n const outputSchemaName = specVarName.replace('Spec', 'Output');\n\n return `import { define${capitalize(kind)} } from '@contractspec/lib.contracts';\nimport { ScalarTypeEnum, SchemaModel } from '@contractspec/lib.schema';\n\n// TODO: Define input schema\nexport const ${inputSchemaName} = new SchemaModel({\n name: '${inputSchemaName}',\n description: 'Input for ${name}',\n fields: {\n // Add your fields here\n // example: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },\n },\n});\n\n// TODO: Define output schema\nexport const ${outputSchemaName} = new SchemaModel({\n name: '${outputSchemaName}',\n description: 'Output for ${name}',\n fields: {\n // Add your fields here\n ok: { type: ScalarTypeEnum.Boolean(), isOptional: false },\n },\n});\n\nexport const ${specVarName} = define${capitalize(kind)}({\n meta: {\n key: '${name}',\n version: ${version},\n stability: '${stability}',\n owners: [${owners.map((o) => `'${o}'`).join(', ')}],\n tags: [${tags.map((t) => `'${t}'`).join(', ')}],\n description: '${description}',\n goal: '${goal}',\n context: '${context}',\n },\n\n io: {\n input: ${inputSchemaName},\n output: ${outputSchemaName},\n errors: {\n // Define possible errors\n // EXAMPLE_ERROR: {\n // description: 'Example error description',\n // http: 400,\n // when: 'When this error occurs',\n // },\n },\n },\n\n policy: {\n auth: '${auth}',\n ${flags.length > 0 ? `flags: [${flags.map((f) => `'${f}'`).join(', ')}],` : '// flags: [],'}\n },\n\n sideEffects: {\n ${data.emitsEvents ? \"emits: [\\n // Define events to emit\\n // { ref: SomeEventSpec, when: 'always' }\\n ],\" : '// emits: [],'}\n analytics: [\n // Define analytics events\n ],\n },\n\n transport: {\n rest: { method: '${kind === 'command' ? 'POST' : 'GET'}' },\n gql: { field: '${name.replace(/\\./g, '_')}' },\n mcp: { toolName: '${name}.v${version}' },\n },\n\n acceptance: {\n scenarios: [\n {\n name: 'Happy path',\n given: ['preconditions'],\n when: ['action taken'],\n then: ['expected outcome'],\n },\n ],\n examples: [\n {\n name: 'Example usage',\n input: { /* example input */ },\n output: { ok: true },\n },\n ],\n },\n});\n`;\n}\n\nfunction capitalize(str: string): string {\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\nfunction toPascalCase(str: string): string {\n return str.split(/[-_.]/).map(capitalize).join('');\n}\n"],"mappings":";;;;AAKA,SAAgB,sBAAsB,MAAiC;CACrE,MAAM,EACJ,MACA,SACA,MACA,aACA,MACA,SACA,WACA,QACA,MACA,MACA,UACE;CAEJ,MAAM,cAAc,aAAa,KAAK,MAAM,IAAI,CAAC,KAAK,IAAI,UAAU,GAAG;CACvE,MAAM,kBAAkB,YAAY,QAAQ,QAAQ,QAAQ;CAC5D,MAAM,mBAAmB,YAAY,QAAQ,QAAQ,SAAS;AAE9D,QAAO,kBAAkB,WAAW,KAAK,CAAC;;;;eAI7B,gBAAgB;WACpB,gBAAgB;4BACC,KAAK;;;;;;;;eAQlB,iBAAiB;WACrB,iBAAiB;6BACC,KAAK;;;;;;;eAOnB,YAAY,WAAW,WAAW,KAAK,CAAC;;YAE3C,KAAK;
|
|
1
|
+
{"version":3,"file":"operation.template.js","names":[],"sources":["../../src/templates/operation.template.ts"],"sourcesContent":["import type { OperationSpecData } from '../types';\n\n/**\n * Generate operation spec TypeScript code\n */\nexport function generateOperationSpec(data: OperationSpecData): string {\n const {\n name,\n version,\n kind,\n description,\n goal,\n context,\n stability,\n owners,\n tags,\n auth,\n flags,\n } = data;\n\n const specVarName = toPascalCase(name.split('.').pop() || 'Unknown') + 'Spec';\n const inputSchemaName = specVarName.replace('Spec', 'Input');\n const outputSchemaName = specVarName.replace('Spec', 'Output');\n\n return `import { define${capitalize(kind)} } from '@contractspec/lib.contracts';\nimport { ScalarTypeEnum, SchemaModel } from '@contractspec/lib.schema';\n\n// TODO: Define input schema\nexport const ${inputSchemaName} = new SchemaModel({\n name: '${inputSchemaName}',\n description: 'Input for ${name}',\n fields: {\n // Add your fields here\n // example: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },\n },\n});\n\n// TODO: Define output schema\nexport const ${outputSchemaName} = new SchemaModel({\n name: '${outputSchemaName}',\n description: 'Output for ${name}',\n fields: {\n // Add your fields here\n ok: { type: ScalarTypeEnum.Boolean(), isOptional: false },\n },\n});\n\nexport const ${specVarName} = define${capitalize(kind)}({\n meta: {\n key: '${name}',\n key: '${name}',\n version: '${version}',\n stability: '${stability}',\n stability: '${stability}',\n owners: [${owners.map((o) => `'${o}'`).join(', ')}],\n tags: [${tags.map((t) => `'${t}'`).join(', ')}],\n description: '${description}',\n goal: '${goal}',\n context: '${context}',\n },\n\n io: {\n input: ${inputSchemaName},\n output: ${outputSchemaName},\n errors: {\n // Define possible errors\n // EXAMPLE_ERROR: {\n // description: 'Example error description',\n // http: 400,\n // when: 'When this error occurs',\n // },\n },\n },\n\n policy: {\n auth: '${auth}',\n ${flags.length > 0 ? `flags: [${flags.map((f) => `'${f}'`).join(', ')}],` : '// flags: [],'}\n },\n\n sideEffects: {\n ${data.emitsEvents ? \"emits: [\\n // Define events to emit\\n // { ref: SomeEventSpec, when: 'always' }\\n ],\" : '// emits: [],'}\n analytics: [\n // Define analytics events\n ],\n },\n\n transport: {\n rest: { method: '${kind === 'command' ? 'POST' : 'GET'}' },\n gql: { field: '${name.replace(/\\./g, '_')}' },\n mcp: { toolName: '${name}.v${version}' },\n },\n\n acceptance: {\n scenarios: [\n {\n name: 'Happy path',\n given: ['preconditions'],\n when: ['action taken'],\n then: ['expected outcome'],\n },\n ],\n examples: [\n {\n name: 'Example usage',\n input: { /* example input */ },\n output: { ok: true },\n },\n ],\n },\n});\n`;\n}\n\nfunction capitalize(str: string): string {\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\nfunction toPascalCase(str: string): string {\n return str.split(/[-_.]/).map(capitalize).join('');\n}\n"],"mappings":";;;;AAKA,SAAgB,sBAAsB,MAAiC;CACrE,MAAM,EACJ,MACA,SACA,MACA,aACA,MACA,SACA,WACA,QACA,MACA,MACA,UACE;CAEJ,MAAM,cAAc,aAAa,KAAK,MAAM,IAAI,CAAC,KAAK,IAAI,UAAU,GAAG;CACvE,MAAM,kBAAkB,YAAY,QAAQ,QAAQ,QAAQ;CAC5D,MAAM,mBAAmB,YAAY,QAAQ,QAAQ,SAAS;AAE9D,QAAO,kBAAkB,WAAW,KAAK,CAAC;;;;eAI7B,gBAAgB;WACpB,gBAAgB;4BACC,KAAK;;;;;;;;eAQlB,iBAAiB;WACrB,iBAAiB;6BACC,KAAK;;;;;;;eAOnB,YAAY,WAAW,WAAW,KAAK,CAAC;;YAE3C,KAAK;YACL,KAAK;gBACD,QAAQ;kBACN,UAAU;kBACV,UAAU;eACb,OAAO,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,CAAC;aACzC,KAAK,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,CAAC;oBAC9B,YAAY;aACnB,KAAK;gBACF,QAAQ;;;;aAIX,gBAAgB;cACf,iBAAiB;;;;;;;;;;;;aAYlB,KAAK;MACZ,MAAM,SAAS,IAAI,WAAW,MAAM,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,CAAC,MAAM,gBAAgB;;;;MAI1F,KAAK,cAAc,sGAAsG,gBAAgB;;;;;;;uBAOxH,SAAS,YAAY,SAAS,MAAM;qBACtC,KAAK,QAAQ,OAAO,IAAI,CAAC;wBACtB,KAAK,IAAI,QAAQ;;;;;;;;;;;;;;;;;;;;;;;AAwBzC,SAAS,WAAW,KAAqB;AACvC,QAAO,IAAI,OAAO,EAAE,CAAC,aAAa,GAAG,IAAI,MAAM,EAAE;;AAGnD,SAAS,aAAa,KAAqB;AACzC,QAAO,IAAI,MAAM,QAAQ,CAAC,IAAI,WAAW,CAAC,KAAK,GAAG"}
|
package/dist/types.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ type Stability = 'experimental' | 'beta' | 'stable' | 'deprecated';
|
|
|
5
5
|
type StepType = 'human' | 'automation' | 'decision';
|
|
6
6
|
interface BaseSpecData {
|
|
7
7
|
name: string;
|
|
8
|
-
version:
|
|
8
|
+
version: string;
|
|
9
9
|
description: string;
|
|
10
10
|
owners: string[];
|
|
11
11
|
tags: string[];
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import vm from "node:vm";
|
|
5
|
+
import ts from "typescript";
|
|
6
|
+
|
|
7
|
+
//#region src/utils/module-loader.ts
|
|
8
|
+
async function loadTypeScriptModule(filePath) {
|
|
9
|
+
const source = await readFile(filePath, "utf-8");
|
|
10
|
+
const transpiled = ts.transpileModule(source, {
|
|
11
|
+
compilerOptions: {
|
|
12
|
+
module: ts.ModuleKind.CommonJS,
|
|
13
|
+
target: ts.ScriptTarget.ES2020,
|
|
14
|
+
esModuleInterop: true
|
|
15
|
+
},
|
|
16
|
+
fileName: filePath
|
|
17
|
+
});
|
|
18
|
+
const moduleExports = {};
|
|
19
|
+
const moduleObject = { exports: moduleExports };
|
|
20
|
+
const require = createRequire(filePath);
|
|
21
|
+
const context = vm.createContext({
|
|
22
|
+
module: moduleObject,
|
|
23
|
+
exports: moduleExports,
|
|
24
|
+
require,
|
|
25
|
+
__dirname: dirname(filePath),
|
|
26
|
+
__filename: filePath,
|
|
27
|
+
process,
|
|
28
|
+
console,
|
|
29
|
+
Buffer,
|
|
30
|
+
setTimeout,
|
|
31
|
+
setImmediate,
|
|
32
|
+
clearTimeout,
|
|
33
|
+
clearImmediate
|
|
34
|
+
});
|
|
35
|
+
new vm.Script(transpiled.outputText, { filename: filePath }).runInContext(context);
|
|
36
|
+
return moduleObject.exports;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
//#endregion
|
|
40
|
+
export { loadTypeScriptModule };
|
|
41
|
+
//# sourceMappingURL=module-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module-loader.js","names":["moduleExports: Record<string, unknown>"],"sources":["../../src/utils/module-loader.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { createRequire } from 'node:module';\nimport vm from 'node:vm';\nimport ts from 'typescript';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport async function loadTypeScriptModule(filePath: string): Promise<any> {\n const source = await readFile(filePath, 'utf-8');\n const transpiled = ts.transpileModule(source, {\n compilerOptions: {\n module: ts.ModuleKind.CommonJS,\n target: ts.ScriptTarget.ES2020,\n esModuleInterop: true,\n },\n fileName: filePath,\n });\n\n const moduleExports: Record<string, unknown> = {};\n const moduleObject = { exports: moduleExports };\n const require = createRequire(filePath);\n\n const context = vm.createContext({\n module: moduleObject,\n exports: moduleExports,\n require,\n __dirname: dirname(filePath),\n __filename: filePath,\n process,\n console,\n Buffer,\n setTimeout,\n setImmediate,\n clearTimeout,\n clearImmediate,\n });\n\n const script = new vm.Script(transpiled.outputText, {\n filename: filePath,\n });\n script.runInContext(context);\n\n return moduleObject.exports;\n}\n"],"mappings":";;;;;;;AAOA,eAAsB,qBAAqB,UAAgC;CACzE,MAAM,SAAS,MAAM,SAAS,UAAU,QAAQ;CAChD,MAAM,aAAa,GAAG,gBAAgB,QAAQ;EAC5C,iBAAiB;GACf,QAAQ,GAAG,WAAW;GACtB,QAAQ,GAAG,aAAa;GACxB,iBAAiB;GAClB;EACD,UAAU;EACX,CAAC;CAEF,MAAMA,gBAAyC,EAAE;CACjD,MAAM,eAAe,EAAE,SAAS,eAAe;CAC/C,MAAM,UAAU,cAAc,SAAS;CAEvC,MAAM,UAAU,GAAG,cAAc;EAC/B,QAAQ;EACR,SAAS;EACT;EACA,WAAW,QAAQ,SAAS;EAC5B,YAAY;EACZ;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;AAKF,CAHe,IAAI,GAAG,OAAO,WAAW,YAAY,EAClD,UAAU,UACX,CAAC,CACK,aAAa,QAAQ;AAE5B,QAAO,aAAa"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contractspec/bundle.workspace",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.45.0",
|
|
4
4
|
"description": "Workspace utilities for monorepo development",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"contractspec",
|
|
@@ -36,20 +36,20 @@
|
|
|
36
36
|
"@ai-sdk/mistral": "3.0.1",
|
|
37
37
|
"@ai-sdk/openai": "3.0.1",
|
|
38
38
|
"ollama-ai-provider": "^1.2.0",
|
|
39
|
-
"@contractspec/module.workspace": "1.
|
|
40
|
-
"@contractspec/lib.contracts": "1.
|
|
41
|
-
"@contractspec/lib.contracts-transformers": "1.
|
|
42
|
-
"@contractspec/lib.ai-providers": "1.
|
|
43
|
-
"@contractspec/lib.schema": "1.
|
|
44
|
-
"@contractspec/lib.testing": "1.
|
|
39
|
+
"@contractspec/module.workspace": "1.45.0",
|
|
40
|
+
"@contractspec/lib.contracts": "1.45.0",
|
|
41
|
+
"@contractspec/lib.contracts-transformers": "1.45.0",
|
|
42
|
+
"@contractspec/lib.ai-providers": "1.45.0",
|
|
43
|
+
"@contractspec/lib.schema": "1.45.0",
|
|
44
|
+
"@contractspec/lib.testing": "1.45.0",
|
|
45
45
|
"ai": "6.0.3",
|
|
46
46
|
"zod": "^4.1.13",
|
|
47
47
|
"glob": "^13.0.0",
|
|
48
48
|
"chokidar": "^5.0.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@contractspec/tool.tsdown": "1.
|
|
52
|
-
"@contractspec/tool.typescript": "1.
|
|
51
|
+
"@contractspec/tool.tsdown": "1.45.0",
|
|
52
|
+
"@contractspec/tool.typescript": "1.45.0",
|
|
53
53
|
"@types/node": "^22.10.2",
|
|
54
54
|
"tsdown": "^0.18.3",
|
|
55
55
|
"typescript": "^5.9.3"
|
package/dist/ai/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/ai/index.ts"],"sourcesContent":[],"mappings":""}
|
package/dist/ai/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/ai/index.ts"],"sourcesContent":["/**\n * AI services for ContractSpec.\n */\n\nexport * from './client';\nexport * from './providers';\nexport * from './agents';\nexport * as prompts from './prompts';\n"],"mappings":""}
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":""}
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @contractspec/bundle.workspace\n *\n * Reusable use-cases and services for ContractSpec workspace operations.\n */\n\n// Re-export module for convenience\nexport * from '@contractspec/module.workspace';\n\n// Ports (adapter interfaces)\nexport * from './ports/index';\n\n// Adapters (Node.js implementations)\nexport * from './adapters/index';\n\n// Types\nexport type { Config, AiProvider, AgentMode } from './types/config';\n\n// Services (use-cases)\nexport * from './services/index';\n\n// Formatters (CI output formats)\nexport * as formatters from './formatters/index';\n\n// Templates (re-export for CLI usage)\nexport * as templates from './templates';\nexport * as ai from './ai';\n"],"mappings":""}
|
package/dist/services/test.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { OperationSpecRegistry } from "@contractspec/lib.contracts";
|
|
2
|
-
import { TestRunner, TestSpec } from "@contractspec/lib.contracts/tests";
|
|
3
|
-
|
|
4
|
-
//#region src/services/test.d.ts
|
|
5
|
-
|
|
6
|
-
type TestRunResult = Awaited<ReturnType<TestRunner['run']>>;
|
|
7
|
-
interface RunTestsResult {
|
|
8
|
-
results: TestRunResult[];
|
|
9
|
-
passed: number;
|
|
10
|
-
failed: number;
|
|
11
|
-
}
|
|
12
|
-
declare function runTests(specs: TestSpec[], registry: OperationSpecRegistry): Promise<RunTestsResult>;
|
|
13
|
-
//#endregion
|
|
14
|
-
export { RunTestsResult, TestRunResult, runTests };
|
|
15
|
-
//# sourceMappingURL=test.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"test.d.ts","names":[],"sources":["../../src/services/test.ts"],"sourcesContent":[],"mappings":";;;;;AAUmC,KAAvB,aAAA,GAAgB,OAAO,CAAC,UAAD,CAAY,UAAZ,CAAA,KAAA,CAAA,CAAA,CAAA;AAElB,UAAA,cAAA,CAAc;EAMT,OAAA,EALX,aAKmB,EAAA;EACrB,MAAA,EAAA,MAAA;EACG,MAAA,EAAA,MAAA;;AACT,iBAHmB,QAAA,CAGnB,KAAA,EAFM,QAEN,EAAA,EAAA,QAAA,EADS,qBACT,CAAA,EAAA,OAAA,CAAQ,cAAR,CAAA"}
|
package/dist/services/test.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { TestRunner } from "@contractspec/lib.contracts/tests";
|
|
2
|
-
|
|
3
|
-
//#region src/services/test.ts
|
|
4
|
-
/**
|
|
5
|
-
* Test service.
|
|
6
|
-
*
|
|
7
|
-
* Pure runner wrapper that executes TestSpec scenarios against a OperationSpecRegistry.
|
|
8
|
-
* Loading/compiling spec modules (TS/ESM) is intentionally left to the caller.
|
|
9
|
-
*/
|
|
10
|
-
async function runTests(specs, registry) {
|
|
11
|
-
const runner = new TestRunner({ registry });
|
|
12
|
-
const results = [];
|
|
13
|
-
let passed = 0;
|
|
14
|
-
let failed = 0;
|
|
15
|
-
for (const spec of specs) {
|
|
16
|
-
const result = await runner.run(spec);
|
|
17
|
-
results.push(result);
|
|
18
|
-
passed += result.passed;
|
|
19
|
-
failed += result.failed;
|
|
20
|
-
}
|
|
21
|
-
return {
|
|
22
|
-
results,
|
|
23
|
-
passed,
|
|
24
|
-
failed
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
//#endregion
|
|
29
|
-
export { runTests };
|
|
30
|
-
//# sourceMappingURL=test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"test.js","names":["results: TestRunResult[]"],"sources":["../../src/services/test.ts"],"sourcesContent":["/**\n * Test service.\n *\n * Pure runner wrapper that executes TestSpec scenarios against a OperationSpecRegistry.\n * Loading/compiling spec modules (TS/ESM) is intentionally left to the caller.\n */\n\nimport { TestRunner, type TestSpec } from '@contractspec/lib.contracts/tests';\nimport type { OperationSpecRegistry } from '@contractspec/lib.contracts';\n\nexport type TestRunResult = Awaited<ReturnType<TestRunner['run']>>;\n\nexport interface RunTestsResult {\n results: TestRunResult[];\n passed: number;\n failed: number;\n}\n\nexport async function runTests(\n specs: TestSpec[],\n registry: OperationSpecRegistry\n): Promise<RunTestsResult> {\n const runner = new TestRunner({ registry });\n\n const results: TestRunResult[] = [];\n let passed = 0;\n let failed = 0;\n\n for (const spec of specs) {\n const result = await runner.run(spec);\n results.push(result);\n passed += result.passed;\n failed += result.failed;\n }\n\n return { results, passed, failed };\n}\n"],"mappings":";;;;;;;;;AAkBA,eAAsB,SACpB,OACA,UACyB;CACzB,MAAM,SAAS,IAAI,WAAW,EAAE,UAAU,CAAC;CAE3C,MAAMA,UAA2B,EAAE;CACnC,IAAI,SAAS;CACb,IAAI,SAAS;AAEb,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,SAAS,MAAM,OAAO,IAAI,KAAK;AACrC,UAAQ,KAAK,OAAO;AACpB,YAAU,OAAO;AACjB,YAAU,OAAO;;AAGnB,QAAO;EAAE;EAAS;EAAQ;EAAQ"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"validate-implementation.d.ts","names":[],"sources":["../../src/services/validate-implementation.ts"],"sourcesContent":[],"mappings":";;;;;AAoDU,UAzCO,6BAAA,CAyCP;EACC,aAAA,CAAA,EAAA,OAAA;EACA,UAAA,CAAA,EAAA,OAAA;EAAR;;;;;UAlCc,4BAAA;;;;;;;;;;;;;iBA6BK,2BAAA;MAEJ;WACR,2BACC,gCACR,QAAQ"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"validate-implementation.js","names":["errors: string[]","warnings: string[]","expected: ValidateImplementationResult['expected']"],"sources":["../../src/services/validate-implementation.ts"],"sourcesContent":["/**\n * Implementation validation service (handlers + tests).\n *\n * Deterministic, static checks intended for reuse across CLI/VSCode/web tooling.\n * This does NOT execute spec modules.\n */\n\nimport type { WorkspaceConfig } from '@contractspec/module.workspace';\nimport { scanSpecSource } from '@contractspec/module.workspace';\nimport type { FsAdapter } from '../ports/fs';\n\nexport interface ValidateImplementationOptions {\n checkHandlers?: boolean;\n checkTests?: boolean;\n /**\n * Override workspace outputDir (defaults to config.outputDir).\n */\n outputDir?: string;\n}\n\nexport interface ValidateImplementationResult {\n valid: boolean;\n errors: string[];\n warnings: string[];\n expected: {\n handlerPath?: string;\n handlerTestPath?: string;\n componentPath?: string;\n componentTestPath?: string;\n formPath?: string;\n formTestPath?: string;\n };\n}\n\nfunction toKebabCase(value: string): string {\n return value\n .replace(/\\./g, '-')\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .toLowerCase();\n}\n\nfunction toPascalCase(value: string): string {\n return value\n .split(/[-_.]/)\n .filter(Boolean)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join('');\n}\n\nexport async function validateImplementationFiles(\n specFile: string,\n adapters: { fs: FsAdapter },\n config: WorkspaceConfig,\n options: ValidateImplementationOptions = {}\n): Promise<ValidateImplementationResult> {\n const { fs } = adapters;\n const errors: string[] = [];\n const warnings: string[] = [];\n\n const exists = await fs.exists(specFile);\n if (!exists) {\n return {\n valid: false,\n errors: [`Spec file not found: ${specFile}`],\n warnings: [],\n expected: {},\n };\n }\n\n const code = await fs.readFile(specFile);\n const scan = scanSpecSource(code, specFile);\n const specName = scan.key ?? fs.basename(specFile).replace(/\\.[jt]s$/, '');\n const outRoot = options.outputDir ?? config.outputDir ?? './src';\n const kebab = toKebabCase(specName);\n\n const expected: ValidateImplementationResult['expected'] = {};\n\n if (scan.specType === 'operation') {\n expected.handlerPath = fs.join(outRoot, 'handlers', `${kebab}.handler.ts`);\n expected.handlerTestPath = fs.join(\n outRoot,\n 'handlers',\n `${kebab}.handler.test.ts`\n );\n }\n if (scan.specType === 'presentation') {\n expected.componentPath = fs.join(outRoot, 'components', `${kebab}.tsx`);\n expected.componentTestPath = fs.join(\n outRoot,\n 'components',\n `${kebab}.test.tsx`\n );\n }\n if (scan.specType === 'form') {\n expected.formPath = fs.join(outRoot, 'forms', `${kebab}.form.tsx`);\n expected.formTestPath = fs.join(outRoot, 'forms', `${kebab}.form.test.tsx`);\n }\n\n if (options.checkHandlers && expected.handlerPath) {\n const handlerExists = await fs.exists(expected.handlerPath);\n if (!handlerExists) {\n errors.push(`Missing handler file: ${expected.handlerPath}`);\n } else {\n const handlerCode = await fs.readFile(expected.handlerPath);\n\n const expectedSpecVar = `${toPascalCase(specName.split('.').pop() ?? specName)}Spec`;\n const hasContractHandlerType = /ContractHandler<\\s*typeof\\s+\\w+\\s*>/.test(\n handlerCode\n );\n const referencesExpectedSpec = new RegExp(\n `typeof\\\\s+${expectedSpecVar}\\\\b`\n ).test(handlerCode);\n if (!hasContractHandlerType) {\n warnings.push(\n `Handler does not appear to type itself as ContractHandler<typeof Spec>: ${expected.handlerPath}`\n );\n } else if (!referencesExpectedSpec) {\n warnings.push(\n `Handler ContractHandler typing does not reference expected spec var (${expectedSpecVar}): ${expected.handlerPath}`\n );\n }\n }\n }\n\n if (options.checkTests) {\n const candidateTests = [\n expected.handlerTestPath,\n expected.componentTestPath,\n expected.formTestPath,\n ].filter((p): p is string => typeof p === 'string');\n\n for (const testPath of candidateTests) {\n const testExists = await fs.exists(testPath);\n if (!testExists) {\n errors.push(`Missing test file: ${testPath}`);\n }\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n expected,\n };\n}\n"],"mappings":";;;AAkCA,SAAS,YAAY,OAAuB;AAC1C,QAAO,MACJ,QAAQ,OAAO,IAAI,CACnB,QAAQ,mBAAmB,QAAQ,CACnC,aAAa;;AAGlB,SAAS,aAAa,OAAuB;AAC3C,QAAO,MACJ,MAAM,QAAQ,CACd,OAAO,QAAQ,CACf,KAAK,SAAS,KAAK,OAAO,EAAE,CAAC,aAAa,GAAG,KAAK,MAAM,EAAE,CAAC,CAC3D,KAAK,GAAG;;AAGb,eAAsB,4BACpB,UACA,UACA,QACA,UAAyC,EAAE,EACJ;CACvC,MAAM,EAAE,OAAO;CACf,MAAMA,SAAmB,EAAE;CAC3B,MAAMC,WAAqB,EAAE;AAG7B,KAAI,CADW,MAAM,GAAG,OAAO,SAAS,CAEtC,QAAO;EACL,OAAO;EACP,QAAQ,CAAC,wBAAwB,WAAW;EAC5C,UAAU,EAAE;EACZ,UAAU,EAAE;EACb;CAIH,MAAM,OAAO,eADA,MAAM,GAAG,SAAS,SAAS,EACN,SAAS;CAC3C,MAAM,WAAW,KAAK,OAAO,GAAG,SAAS,SAAS,CAAC,QAAQ,YAAY,GAAG;CAC1E,MAAM,UAAU,QAAQ,aAAa,OAAO,aAAa;CACzD,MAAM,QAAQ,YAAY,SAAS;CAEnC,MAAMC,WAAqD,EAAE;AAE7D,KAAI,KAAK,aAAa,aAAa;AACjC,WAAS,cAAc,GAAG,KAAK,SAAS,YAAY,GAAG,MAAM,aAAa;AAC1E,WAAS,kBAAkB,GAAG,KAC5B,SACA,YACA,GAAG,MAAM,kBACV;;AAEH,KAAI,KAAK,aAAa,gBAAgB;AACpC,WAAS,gBAAgB,GAAG,KAAK,SAAS,cAAc,GAAG,MAAM,MAAM;AACvE,WAAS,oBAAoB,GAAG,KAC9B,SACA,cACA,GAAG,MAAM,WACV;;AAEH,KAAI,KAAK,aAAa,QAAQ;AAC5B,WAAS,WAAW,GAAG,KAAK,SAAS,SAAS,GAAG,MAAM,WAAW;AAClE,WAAS,eAAe,GAAG,KAAK,SAAS,SAAS,GAAG,MAAM,gBAAgB;;AAG7E,KAAI,QAAQ,iBAAiB,SAAS,YAEpC,KAAI,CADkB,MAAM,GAAG,OAAO,SAAS,YAAY,CAEzD,QAAO,KAAK,yBAAyB,SAAS,cAAc;MACvD;EACL,MAAM,cAAc,MAAM,GAAG,SAAS,SAAS,YAAY;EAE3D,MAAM,kBAAkB,GAAG,aAAa,SAAS,MAAM,IAAI,CAAC,KAAK,IAAI,SAAS,CAAC;EAC/E,MAAM,yBAAyB,sCAAsC,KACnE,YACD;EACD,MAAM,0CAAyB,IAAI,OACjC,aAAa,gBAAgB,KAC9B,EAAC,KAAK,YAAY;AACnB,MAAI,CAAC,uBACH,UAAS,KACP,2EAA2E,SAAS,cACrF;WACQ,CAAC,uBACV,UAAS,KACP,wEAAwE,gBAAgB,KAAK,SAAS,cACvG;;AAKP,KAAI,QAAQ,YAAY;EACtB,MAAM,iBAAiB;GACrB,SAAS;GACT,SAAS;GACT,SAAS;GACV,CAAC,QAAQ,MAAmB,OAAO,MAAM,SAAS;AAEnD,OAAK,MAAM,YAAY,eAErB,KAAI,CADe,MAAM,GAAG,OAAO,SAAS,CAE1C,QAAO,KAAK,sBAAsB,WAAW;;AAKnD,QAAO;EACL,OAAO,OAAO,WAAW;EACzB;EACA;EACA;EACD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.ts","names":[],"sources":["../../src/services/validate.ts"],"sourcesContent":[],"mappings":";;;;;;AAwBA;AAUA;;AAEqC,UAtBpB,mBAAA,CAsBoB;EAC1B;;;EACD,aAAA,CAAA,EAAA,OAAA;AAoCV;;;;AAIuB,UAtDN,kBAAA,CAsDM;EAAZ,KAAA,EAAA,OAAA;EAAR,eAAA,CAAA,EApDiB,gBAoDjB;EAAO,MAAA,EAAA,MAAA,EAAA;;;;;;iBA5CY,YAAA;MAEJ;UAAmB;aAC1B,sBACR,QAAQ;;;;iBAoCW,aAAA;MAEJ;UAAmB;aAC1B,sBACR,QAAQ,YAAY"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"validate.js","names":["allErrors: string[]","allWarnings: string[]","structureResult: ValidationResult | undefined"],"sources":["../../src/services/validate.ts"],"sourcesContent":["/**\n * Validation service.\n */\n\nimport {\n validateSpecStructure,\n type ValidationResult,\n} from '@contractspec/module.workspace';\nimport type { FsAdapter } from '../ports/fs';\nimport type { LoggerAdapter } from '../ports/logger';\n\n/**\n * Options for spec validation.\n */\nexport interface ValidateSpecOptions {\n /**\n * Skip spec structure validation (e.g., for blueprint files).\n */\n skipStructure?: boolean;\n}\n\n/**\n * Result of spec validation.\n */\nexport interface ValidateSpecResult {\n valid: boolean;\n structureResult?: ValidationResult;\n errors: string[];\n warnings: string[];\n}\n\n/**\n * Validate a spec file.\n */\nexport async function validateSpec(\n specFile: string,\n adapters: { fs: FsAdapter; logger: LoggerAdapter },\n options: ValidateSpecOptions = {}\n): Promise<ValidateSpecResult> {\n const { fs } = adapters;\n\n const exists = await fs.exists(specFile);\n if (!exists) {\n return {\n valid: false,\n errors: [`Spec file not found: ${specFile}`],\n warnings: [],\n };\n }\n\n const specCode = await fs.readFile(specFile);\n const fileName = fs.basename(specFile);\n\n const allErrors: string[] = [];\n const allWarnings: string[] = [];\n let structureResult: ValidationResult | undefined;\n\n if (!options.skipStructure) {\n structureResult = validateSpecStructure(specCode, fileName);\n allErrors.push(...structureResult.errors);\n allWarnings.push(...structureResult.warnings);\n }\n\n return {\n valid: allErrors.length === 0,\n structureResult,\n errors: allErrors,\n warnings: allWarnings,\n };\n}\n\n/**\n * Validate multiple spec files.\n */\nexport async function validateSpecs(\n specFiles: string[],\n adapters: { fs: FsAdapter; logger: LoggerAdapter },\n options: ValidateSpecOptions = {}\n): Promise<Map<string, ValidateSpecResult>> {\n const results = new Map<string, ValidateSpecResult>();\n\n for (const specFile of specFiles) {\n const result = await validateSpec(specFile, adapters, options);\n results.set(specFile, result);\n }\n\n return results;\n}\n"],"mappings":";;;;;;;;;AAkCA,eAAsB,aACpB,UACA,UACA,UAA+B,EAAE,EACJ;CAC7B,MAAM,EAAE,OAAO;AAGf,KAAI,CADW,MAAM,GAAG,OAAO,SAAS,CAEtC,QAAO;EACL,OAAO;EACP,QAAQ,CAAC,wBAAwB,WAAW;EAC5C,UAAU,EAAE;EACb;CAGH,MAAM,WAAW,MAAM,GAAG,SAAS,SAAS;CAC5C,MAAM,WAAW,GAAG,SAAS,SAAS;CAEtC,MAAMA,YAAsB,EAAE;CAC9B,MAAMC,cAAwB,EAAE;CAChC,IAAIC;AAEJ,KAAI,CAAC,QAAQ,eAAe;AAC1B,oBAAkB,sBAAsB,UAAU,SAAS;AAC3D,YAAU,KAAK,GAAG,gBAAgB,OAAO;AACzC,cAAY,KAAK,GAAG,gBAAgB,SAAS;;AAG/C,QAAO;EACL,OAAO,UAAU,WAAW;EAC5B;EACA,QAAQ;EACR,UAAU;EACX;;;;;AAMH,eAAsB,cACpB,WACA,UACA,UAA+B,EAAE,EACS;CAC1C,MAAM,0BAAU,IAAI,KAAiC;AAErD,MAAK,MAAM,YAAY,WAAW;EAChC,MAAM,SAAS,MAAM,aAAa,UAAU,UAAU,QAAQ;AAC9D,UAAQ,IAAI,UAAU,OAAO;;AAG/B,QAAO"}
|