@contractspec/bundle.workspace 1.45.1 → 1.45.2
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/dist/services/validate/blueprint-validator.js +1 -1
- package/dist/services/validate/blueprint-validator.js.map +1 -1
- package/dist/services/validate/tenant-validator.js +1 -1
- package/dist/services/validate/tenant-validator.js.map +1 -1
- package/dist/templates/app-config.template.js +7 -7
- package/dist/templates/app-config.template.js.map +1 -1
- package/dist/templates/experiment.template.js +4 -4
- package/dist/templates/experiment.template.js.map +1 -1
- package/dist/templates/integration.template.js +3 -3
- package/dist/templates/integration.template.js.map +1 -1
- package/dist/types.d.ts +20 -20
- package/package.json +7 -11
|
@@ -42,7 +42,7 @@ function extractBlueprintSpec(mod) {
|
|
|
42
42
|
return candidates[0];
|
|
43
43
|
}
|
|
44
44
|
function isBlueprintSpec(value) {
|
|
45
|
-
return typeof value === "object" && value !== null && "meta" in value && typeof value.meta?.key === "string" && typeof value.meta?.version === "
|
|
45
|
+
return typeof value === "object" && value !== null && "meta" in value && typeof value.meta?.key === "string" && typeof value.meta?.version === "string";
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blueprint-validator.js","names":["validateBlueprint","validateBlueprintSpec"],"sources":["../../../src/services/validate/blueprint-validator.ts"],"sourcesContent":["import { resolve } from 'path';\nimport { pathToFileURL } from 'url';\nimport {\n validateBlueprint as validateBlueprintSpec,\n type AppBlueprintSpec,\n} from '@contractspec/lib.contracts';\nimport type { FsAdapter } from '../../ports/fs';\n\n/**\n * Result of blueprint validation.\n */\nexport interface BlueprintValidationResult {\n spec?: AppBlueprintSpec;\n report?: ReturnType<typeof validateBlueprintSpec>;\n valid: boolean;\n errors: string[];\n}\n\n/**\n * Validate a blueprint spec file.\n */\nexport async function validateBlueprint(\n blueprintPath: string,\n adapters: { fs: FsAdapter }\n): Promise<BlueprintValidationResult> {\n const { fs } = adapters;\n const resolvedPath = resolve(process.cwd(), blueprintPath);\n\n if (!(await fs.exists(resolvedPath))) {\n return {\n valid: false,\n errors: [`Blueprint file not found: ${resolvedPath}`],\n };\n }\n\n try {\n const mod = await loadModule(resolvedPath);\n const spec = extractBlueprintSpec(mod);\n const report = validateBlueprintSpec(spec);\n\n return {\n spec,\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\nasync function loadModule(\n modulePath: string\n): Promise<Record<string, unknown>> {\n try {\n const url = pathToFileURL(modulePath).href;\n // Using native import which works with Bun and Node (if configured)\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\nfunction extractBlueprintSpec(mod: Record<string, unknown>): AppBlueprintSpec {\n const candidates = Object.values(mod).filter(isBlueprintSpec);\n if (candidates.length === 0) {\n throw new Error('Blueprint module does not export an AppBlueprintSpec.');\n }\n return candidates[0] as AppBlueprintSpec;\n}\n\nfunction isBlueprintSpec(value: unknown): value is AppBlueprintSpec {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'meta' in value &&\n typeof (value as AppBlueprintSpec).meta?.key === 'string' &&\n typeof (value as AppBlueprintSpec).meta?.version === '
|
|
1
|
+
{"version":3,"file":"blueprint-validator.js","names":["validateBlueprint","validateBlueprintSpec"],"sources":["../../../src/services/validate/blueprint-validator.ts"],"sourcesContent":["import { resolve } from 'path';\nimport { pathToFileURL } from 'url';\nimport {\n validateBlueprint as validateBlueprintSpec,\n type AppBlueprintSpec,\n} from '@contractspec/lib.contracts';\nimport type { FsAdapter } from '../../ports/fs';\n\n/**\n * Result of blueprint validation.\n */\nexport interface BlueprintValidationResult {\n spec?: AppBlueprintSpec;\n report?: ReturnType<typeof validateBlueprintSpec>;\n valid: boolean;\n errors: string[];\n}\n\n/**\n * Validate a blueprint spec file.\n */\nexport async function validateBlueprint(\n blueprintPath: string,\n adapters: { fs: FsAdapter }\n): Promise<BlueprintValidationResult> {\n const { fs } = adapters;\n const resolvedPath = resolve(process.cwd(), blueprintPath);\n\n if (!(await fs.exists(resolvedPath))) {\n return {\n valid: false,\n errors: [`Blueprint file not found: ${resolvedPath}`],\n };\n }\n\n try {\n const mod = await loadModule(resolvedPath);\n const spec = extractBlueprintSpec(mod);\n const report = validateBlueprintSpec(spec);\n\n return {\n spec,\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\nasync function loadModule(\n modulePath: string\n): Promise<Record<string, unknown>> {\n try {\n const url = pathToFileURL(modulePath).href;\n // Using native import which works with Bun and Node (if configured)\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\nfunction extractBlueprintSpec(mod: Record<string, unknown>): AppBlueprintSpec {\n const candidates = Object.values(mod).filter(isBlueprintSpec);\n if (candidates.length === 0) {\n throw new Error('Blueprint module does not export an AppBlueprintSpec.');\n }\n return candidates[0] as AppBlueprintSpec;\n}\n\nfunction isBlueprintSpec(value: unknown): value is AppBlueprintSpec {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'meta' in value &&\n typeof (value as AppBlueprintSpec).meta?.key === 'string' &&\n typeof (value as AppBlueprintSpec).meta?.version === 'string'\n );\n}\n"],"mappings":";;;;;;;;AAqBA,eAAsBA,oBACpB,eACA,UACoC;CACpC,MAAM,EAAE,OAAO;CACf,MAAM,eAAe,QAAQ,QAAQ,KAAK,EAAE,cAAc;AAE1D,KAAI,CAAE,MAAM,GAAG,OAAO,aAAa,CACjC,QAAO;EACL,OAAO;EACP,QAAQ,CAAC,6BAA6B,eAAe;EACtD;AAGH,KAAI;EAEF,MAAM,OAAO,qBADD,MAAM,WAAW,aAAa,CACJ;EACtC,MAAM,SAASC,kBAAsB,KAAK;AAE1C,SAAO;GACL;GACA;GACA,OAAO,OAAO;GACd,QAAQ,OAAO,OAAO,KAAK,MAAM,IAAI,EAAE,KAAK,IAAI,EAAE,KAAK,IAAI,EAAE,UAAU;GACxE;UACM,OAAO;AACd,SAAO;GACL,OAAO;GACP,QAAQ,CAAC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,CAAC;GACjE;;;AAIL,eAAe,WACb,YACkC;AAClC,KAAI;AAIF,SADY,MAAM,OAFN,cAAc,WAAW,CAAC;UAI/B,OAAO;AACd,QAAM,IAAI,MAAM,4BAA4B,WAAW,IAAI,QAAQ;;;AAIvE,SAAS,qBAAqB,KAAgD;CAC5E,MAAM,aAAa,OAAO,OAAO,IAAI,CAAC,OAAO,gBAAgB;AAC7D,KAAI,WAAW,WAAW,EACxB,OAAM,IAAI,MAAM,wDAAwD;AAE1E,QAAO,WAAW;;AAGpB,SAAS,gBAAgB,OAA2C;AAClE,QACE,OAAO,UAAU,YACjB,UAAU,QACV,UAAU,SACV,OAAQ,MAA2B,MAAM,QAAQ,YACjD,OAAQ,MAA2B,MAAM,YAAY"}
|
|
@@ -115,7 +115,7 @@ async function loadTranslationCatalog(path, fs) {
|
|
|
115
115
|
return normaliseTranslationCatalog(catalogs[0]);
|
|
116
116
|
}
|
|
117
117
|
function isBlueprintTranslationCatalog(value) {
|
|
118
|
-
return typeof value === "object" && value !== null && "meta" in value && typeof value.meta?.key === "string" && typeof value.meta?.version === "
|
|
118
|
+
return typeof value === "object" && value !== null && "meta" in value && typeof value.meta?.key === "string" && typeof value.meta?.version === "string" && Array.isArray(value.entries);
|
|
119
119
|
}
|
|
120
120
|
function normaliseTranslationCatalog(catalog) {
|
|
121
121
|
const supportedLocales = catalog.supportedLocales && catalog.supportedLocales.length > 0 ? catalog.supportedLocales : [catalog.defaultLocale];
|
|
@@ -1 +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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"}
|
|
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 === 'string' &&\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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"}
|
|
@@ -40,24 +40,24 @@ function buildMappingSection(prop, mappings) {
|
|
|
40
40
|
if (mappings.length === 0) return "";
|
|
41
41
|
return ` ${prop}: {\n${mappings.map((mapping) => ` ${mapping.slot}: {
|
|
42
42
|
name: '${escapeString(mapping.name)}',
|
|
43
|
-
${
|
|
43
|
+
${mapping.version !== void 0 ? `version: '${mapping.version}',` : ""}
|
|
44
44
|
}`).join(",\n")}\n },\n`;
|
|
45
45
|
}
|
|
46
46
|
function buildPolicySection(data) {
|
|
47
47
|
if (data.policyRefs.length === 0) return "";
|
|
48
48
|
return ` policies: [\n${data.policyRefs.map((policy) => ` {
|
|
49
|
-
name: '${escapeString(policy.name)}'${
|
|
49
|
+
name: '${escapeString(policy.name)}'${policy.version !== void 0 ? `,\n version: '${policy.version}'` : ""}
|
|
50
50
|
}`).join(",\n")}\n ],\n`;
|
|
51
51
|
}
|
|
52
52
|
function buildThemeSection(data) {
|
|
53
53
|
if (!data.theme) return "";
|
|
54
|
-
return ` theme: {\n${` primary: { name: '${escapeString(data.theme.name)}', version: ${data.theme.version} },\n`}${data.themeFallbacks.length > 0 ? ` fallbacks: [${data.themeFallbacks.map((theme) => `{ name: '${escapeString(theme.name)}', version: ${theme.version} }`).join(", ")}],\n` : ""} },\n`;
|
|
54
|
+
return ` theme: {\n${` primary: { name: '${escapeString(data.theme.name)}', version: '${data.theme.version}' },\n`}${data.themeFallbacks.length > 0 ? ` fallbacks: [${data.themeFallbacks.map((theme) => `{ name: '${escapeString(theme.name)}', version: '${theme.version}' }`).join(", ")}],\n` : ""} },\n`;
|
|
55
55
|
}
|
|
56
56
|
function buildTelemetrySection(data) {
|
|
57
57
|
if (!data.telemetry) return "";
|
|
58
58
|
return ` telemetry: {
|
|
59
59
|
spec: {
|
|
60
|
-
name: '${escapeString(data.telemetry.name)}'${
|
|
60
|
+
name: '${escapeString(data.telemetry.name)}'${data.telemetry.version !== void 0 ? `\n version: '${data.telemetry.version}'` : ""}
|
|
61
61
|
},
|
|
62
62
|
},\n`;
|
|
63
63
|
}
|
|
@@ -82,9 +82,9 @@ function buildRoutesSection(data) {
|
|
|
82
82
|
route.label ? `label: '${escapeString(route.label)}'` : null,
|
|
83
83
|
route.dataView ? `dataView: '${escapeString(route.dataView)}'` : null,
|
|
84
84
|
route.workflow ? `workflow: '${escapeString(route.workflow)}'` : null,
|
|
85
|
-
route.guardName ? `guard: { name: '${escapeString(route.guardName)}'${
|
|
85
|
+
route.guardName ? `guard: { name: '${escapeString(route.guardName)}'${route.guardVersion !== void 0 ? `, version: '${route.guardVersion}'` : ""} }` : null,
|
|
86
86
|
route.featureFlag ? `featureFlag: '${escapeString(route.featureFlag)}'` : null,
|
|
87
|
-
route.experimentName ? `experiment: { name: '${escapeString(route.experimentName)}'${
|
|
87
|
+
route.experimentName ? `experiment: { name: '${escapeString(route.experimentName)}'${route.experimentVersion !== void 0 ? `, version: '${route.experimentVersion}'` : ""} }` : null
|
|
88
88
|
].filter(Boolean).join(", ")} }`;
|
|
89
89
|
}).join(",\n")}\n ],\n`;
|
|
90
90
|
}
|
|
@@ -92,7 +92,7 @@ function formatCapabilityRef(key) {
|
|
|
92
92
|
return `{ key: '${escapeString(key)}' }`;
|
|
93
93
|
}
|
|
94
94
|
function formatExperimentRef(exp) {
|
|
95
|
-
const version =
|
|
95
|
+
const version = exp.version !== void 0 ? `, version: '${exp.version}'` : "";
|
|
96
96
|
return `{ name: '${escapeString(exp.name)}'${version} }`;
|
|
97
97
|
}
|
|
98
98
|
function toPascalCase(value) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-config.template.js","names":[],"sources":["../../src/templates/app-config.template.ts"],"sourcesContent":["import type { AppBlueprintSpecData } from '../types';\n\nexport function generateAppBlueprintSpec(data: AppBlueprintSpecData): string {\n const exportName =\n toPascalCase(data.name.split('.').pop() ?? 'App') + 'AppConfig';\n\n const capabilitiesSection = buildCapabilitiesSection(data);\n const featuresSection = buildFeaturesSection(data);\n const dataViewsSection = buildMappingSection('dataViews', data.dataViews);\n const workflowsSection = buildMappingSection('workflows', data.workflows);\n const policiesSection = buildPolicySection(data);\n const themeSection = buildThemeSection(data);\n const telemetrySection = buildTelemetrySection(data);\n const experimentsSection = buildExperimentsSection(data);\n const flagsSection = buildFeatureFlagsSection(data);\n const routesSection = buildRoutesSection(data);\n const notesSection = data.notes\n ? ` notes: '${escapeString(data.notes)}',\\n`\n : '';\n\n return `import type { AppBlueprintSpec } from '@contractspec/lib.contracts/app-config';\n\nexport const ${exportName}: AppBlueprintSpec = {\n meta: {\n key: '${escapeString(data.name)}',\n version: ${data.version},\n title: '${escapeString(data.title)}',\n description: '${escapeString(data.description)}',\n domain: '${escapeString(data.domain)}',\n owners: [${data.owners.map((owner) => `'${escapeString(owner)}'`).join(', ')}],\n tags: [${data.tags.map((tag) => `'${escapeString(tag)}'`).join(', ')}],\n stability: '${data.stability}',\n appId: '${escapeString(data.appId)}',\n },\n${capabilitiesSection}${featuresSection}${dataViewsSection}${workflowsSection}${policiesSection}${themeSection}${telemetrySection}${experimentsSection}${flagsSection}${routesSection}${notesSection}};\\n`;\n}\n\nfunction buildCapabilitiesSection(data: AppBlueprintSpecData): string {\n if (\n data.capabilitiesEnabled.length === 0 &&\n data.capabilitiesDisabled.length === 0\n ) {\n return '';\n }\n const enabled =\n data.capabilitiesEnabled.length > 0\n ? ` enabled: [${data.capabilitiesEnabled\n .map((key) => formatCapabilityRef(key))\n .join(', ')}],\\n`\n : '';\n const disabled =\n data.capabilitiesDisabled.length > 0\n ? ` disabled: [${data.capabilitiesDisabled\n .map((key) => formatCapabilityRef(key))\n .join(', ')}],\\n`\n : '';\n return ` capabilities: {\\n${enabled}${disabled} },\\n`;\n}\n\nfunction buildFeaturesSection(data: AppBlueprintSpecData): string {\n if (data.featureIncludes.length === 0 && data.featureExcludes.length === 0) {\n return '';\n }\n const include =\n data.featureIncludes.length > 0\n ? ` include: [${data.featureIncludes\n .map((key) => `{ key: '${escapeString(key)}' }`)\n .join(', ')}],\\n`\n : '';\n const exclude =\n data.featureExcludes.length > 0\n ? ` exclude: [${data.featureExcludes\n .map((key) => `{ key: '${escapeString(key)}' }`)\n .join(', ')}],\\n`\n : '';\n return ` features: {\\n${include}${exclude} },\\n`;\n}\n\nfunction buildMappingSection(\n prop: 'dataViews' | 'workflows',\n mappings: AppBlueprintSpecData['dataViews']\n): string {\n if (mappings.length === 0) return '';\n const body = mappings\n .map(\n (mapping) => ` ${mapping.slot}: {\n name: '${escapeString(mapping.name)}',\n ${typeof mapping.version === 'number' ? `version: ${mapping.version},` : ''}\n }`\n )\n .join(',\\n');\n return ` ${prop}: {\\n${body}\\n },\\n`;\n}\n\nfunction buildPolicySection(data: AppBlueprintSpecData): string {\n if (data.policyRefs.length === 0) return '';\n const entries = data.policyRefs\n .map(\n (policy) => ` {\n name: '${escapeString(policy.name)}'${typeof policy.version === 'number' ? `,\\n version: ${policy.version}` : ''}\n }`\n )\n .join(',\\n');\n return ` policies: [\\n${entries}\\n ],\\n`;\n}\n\nfunction buildThemeSection(data: AppBlueprintSpecData): string {\n if (!data.theme) return '';\n const primary = ` primary: { name: '${escapeString(data.theme.name)}', version: ${data.theme.version} },\\n`;\n const fallbacks =\n data.themeFallbacks.length > 0\n ? ` fallbacks: [${data.themeFallbacks\n .map(\n (theme) =>\n `{ name: '${escapeString(theme.name)}', version: ${theme.version} }`\n )\n .join(', ')}],\\n`\n : '';\n return ` theme: {\\n${primary}${fallbacks} },\\n`;\n}\n\nfunction buildTelemetrySection(data: AppBlueprintSpecData): string {\n if (!data.telemetry) return '';\n return ` telemetry: {\n spec: {\n name: '${escapeString(data.telemetry.name)}'${\n typeof data.telemetry.version === 'number'\n ? `,\\n version: ${data.telemetry.version}`\n : ''\n }\n },\n },\\n`;\n}\n\nfunction buildExperimentsSection(data: AppBlueprintSpecData): string {\n if (\n data.activeExperiments.length === 0 &&\n data.pausedExperiments.length === 0\n ) {\n return '';\n }\n const active =\n data.activeExperiments.length > 0\n ? ` active: [${data.activeExperiments\n .map((exp) => formatExperimentRef(exp))\n .join(', ')}],\\n`\n : '';\n const paused =\n data.pausedExperiments.length > 0\n ? ` paused: [${data.pausedExperiments\n .map((exp) => formatExperimentRef(exp))\n .join(', ')}],\\n`\n : '';\n return ` experiments: {\\n${active}${paused} },\\n`;\n}\n\nfunction buildFeatureFlagsSection(data: AppBlueprintSpecData): string {\n if (data.featureFlags.length === 0) return '';\n const flags = data.featureFlags\n .map(\n (flag) => ` {\n key: '${escapeString(flag.key)}',\n enabled: ${flag.enabled},\n ${flag.variant ? `variant: '${escapeString(flag.variant)}',` : ''}\n ${flag.description ? `description: '${escapeString(flag.description)}',` : ''}\n }`\n )\n .join(',\\n');\n return ` featureFlags: [\\n${flags}\\n ],\\n`;\n}\n\nfunction buildRoutesSection(data: AppBlueprintSpecData): string {\n if (data.routes.length === 0) return '';\n const routes = data.routes\n .map((route) => {\n const entries = [\n `path: '${escapeString(route.path)}'`,\n route.label ? `label: '${escapeString(route.label)}'` : null,\n route.dataView ? `dataView: '${escapeString(route.dataView)}'` : null,\n route.workflow ? `workflow: '${escapeString(route.workflow)}'` : null,\n route.guardName\n ? `guard: { name: '${escapeString(route.guardName)}'${\n typeof route.guardVersion === 'number'\n ? `, version: ${route.guardVersion}`\n : ''\n } }`\n : null,\n route.featureFlag\n ? `featureFlag: '${escapeString(route.featureFlag)}'`\n : null,\n route.experimentName\n ? `experiment: { name: '${escapeString(route.experimentName)}'${\n typeof route.experimentVersion === 'number'\n ? `, version: ${route.experimentVersion}`\n : ''\n } }`\n : null,\n ].filter(Boolean);\n return ` { ${entries.join(', ')} }`;\n })\n .join(',\\n');\n return ` routes: [\\n${routes}\\n ],\\n`;\n}\n\nfunction formatCapabilityRef(key: string): string {\n return `{ key: '${escapeString(key)}' }`;\n}\n\nfunction formatExperimentRef(exp: { name: string; version?: number }): string {\n const version =\n typeof exp.version === 'number' ? `, version: ${exp.version}` : '';\n return `{ name: '${escapeString(exp.name)}'${version} }`;\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 escapeString(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\");\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"app-config.template.js","names":[],"sources":["../../src/templates/app-config.template.ts"],"sourcesContent":["import type { AppBlueprintSpecData } from '../types';\n\nexport function generateAppBlueprintSpec(data: AppBlueprintSpecData): string {\n const exportName =\n toPascalCase(data.name.split('.').pop() ?? 'App') + 'AppConfig';\n\n const capabilitiesSection = buildCapabilitiesSection(data);\n const featuresSection = buildFeaturesSection(data);\n const dataViewsSection = buildMappingSection('dataViews', data.dataViews);\n const workflowsSection = buildMappingSection('workflows', data.workflows);\n const policiesSection = buildPolicySection(data);\n const themeSection = buildThemeSection(data);\n const telemetrySection = buildTelemetrySection(data);\n const experimentsSection = buildExperimentsSection(data);\n const flagsSection = buildFeatureFlagsSection(data);\n const routesSection = buildRoutesSection(data);\n const notesSection = data.notes\n ? ` notes: '${escapeString(data.notes)}',\\n`\n : '';\n\n return `import type { AppBlueprintSpec } from '@contractspec/lib.contracts/app-config';\n\nexport const ${exportName}: AppBlueprintSpec = {\n meta: {\n key: '${escapeString(data.name)}',\n version: ${data.version},\n title: '${escapeString(data.title)}',\n description: '${escapeString(data.description)}',\n domain: '${escapeString(data.domain)}',\n owners: [${data.owners.map((owner) => `'${escapeString(owner)}'`).join(', ')}],\n tags: [${data.tags.map((tag) => `'${escapeString(tag)}'`).join(', ')}],\n stability: '${data.stability}',\n appId: '${escapeString(data.appId)}',\n },\n${capabilitiesSection}${featuresSection}${dataViewsSection}${workflowsSection}${policiesSection}${themeSection}${telemetrySection}${experimentsSection}${flagsSection}${routesSection}${notesSection}};\\n`;\n}\n\nfunction buildCapabilitiesSection(data: AppBlueprintSpecData): string {\n if (\n data.capabilitiesEnabled.length === 0 &&\n data.capabilitiesDisabled.length === 0\n ) {\n return '';\n }\n const enabled =\n data.capabilitiesEnabled.length > 0\n ? ` enabled: [${data.capabilitiesEnabled\n .map((key) => formatCapabilityRef(key))\n .join(', ')}],\\n`\n : '';\n const disabled =\n data.capabilitiesDisabled.length > 0\n ? ` disabled: [${data.capabilitiesDisabled\n .map((key) => formatCapabilityRef(key))\n .join(', ')}],\\n`\n : '';\n return ` capabilities: {\\n${enabled}${disabled} },\\n`;\n}\n\nfunction buildFeaturesSection(data: AppBlueprintSpecData): string {\n if (data.featureIncludes.length === 0 && data.featureExcludes.length === 0) {\n return '';\n }\n const include =\n data.featureIncludes.length > 0\n ? ` include: [${data.featureIncludes\n .map((key) => `{ key: '${escapeString(key)}' }`)\n .join(', ')}],\\n`\n : '';\n const exclude =\n data.featureExcludes.length > 0\n ? ` exclude: [${data.featureExcludes\n .map((key) => `{ key: '${escapeString(key)}' }`)\n .join(', ')}],\\n`\n : '';\n return ` features: {\\n${include}${exclude} },\\n`;\n}\n\nfunction buildMappingSection(\n prop: 'dataViews' | 'workflows',\n mappings: AppBlueprintSpecData['dataViews']\n): string {\n if (mappings.length === 0) return '';\n const body = mappings\n .map(\n (mapping) => ` ${mapping.slot}: {\n name: '${escapeString(mapping.name)}',\n ${mapping.version !== undefined ? `version: '${mapping.version}',` : ''}\n }`\n )\n .join(',\\n');\n return ` ${prop}: {\\n${body}\\n },\\n`;\n}\n\nfunction buildPolicySection(data: AppBlueprintSpecData): string {\n if (data.policyRefs.length === 0) return '';\n const entries = data.policyRefs\n .map(\n (policy) => ` {\n name: '${escapeString(policy.name)}'${policy.version !== undefined ? `,\\n version: '${policy.version}'` : ''}\n }`\n )\n .join(',\\n');\n return ` policies: [\\n${entries}\\n ],\\n`;\n}\n\nfunction buildThemeSection(data: AppBlueprintSpecData): string {\n if (!data.theme) return '';\n const primary = ` primary: { name: '${escapeString(data.theme.name)}', version: '${data.theme.version}' },\\n`;\n const fallbacks =\n data.themeFallbacks.length > 0\n ? ` fallbacks: [${data.themeFallbacks\n .map(\n (theme) =>\n `{ name: '${escapeString(theme.name)}', version: '${theme.version}' }`\n )\n .join(', ')}],\\n`\n : '';\n return ` theme: {\\n${primary}${fallbacks} },\\n`;\n}\n\nfunction buildTelemetrySection(data: AppBlueprintSpecData): string {\n if (!data.telemetry) return '';\n return ` telemetry: {\n spec: {\n name: '${escapeString(data.telemetry.name)}'${\n data.telemetry.version !== undefined\n ? `\\n version: '${data.telemetry.version}'`\n : ''\n }\n },\n },\\n`;\n}\n\nfunction buildExperimentsSection(data: AppBlueprintSpecData): string {\n if (\n data.activeExperiments.length === 0 &&\n data.pausedExperiments.length === 0\n ) {\n return '';\n }\n const active =\n data.activeExperiments.length > 0\n ? ` active: [${data.activeExperiments\n .map((exp) => formatExperimentRef(exp))\n .join(', ')}],\\n`\n : '';\n const paused =\n data.pausedExperiments.length > 0\n ? ` paused: [${data.pausedExperiments\n .map((exp) => formatExperimentRef(exp))\n .join(', ')}],\\n`\n : '';\n return ` experiments: {\\n${active}${paused} },\\n`;\n}\n\nfunction buildFeatureFlagsSection(data: AppBlueprintSpecData): string {\n if (data.featureFlags.length === 0) return '';\n const flags = data.featureFlags\n .map(\n (flag) => ` {\n key: '${escapeString(flag.key)}',\n enabled: ${flag.enabled},\n ${flag.variant ? `variant: '${escapeString(flag.variant)}',` : ''}\n ${flag.description ? `description: '${escapeString(flag.description)}',` : ''}\n }`\n )\n .join(',\\n');\n return ` featureFlags: [\\n${flags}\\n ],\\n`;\n}\n\nfunction buildRoutesSection(data: AppBlueprintSpecData): string {\n if (data.routes.length === 0) return '';\n const routes = data.routes\n .map((route) => {\n const entries = [\n `path: '${escapeString(route.path)}'`,\n route.label ? `label: '${escapeString(route.label)}'` : null,\n route.dataView ? `dataView: '${escapeString(route.dataView)}'` : null,\n route.workflow ? `workflow: '${escapeString(route.workflow)}'` : null,\n route.guardName\n ? `guard: { name: '${escapeString(route.guardName)}'${\n route.guardVersion !== undefined\n ? `, version: '${route.guardVersion}'`\n : ''\n } }`\n : null,\n route.featureFlag\n ? `featureFlag: '${escapeString(route.featureFlag)}'`\n : null,\n route.experimentName\n ? `experiment: { name: '${escapeString(route.experimentName)}'${\n route.experimentVersion !== undefined\n ? `, version: '${route.experimentVersion}'`\n : ''\n } }`\n : null,\n ].filter(Boolean);\n return ` { ${entries.join(', ')} }`;\n })\n .join(',\\n');\n return ` routes: [\\n${routes}\\n ],\\n`;\n}\n\nfunction formatCapabilityRef(key: string): string {\n return `{ key: '${escapeString(key)}' }`;\n}\n\nfunction formatExperimentRef(exp: {\n name: string;\n version?: string | number;\n}): string {\n const version =\n exp.version !== undefined ? `, version: '${exp.version}'` : '';\n return `{ name: '${escapeString(exp.name)}'${version} }`;\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 escapeString(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\");\n}\n"],"mappings":"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"}
|
|
@@ -6,7 +6,7 @@ function generateExperimentSpec(data) {
|
|
|
6
6
|
${variant.overrides.map((override) => ` {
|
|
7
7
|
type: '${override.type}',
|
|
8
8
|
target: '${escapeString(override.target)}',
|
|
9
|
-
${typeof override.version === "
|
|
9
|
+
${typeof override.version === "string" ? `version: ${override.version},` : ""}
|
|
10
10
|
}`).join(",\n")}
|
|
11
11
|
],` : "";
|
|
12
12
|
return ` {
|
|
@@ -21,7 +21,7 @@ ${overrides}
|
|
|
21
21
|
const metrics = data.successMetrics?.length ? ` successMetrics: [
|
|
22
22
|
${data.successMetrics.map((metric) => ` {
|
|
23
23
|
name: '${escapeString(metric.name)}',
|
|
24
|
-
telemetryEvent: { name: '${escapeString(metric.eventName)}', version: ${metric.eventVersion} },
|
|
24
|
+
telemetryEvent: { name: '${escapeString(metric.eventName)}', version: ${typeof metric.eventVersion === "string" ? `'${metric.eventVersion}'` : metric.eventVersion} },
|
|
25
25
|
aggregation: '${metric.aggregation}',
|
|
26
26
|
${typeof metric.target === "number" ? `target: ${metric.target},` : ""}
|
|
27
27
|
}`).join(",\n")}
|
|
@@ -31,7 +31,7 @@ ${data.successMetrics.map((metric) => ` {
|
|
|
31
31
|
export const ${specVar}: ExperimentSpec = {
|
|
32
32
|
meta: {
|
|
33
33
|
key: '${escapeString(data.name)}',
|
|
34
|
-
version: ${data.version},
|
|
34
|
+
version: ${typeof data.version === "string" ? `'${data.version}'` : data.version},
|
|
35
35
|
title: '${escapeString(data.name)} experiment',
|
|
36
36
|
description: '${escapeString(data.description || "Describe the experiment goal.")}',
|
|
37
37
|
domain: '${escapeString(data.domain)}',
|
|
@@ -65,7 +65,7 @@ function renderAllocation(allocation) {
|
|
|
65
65
|
${allocation.rules.map((rule) => ` {
|
|
66
66
|
variantId: '${escapeString(rule.variantId)}',
|
|
67
67
|
${typeof rule.percentage === "number" ? `percentage: ${rule.percentage},` : ""}
|
|
68
|
-
${rule.policy ? `policy: { name: '${escapeString(rule.policy.name)}'${typeof rule.policy.version === "
|
|
68
|
+
${rule.policy ? `policy: { name: '${escapeString(rule.policy.name)}'${rule.policy.version !== void 0 ? `, version: ${typeof rule.policy.version === "string" ? `'${rule.policy.version}'` : rule.policy.version}` : ""} },` : ""}
|
|
69
69
|
${rule.expression ? `expression: '${escapeString(rule.expression)}',` : ""}
|
|
70
70
|
}`).join(",\n")}
|
|
71
71
|
],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"experiment.template.js","names":[],"sources":["../../src/templates/experiment.template.ts"],"sourcesContent":["import type { ExperimentSpecData } from '../types';\n\nexport function generateExperimentSpec(data: ExperimentSpecData): string {\n const specVar =\n toPascalCase(data.name.split('.').pop() ?? 'Experiment') + 'Experiment';\n\n const variants = data.variants\n .map((variant) => {\n const overrides = variant.overrides?.length\n ? ` overrides: [\n${variant.overrides\n .map(\n (override) => ` {\n type: '${override.type}',\n target: '${escapeString(override.target)}',\n ${typeof override.version === '
|
|
1
|
+
{"version":3,"file":"experiment.template.js","names":[],"sources":["../../src/templates/experiment.template.ts"],"sourcesContent":["import type { ExperimentSpecData } from '../types';\n\nexport function generateExperimentSpec(data: ExperimentSpecData): string {\n const specVar =\n toPascalCase(data.name.split('.').pop() ?? 'Experiment') + 'Experiment';\n\n const variants = data.variants\n .map((variant) => {\n const overrides = variant.overrides?.length\n ? ` overrides: [\n${variant.overrides\n .map(\n (override) => ` {\n type: '${override.type}',\n target: '${escapeString(override.target)}',\n ${typeof override.version === 'string' ? `version: ${override.version},` : ''}\n }`\n )\n .join(',\\n')}\n ],`\n : '';\n return ` {\n id: '${escapeString(variant.id)}',\n name: '${escapeString(variant.name)}',\n ${variant.description ? `description: '${escapeString(variant.description)}',` : ''}\n ${typeof variant.weight === 'number' ? `weight: ${variant.weight},` : ''}\n${overrides}\n }`;\n })\n .join(',\\n');\n\n const allocation = renderAllocation(data.allocation);\n\n const metrics = data.successMetrics?.length\n ? ` successMetrics: [\n${data.successMetrics\n .map(\n (metric) => ` {\n name: '${escapeString(metric.name)}',\n telemetryEvent: { name: '${escapeString(metric.eventName)}', version: ${typeof metric.eventVersion === 'string' ? `'${metric.eventVersion}'` : metric.eventVersion} },\n aggregation: '${metric.aggregation}',\n ${typeof metric.target === 'number' ? `target: ${metric.target},` : ''}\n }`\n )\n .join(',\\n')}\n ],`\n : '';\n\n return `import type { ExperimentSpec } from '@contractspec/lib.contracts/experiments';\n\nexport const ${specVar}: ExperimentSpec = {\n meta: {\n key: '${escapeString(data.name)}',\n version: ${typeof data.version === 'string' ? `'${data.version}'` : data.version},\n title: '${escapeString(data.name)} experiment',\n description: '${escapeString(\n data.description || 'Describe the experiment goal.'\n )}',\n domain: '${escapeString(data.domain)}',\n owners: [${data.owners.map((owner) => `'${escapeString(owner)}'`).join(', ')}],\n tags: [${data.tags.map((tag) => `'${escapeString(tag)}'`).join(', ')}],\n stability: '${data.stability}',\n },\n controlVariant: '${escapeString(data.controlVariant)}',\n variants: [\n${variants}\n ],\n allocation: ${allocation},\n${metrics}\n};\n`;\n}\n\nfunction renderAllocation(\n allocation: ExperimentSpecData['allocation']\n): string {\n switch (allocation.type) {\n case 'random':\n return `{\n type: 'random',\n ${allocation.salt ? `salt: '${escapeString(allocation.salt)}',` : ''}\n }`;\n case 'sticky':\n return `{\n type: 'sticky',\n attribute: '${allocation.attribute}',\n ${allocation.salt ? `salt: '${escapeString(allocation.salt)}',` : ''}\n }`;\n case 'targeted':\n return `{\n type: 'targeted',\n rules: [\n${allocation.rules\n .map(\n (rule) => ` {\n variantId: '${escapeString(rule.variantId)}',\n ${typeof rule.percentage === 'number' ? `percentage: ${rule.percentage},` : ''}\n ${\n rule.policy\n ? `policy: { name: '${escapeString(rule.policy.name)}'${rule.policy.version !== undefined ? `, version: ${typeof rule.policy.version === 'string' ? `'${rule.policy.version}'` : rule.policy.version}` : ''} },`\n : ''\n }\n ${rule.expression ? `expression: '${escapeString(rule.expression)}',` : ''}\n }`\n )\n .join(',\\n')}\n ],\n fallback: '${allocation.fallback ?? 'control'}',\n }`;\n default:\n return renderUnsupportedAllocation(allocation);\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 escapeString(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\");\n}\n\nfunction renderUnsupportedAllocation(allocation: never): string {\n throw new Error(\n `Unsupported allocation type ${allocation as unknown as string}`\n );\n}\n"],"mappings":"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"}
|
|
@@ -5,10 +5,10 @@ function generateIntegrationSpec(data) {
|
|
|
5
5
|
const registerFn = `register${specName}Integration`;
|
|
6
6
|
const supportedModes = data.supportedModes.length ? data.supportedModes : ["managed"];
|
|
7
7
|
const supportedModesLine = supportedModes.map((mode) => `'${mode}'`).join(", ");
|
|
8
|
-
const provides = data.capabilitiesProvided.map((cap) => ` { key: '${cap.key}', version: ${cap.version} }`).join(",\n");
|
|
8
|
+
const provides = data.capabilitiesProvided.map((cap) => ` { key: '${cap.key}', version: '${cap.version}' }`).join(",\n");
|
|
9
9
|
const requires = data.capabilitiesRequired.length > 0 ? ` requires: [
|
|
10
10
|
${data.capabilitiesRequired.map((req) => {
|
|
11
|
-
const version =
|
|
11
|
+
const version = req.version !== void 0 ? `, version: '${req.version}'` : "";
|
|
12
12
|
const optional = req.optional ? ", optional: true" : "";
|
|
13
13
|
const reason = req.reason ? `, reason: '${escape(req.reason)}'` : "";
|
|
14
14
|
return ` { key: '${req.key}'${version}${optional}${reason} }`;
|
|
@@ -28,7 +28,7 @@ import type { IntegrationSpecRegistry } from '@contractspec/lib.contracts/integr
|
|
|
28
28
|
export const ${varName}: IntegrationSpec = {
|
|
29
29
|
meta: {
|
|
30
30
|
key: '${data.name}',
|
|
31
|
-
version: ${data.version},
|
|
31
|
+
version: ${typeof data.version === "string" ? `'${data.version}'` : data.version},
|
|
32
32
|
category: '${data.category}',
|
|
33
33
|
displayName: '${escape(data.displayName)}',
|
|
34
34
|
title: '${escape(data.title)}',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration.template.js","names":["entries: string[]"],"sources":["../../src/templates/integration.template.ts"],"sourcesContent":["import type {\n IntegrationConfigFieldData,\n IntegrationSecretFieldData,\n IntegrationSpecData,\n Stability,\n} from '../types';\n\nexport function generateIntegrationSpec(data: IntegrationSpecData): string {\n const specName = toPascalCase(data.name.split('.').pop() ?? 'Integration');\n const varName = `${specName}IntegrationSpec`;\n const registerFn = `register${specName}Integration`;\n\n const supportedModes = data.supportedModes.length\n ? data.supportedModes\n : ['managed'];\n const supportedModesLine = supportedModes\n .map((mode) => `'${mode}'`)\n .join(', ');\n\n const provides = data.capabilitiesProvided\n .map((cap) => ` { key: '${cap.key}', version: ${cap.version} }`)\n .join(',\\n');\n\n const requires =\n data.capabilitiesRequired.length > 0\n ? ` requires: [\n${data.capabilitiesRequired\n .map((req) => {\n const version =\n typeof req.version === 'number' ? `, version: ${req.version}` : '';\n const optional = req.optional ? ', optional: true' : '';\n const reason = req.reason ? `, reason: '${escape(req.reason)}'` : '';\n return ` { key: '${req.key}'${version}${optional}${reason} }`;\n })\n .join(',\\n')}\n ],`\n : '';\n\n const configSchema = renderConfigSchema(data.configFields);\n const configExample = renderConfigExample(data.configFields);\n const secretSchema = renderSecretSchema(data.secretFields);\n const secretExample = renderSecretExample(data.secretFields);\n const docsUrl = data.docsUrl ? ` docsUrl: '${escape(data.docsUrl)}',\\n` : '';\n const constraints = renderConstraints(data.rateLimitRpm, data.rateLimitRph);\n const byokSetup = renderByokSetup(\n supportedModes,\n data.byokSetupInstructions,\n data.byokRequiredScopes\n );\n\n return `import { StabilityEnum } from '@contractspec/lib.contracts/ownership';\nimport type { IntegrationSpec } from '@contractspec/lib.contracts/integrations/spec';\nimport type { IntegrationSpecRegistry } from '@contractspec/lib.contracts/integrations/spec';\n\nexport const ${varName}: IntegrationSpec = {\n meta: {\n key: '${data.name}',\n version: ${data.version},\n category: '${data.category}',\n displayName: '${escape(data.displayName)}',\n title: '${escape(data.title)}',\n description: '${escape(data.description)}',\n domain: '${escape(data.domain)}',\n owners: [${data.owners.map((owner) => `'${escape(owner)}'`).join(', ')}],\n tags: [${data.tags.map((tag) => `'${escape(tag)}'`).join(', ')}],\n stability: StabilityEnum.${stabilityToEnum(data.stability)},\n },\n supportedModes: [${supportedModesLine}],\n capabilities: {\n provides: [\n${provides}\n ],\n${requires.length > 0 ? `${requires}\\n` : ''} },\n configSchema: {\n${configSchema}\n example: ${configExample},\n },\n secretSchema: {\n${secretSchema}\n example: ${secretExample},\n },\n${docsUrl}${constraints}${byokSetup} healthCheck: {\n method: '${data.healthCheckMethod}',\n timeoutMs: ${data.healthCheckTimeoutMs},\n },\n};\n\nexport function ${registerFn}(\n registry: IntegrationSpecRegistry\n): IntegrationSpecRegistry {\n return registry.register(${varName});\n}\n`;\n}\n\nfunction renderConfigSchema(fields: IntegrationConfigFieldData[]): string {\n const requiredFields = fields.filter((field) => field.required);\n const requiredBlock =\n requiredFields.length > 0\n ? ` required: [${requiredFields\n .map((field) => `'${field.key}'`)\n .join(', ')}],\n`\n : '';\n\n const properties = fields.length\n ? fields\n .map((field) => {\n const description = field.description\n ? `, description: '${escape(field.description)}'`\n : '';\n return ` ${field.key}: { type: '${mapConfigType(\n field.type\n )}'${description} }`;\n })\n .join(',\\n')\n : '';\n\n return ` schema: {\n type: 'object',\n${requiredBlock} properties: {\n${properties || ' '}\n },\n },\\n`;\n}\n\nfunction renderSecretSchema(fields: IntegrationSecretFieldData[]): string {\n const requiredFields = fields.filter((field) => field.required);\n const requiredBlock =\n requiredFields.length > 0\n ? ` required: [${requiredFields\n .map((field) => `'${field.key}'`)\n .join(', ')}],\n`\n : '';\n\n const properties = fields.length\n ? fields\n .map((field) => {\n const description = field.description\n ? `, description: '${escape(field.description)}'`\n : '';\n return ` ${field.key}: { type: 'string'${description} }`;\n })\n .join(',\\n')\n : '';\n\n return ` schema: {\n type: 'object',\n${requiredBlock} properties: {\n${properties || ' '}\n },\n },\\n`;\n}\n\nfunction renderConfigExample(fields: IntegrationConfigFieldData[]): string {\n if (fields.length === 0) {\n return `{}`;\n }\n\n const exampleEntries = fields.map((field) => {\n switch (field.type) {\n case 'number':\n return ` ${field.key}: 0`;\n case 'boolean':\n return ` ${field.key}: true`;\n case 'string':\n default:\n return ` ${field.key}: '${field.key.toUpperCase()}_VALUE'`;\n }\n });\n\n return `{\n${exampleEntries.join(',\\n')}\n }`;\n}\n\nfunction renderSecretExample(fields: IntegrationSecretFieldData[]): string {\n if (fields.length === 0) {\n return `{}`;\n }\n\n const exampleEntries = fields.map(\n (field) => ` ${field.key}: '${field.key.toUpperCase()}_SECRET'`\n );\n\n return `{\n${exampleEntries.join(',\\n')}\n }`;\n}\n\nfunction renderConstraints(rpm?: number, rph?: number): string {\n if (rpm == null && rph == null) return '';\n const entries: string[] = [];\n if (rpm != null) entries.push(` rpm: ${rpm}`);\n if (rph != null) entries.push(` rph: ${rph}`);\n return ` constraints: {\n rateLimit: {\n${entries.join(',\\n')}\n },\n },\n`;\n}\n\nfunction renderByokSetup(\n modes: string[],\n instructions?: string,\n scopes?: string[]\n): string {\n if (!modes.includes('byok')) {\n return '';\n }\n\n const instructionsLine = instructions\n ? ` setupInstructions: '${escape(instructions)}',\\n`\n : '';\n const scopesLine =\n scopes && scopes.length\n ? ` requiredScopes: [${scopes\n .map((scope) => `'${escape(scope)}'`)\n .join(', ')}],\\n`\n : '';\n\n if (!instructionsLine && !scopesLine) {\n return '';\n }\n\n return ` byokSetup: {\n${instructionsLine}${scopesLine} },\n`;\n}\n\nfunction mapConfigType(type: IntegrationConfigFieldData['type']): string {\n switch (type) {\n case 'number':\n return 'number';\n case 'boolean':\n return 'boolean';\n case 'string':\n default:\n return 'string';\n }\n}\n\nfunction stabilityToEnum(stability: Stability): string {\n switch (stability) {\n case 'beta':\n return 'Beta';\n case 'stable':\n return 'Stable';\n case 'deprecated':\n return 'Deprecated';\n case 'experimental':\n default:\n return 'Experimental';\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, '\\\\`').replace(/'/g, \"\\\\'\");\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"integration.template.js","names":["entries: string[]"],"sources":["../../src/templates/integration.template.ts"],"sourcesContent":["import type {\n IntegrationConfigFieldData,\n IntegrationSecretFieldData,\n IntegrationSpecData,\n Stability,\n} from '../types';\n\nexport function generateIntegrationSpec(data: IntegrationSpecData): string {\n const specName = toPascalCase(data.name.split('.').pop() ?? 'Integration');\n const varName = `${specName}IntegrationSpec`;\n const registerFn = `register${specName}Integration`;\n\n const supportedModes = data.supportedModes.length\n ? data.supportedModes\n : ['managed'];\n const supportedModesLine = supportedModes\n .map((mode) => `'${mode}'`)\n .join(', ');\n\n const provides = data.capabilitiesProvided\n .map((cap) => ` { key: '${cap.key}', version: '${cap.version}' }`)\n .join(',\\n');\n\n const requires =\n data.capabilitiesRequired.length > 0\n ? ` requires: [\n${data.capabilitiesRequired\n .map((req) => {\n const version =\n req.version !== undefined ? `, version: '${req.version}'` : '';\n const optional = req.optional ? ', optional: true' : '';\n const reason = req.reason ? `, reason: '${escape(req.reason)}'` : '';\n return ` { key: '${req.key}'${version}${optional}${reason} }`;\n })\n .join(',\\n')}\n ],`\n : '';\n\n const configSchema = renderConfigSchema(data.configFields);\n const configExample = renderConfigExample(data.configFields);\n const secretSchema = renderSecretSchema(data.secretFields);\n const secretExample = renderSecretExample(data.secretFields);\n const docsUrl = data.docsUrl ? ` docsUrl: '${escape(data.docsUrl)}',\\n` : '';\n const constraints = renderConstraints(data.rateLimitRpm, data.rateLimitRph);\n const byokSetup = renderByokSetup(\n supportedModes,\n data.byokSetupInstructions,\n data.byokRequiredScopes\n );\n\n return `import { StabilityEnum } from '@contractspec/lib.contracts/ownership';\nimport type { IntegrationSpec } from '@contractspec/lib.contracts/integrations/spec';\nimport type { IntegrationSpecRegistry } from '@contractspec/lib.contracts/integrations/spec';\n\nexport const ${varName}: IntegrationSpec = {\n meta: {\n key: '${data.name}',\n version: ${typeof data.version === 'string' ? `'${data.version}'` : data.version},\n category: '${data.category}',\n displayName: '${escape(data.displayName)}',\n title: '${escape(data.title)}',\n description: '${escape(data.description)}',\n domain: '${escape(data.domain)}',\n owners: [${data.owners.map((owner) => `'${escape(owner)}'`).join(', ')}],\n tags: [${data.tags.map((tag) => `'${escape(tag)}'`).join(', ')}],\n stability: StabilityEnum.${stabilityToEnum(data.stability)},\n },\n supportedModes: [${supportedModesLine}],\n capabilities: {\n provides: [\n${provides}\n ],\n${requires.length > 0 ? `${requires}\\n` : ''} },\n configSchema: {\n${configSchema}\n example: ${configExample},\n },\n secretSchema: {\n${secretSchema}\n example: ${secretExample},\n },\n${docsUrl}${constraints}${byokSetup} healthCheck: {\n method: '${data.healthCheckMethod}',\n timeoutMs: ${data.healthCheckTimeoutMs},\n },\n};\n\nexport function ${registerFn}(\n registry: IntegrationSpecRegistry\n): IntegrationSpecRegistry {\n return registry.register(${varName});\n}\n`;\n}\n\nfunction renderConfigSchema(fields: IntegrationConfigFieldData[]): string {\n const requiredFields = fields.filter((field) => field.required);\n const requiredBlock =\n requiredFields.length > 0\n ? ` required: [${requiredFields\n .map((field) => `'${field.key}'`)\n .join(', ')}],\n`\n : '';\n\n const properties = fields.length\n ? fields\n .map((field) => {\n const description = field.description\n ? `, description: '${escape(field.description)}'`\n : '';\n return ` ${field.key}: { type: '${mapConfigType(\n field.type\n )}'${description} }`;\n })\n .join(',\\n')\n : '';\n\n return ` schema: {\n type: 'object',\n${requiredBlock} properties: {\n${properties || ' '}\n },\n },\\n`;\n}\n\nfunction renderSecretSchema(fields: IntegrationSecretFieldData[]): string {\n const requiredFields = fields.filter((field) => field.required);\n const requiredBlock =\n requiredFields.length > 0\n ? ` required: [${requiredFields\n .map((field) => `'${field.key}'`)\n .join(', ')}],\n`\n : '';\n\n const properties = fields.length\n ? fields\n .map((field) => {\n const description = field.description\n ? `, description: '${escape(field.description)}'`\n : '';\n return ` ${field.key}: { type: 'string'${description} }`;\n })\n .join(',\\n')\n : '';\n\n return ` schema: {\n type: 'object',\n${requiredBlock} properties: {\n${properties || ' '}\n },\n },\\n`;\n}\n\nfunction renderConfigExample(fields: IntegrationConfigFieldData[]): string {\n if (fields.length === 0) {\n return `{}`;\n }\n\n const exampleEntries = fields.map((field) => {\n switch (field.type) {\n case 'number':\n return ` ${field.key}: 0`;\n case 'boolean':\n return ` ${field.key}: true`;\n case 'string':\n default:\n return ` ${field.key}: '${field.key.toUpperCase()}_VALUE'`;\n }\n });\n\n return `{\n${exampleEntries.join(',\\n')}\n }`;\n}\n\nfunction renderSecretExample(fields: IntegrationSecretFieldData[]): string {\n if (fields.length === 0) {\n return `{}`;\n }\n\n const exampleEntries = fields.map(\n (field) => ` ${field.key}: '${field.key.toUpperCase()}_SECRET'`\n );\n\n return `{\n${exampleEntries.join(',\\n')}\n }`;\n}\n\nfunction renderConstraints(rpm?: number, rph?: number): string {\n if (rpm == null && rph == null) return '';\n const entries: string[] = [];\n if (rpm != null) entries.push(` rpm: ${rpm}`);\n if (rph != null) entries.push(` rph: ${rph}`);\n return ` constraints: {\n rateLimit: {\n${entries.join(',\\n')}\n },\n },\n`;\n}\n\nfunction renderByokSetup(\n modes: string[],\n instructions?: string,\n scopes?: string[]\n): string {\n if (!modes.includes('byok')) {\n return '';\n }\n\n const instructionsLine = instructions\n ? ` setupInstructions: '${escape(instructions)}',\\n`\n : '';\n const scopesLine =\n scopes && scopes.length\n ? ` requiredScopes: [${scopes\n .map((scope) => `'${escape(scope)}'`)\n .join(', ')}],\\n`\n : '';\n\n if (!instructionsLine && !scopesLine) {\n return '';\n }\n\n return ` byokSetup: {\n${instructionsLine}${scopesLine} },\n`;\n}\n\nfunction mapConfigType(type: IntegrationConfigFieldData['type']): string {\n switch (type) {\n case 'number':\n return 'number';\n case 'boolean':\n return 'boolean';\n case 'string':\n default:\n return 'string';\n }\n}\n\nfunction stabilityToEnum(stability: Stability): string {\n switch (stability) {\n case 'beta':\n return 'Beta';\n case 'stable':\n return 'Stable';\n case 'deprecated':\n return 'Deprecated';\n case 'experimental':\n default:\n return 'Experimental';\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, '\\\\`').replace(/'/g, \"\\\\'\");\n}\n"],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -34,11 +34,11 @@ interface WorkflowStepData {
|
|
|
34
34
|
description?: string;
|
|
35
35
|
operation?: {
|
|
36
36
|
name: string;
|
|
37
|
-
version:
|
|
37
|
+
version: string;
|
|
38
38
|
};
|
|
39
39
|
form?: {
|
|
40
40
|
key: string;
|
|
41
|
-
version:
|
|
41
|
+
version: string;
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
interface WorkflowTransitionData {
|
|
@@ -70,11 +70,11 @@ interface DataViewSpecData extends BaseSpecData {
|
|
|
70
70
|
kind: DataViewKind;
|
|
71
71
|
primaryOperation: {
|
|
72
72
|
name: string;
|
|
73
|
-
version:
|
|
73
|
+
version: string;
|
|
74
74
|
};
|
|
75
75
|
itemOperation?: {
|
|
76
76
|
name: string;
|
|
77
|
-
version:
|
|
77
|
+
version: string;
|
|
78
78
|
};
|
|
79
79
|
fields: DataViewFieldData[];
|
|
80
80
|
primaryField?: string;
|
|
@@ -96,7 +96,7 @@ interface TelemetryAnomalyRuleData {
|
|
|
96
96
|
}
|
|
97
97
|
interface TelemetryEventData {
|
|
98
98
|
name: string;
|
|
99
|
-
version:
|
|
99
|
+
version: string;
|
|
100
100
|
what: string;
|
|
101
101
|
who?: string;
|
|
102
102
|
why?: string;
|
|
@@ -128,7 +128,7 @@ interface TelemetrySpecData extends BaseSpecData {
|
|
|
128
128
|
interface ExperimentVariantOverrideData {
|
|
129
129
|
type: 'dataView' | 'workflow' | 'theme' | 'policy' | 'presentation';
|
|
130
130
|
target: string;
|
|
131
|
-
version?:
|
|
131
|
+
version?: string;
|
|
132
132
|
}
|
|
133
133
|
interface ExperimentVariantData {
|
|
134
134
|
id: string;
|
|
@@ -142,7 +142,7 @@ interface TargetingRuleData {
|
|
|
142
142
|
percentage?: number;
|
|
143
143
|
policy?: {
|
|
144
144
|
name: string;
|
|
145
|
-
version?:
|
|
145
|
+
version?: string;
|
|
146
146
|
};
|
|
147
147
|
expression?: string;
|
|
148
148
|
}
|
|
@@ -164,7 +164,7 @@ type ExperimentAllocationData = RandomAllocationData | StickyAllocationData | Ta
|
|
|
164
164
|
interface ExperimentMetricData {
|
|
165
165
|
name: string;
|
|
166
166
|
eventName: string;
|
|
167
|
-
eventVersion:
|
|
167
|
+
eventVersion: string;
|
|
168
168
|
aggregation: 'count' | 'avg' | 'p75' | 'p90' | 'p95' | 'p99';
|
|
169
169
|
target?: number;
|
|
170
170
|
}
|
|
@@ -178,7 +178,7 @@ interface ExperimentSpecData extends BaseSpecData {
|
|
|
178
178
|
interface AppConfigMappingData {
|
|
179
179
|
slot: string;
|
|
180
180
|
name: string;
|
|
181
|
-
version?:
|
|
181
|
+
version?: string;
|
|
182
182
|
}
|
|
183
183
|
interface AppConfigFeatureFlagData {
|
|
184
184
|
key: string;
|
|
@@ -192,10 +192,10 @@ interface AppRouteConfigData {
|
|
|
192
192
|
dataView?: string;
|
|
193
193
|
workflow?: string;
|
|
194
194
|
guardName?: string;
|
|
195
|
-
guardVersion?:
|
|
195
|
+
guardVersion?: string;
|
|
196
196
|
featureFlag?: string;
|
|
197
197
|
experimentName?: string;
|
|
198
|
-
experimentVersion?:
|
|
198
|
+
experimentVersion?: string;
|
|
199
199
|
}
|
|
200
200
|
interface AppBlueprintSpecData extends BaseSpecData {
|
|
201
201
|
title: string;
|
|
@@ -209,27 +209,27 @@ interface AppBlueprintSpecData extends BaseSpecData {
|
|
|
209
209
|
workflows: AppConfigMappingData[];
|
|
210
210
|
policyRefs: {
|
|
211
211
|
name: string;
|
|
212
|
-
version?:
|
|
212
|
+
version?: string;
|
|
213
213
|
}[];
|
|
214
214
|
theme?: {
|
|
215
215
|
name: string;
|
|
216
|
-
version:
|
|
216
|
+
version: string;
|
|
217
217
|
};
|
|
218
218
|
themeFallbacks: {
|
|
219
219
|
name: string;
|
|
220
|
-
version:
|
|
220
|
+
version: string;
|
|
221
221
|
}[];
|
|
222
222
|
telemetry?: {
|
|
223
223
|
name: string;
|
|
224
|
-
version?:
|
|
224
|
+
version?: string;
|
|
225
225
|
};
|
|
226
226
|
activeExperiments: {
|
|
227
227
|
name: string;
|
|
228
|
-
version?:
|
|
228
|
+
version?: string;
|
|
229
229
|
}[];
|
|
230
230
|
pausedExperiments: {
|
|
231
231
|
name: string;
|
|
232
|
-
version?:
|
|
232
|
+
version?: string;
|
|
233
233
|
}[];
|
|
234
234
|
featureFlags: AppConfigFeatureFlagData[];
|
|
235
235
|
routes: AppRouteConfigData[];
|
|
@@ -266,11 +266,11 @@ type IntegrationOwnershipModeData = 'managed' | 'byok';
|
|
|
266
266
|
type IntegrationHealthCheckMethod = 'ping' | 'list' | 'custom';
|
|
267
267
|
interface IntegrationCapabilityRefData {
|
|
268
268
|
key: string;
|
|
269
|
-
version:
|
|
269
|
+
version: string;
|
|
270
270
|
}
|
|
271
271
|
interface IntegrationCapabilityRequirementData {
|
|
272
272
|
key: string;
|
|
273
|
-
version?:
|
|
273
|
+
version?: string;
|
|
274
274
|
optional?: boolean;
|
|
275
275
|
reason?: string;
|
|
276
276
|
}
|
|
@@ -312,7 +312,7 @@ interface KnowledgeSpaceSpecData extends BaseSpecData {
|
|
|
312
312
|
category: KnowledgeCategoryData;
|
|
313
313
|
retention: KnowledgeRetentionData;
|
|
314
314
|
policyName?: string;
|
|
315
|
-
policyVersion?:
|
|
315
|
+
policyVersion?: string;
|
|
316
316
|
trustLevel: KnowledgeTrustLevel;
|
|
317
317
|
automationWritable: boolean;
|
|
318
318
|
embeddingModel?: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contractspec/bundle.workspace",
|
|
3
|
-
"version": "1.45.
|
|
3
|
+
"version": "1.45.2",
|
|
4
4
|
"description": "Workspace utilities for monorepo development",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"contractspec",
|
|
@@ -32,24 +32,20 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@ai-sdk/anthropic": "3.0.1",
|
|
35
|
-
"@ai-sdk/google": "3.0.1",
|
|
36
|
-
"@ai-sdk/mistral": "3.0.1",
|
|
37
35
|
"@ai-sdk/openai": "3.0.1",
|
|
38
36
|
"ollama-ai-provider": "^1.2.0",
|
|
39
|
-
"@contractspec/module.workspace": "1.45.
|
|
40
|
-
"@contractspec/lib.contracts": "1.45.
|
|
41
|
-
"@contractspec/lib.contracts-transformers": "1.45.
|
|
42
|
-
"@contractspec/lib.ai-providers": "1.45.
|
|
43
|
-
"@contractspec/lib.schema": "1.45.1",
|
|
44
|
-
"@contractspec/lib.testing": "1.45.1",
|
|
37
|
+
"@contractspec/module.workspace": "1.45.2",
|
|
38
|
+
"@contractspec/lib.contracts": "1.45.2",
|
|
39
|
+
"@contractspec/lib.contracts-transformers": "1.45.2",
|
|
40
|
+
"@contractspec/lib.ai-providers": "1.45.2",
|
|
45
41
|
"ai": "6.0.3",
|
|
46
42
|
"zod": "^4.1.13",
|
|
47
43
|
"glob": "^13.0.0",
|
|
48
44
|
"chokidar": "^5.0.0"
|
|
49
45
|
},
|
|
50
46
|
"devDependencies": {
|
|
51
|
-
"@contractspec/tool.tsdown": "1.45.
|
|
52
|
-
"@contractspec/tool.typescript": "1.45.
|
|
47
|
+
"@contractspec/tool.tsdown": "1.45.2",
|
|
48
|
+
"@contractspec/tool.typescript": "1.45.2",
|
|
53
49
|
"@types/node": "^22.10.2",
|
|
54
50
|
"tsdown": "^0.18.3",
|
|
55
51
|
"typescript": "^5.9.3"
|