@contractspec/bundle.workspace 0.0.0-canary-20260113170453 → 0.0.0-canary-20260114030712
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/openapi/import-service.d.mts +2 -2
- package/dist/services/openapi/import-service.d.mts.map +1 -1
- package/dist/services/openapi/import-service.mjs +3 -6
- package/dist/services/openapi/import-service.mjs.map +1 -1
- package/dist/services/openapi/sync-service.d.mts +2 -2
- package/dist/services/openapi/sync-service.d.mts.map +1 -1
- package/dist/services/openapi/sync-service.mjs.map +1 -1
- package/dist/services/rulesync.d.mts.map +1 -1
- package/dist/services/rulesync.mjs +4 -3
- package/dist/services/rulesync.mjs.map +1 -1
- package/dist/services/sync.d.mts.map +1 -1
- package/package.json +8 -8
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { FsAdapter } from "../../ports/fs.mjs";
|
|
2
2
|
import { LoggerAdapter } from "../../ports/logger.mjs";
|
|
3
3
|
import { OpenApiImportServiceOptions, OpenApiImportServiceResult } from "./types.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { ResolvedContractsrcConfig } from "@contractspec/lib.contracts";
|
|
5
5
|
|
|
6
6
|
//#region src/services/openapi/import-service.d.ts
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Import ContractSpec specs from an OpenAPI document.
|
|
10
10
|
*/
|
|
11
|
-
declare function importFromOpenApiService(contractspecOptions:
|
|
11
|
+
declare function importFromOpenApiService(contractspecOptions: ResolvedContractsrcConfig, options: OpenApiImportServiceOptions, adapters: {
|
|
12
12
|
fs: FsAdapter;
|
|
13
13
|
logger: LoggerAdapter;
|
|
14
14
|
}): Promise<OpenApiImportServiceResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"import-service.d.mts","names":[],"sources":["../../../src/services/openapi/import-service.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"import-service.d.mts","names":[],"sources":["../../../src/services/openapi/import-service.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AAwDqC,iBAHf,wBAAA,CAGe,mBAAA,EAFd,yBAEc,EAAA,OAAA,EAD1B,2BAC0B,EAAA,QAAA,EAAA;EAC1B,EAAA,EADO,SACP;EAAR,MAAA,EADkC,aAClC;CAAO,CAAA,EAAP,OAAO,CAAC,0BAAD,CAAA"}
|
|
@@ -10,14 +10,11 @@ import { importFromOpenApi, parseOpenApi } from "@contractspec/lib.contracts-tra
|
|
|
10
10
|
*/
|
|
11
11
|
function getOutputDir(type, options, config) {
|
|
12
12
|
if (options.outputDir) return options.outputDir;
|
|
13
|
-
const baseDir = config.outputDir
|
|
14
|
-
const conventions = config.conventions
|
|
15
|
-
operations: "operations",
|
|
16
|
-
events: "events"
|
|
17
|
-
};
|
|
13
|
+
const baseDir = config.outputDir;
|
|
14
|
+
const conventions = config.conventions;
|
|
18
15
|
switch (type) {
|
|
19
16
|
case "operation": return join(baseDir, conventions.operations.split("|")[0] ?? "operations");
|
|
20
|
-
case "event": return join(baseDir, conventions.events
|
|
17
|
+
case "event": return join(baseDir, conventions.events);
|
|
21
18
|
case "model": return join(baseDir, "models");
|
|
22
19
|
default: return baseDir;
|
|
23
20
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"import-service.mjs","names":["path"],"sources":["../../../src/services/openapi/import-service.ts"],"sourcesContent":["/**\n * OpenAPI import service - imports specs from OpenAPI documents.\n */\n\nimport {\n importFromOpenApi,\n parseOpenApi,\n} from '@contractspec/lib.contracts-transformers/openapi';\nimport type { FsAdapter } from '../../ports/fs';\nimport type { LoggerAdapter } from '../../ports/logger';\nimport type {\n OpenApiImportServiceOptions,\n OpenApiImportServiceResult,\n} from './types';\nimport { dirname, join, basename } from 'path';\nimport type { ContractsrcConfig } from '@contractspec/lib.contracts';\n\n/**\n * Get output directory for spec type\n */\nfunction getOutputDir(\n type: 'operation' | 'event' | 'model',\n options: OpenApiImportServiceOptions,\n config: ContractsrcConfig\n): string {\n // If outputDir is explicitly set in options, use it for all types\n if (options.outputDir) {\n return options.outputDir;\n }\n\n // Default base\n const baseDir = config.outputDir ?? 'src';\n const conventions = config.conventions ?? {\n operations: 'operations',\n events: 'events',\n };\n\n switch (type) {\n case 'operation':\n // Conventions usually format like \"operations/**\" or \"operations\"\n return join(\n baseDir,\n conventions.operations.split('|')[0] ?? 'operations'\n );\n case 'event':\n return join(baseDir, conventions.events ?? 'events');\n case 'model':\n return join(baseDir, 'models'); // Standardize on 'models' for now\n default:\n return baseDir;\n }\n}\n\n/**\n * Import ContractSpec specs from an OpenAPI document.\n */\nexport async function importFromOpenApiService(\n contractspecOptions: ContractsrcConfig,\n options: OpenApiImportServiceOptions,\n adapters: { fs: FsAdapter; logger: LoggerAdapter }\n): Promise<OpenApiImportServiceResult> {\n const { fs, logger } = adapters;\n const {\n source,\n\n prefix,\n tags,\n exclude,\n defaultStability,\n defaultOwners,\n defaultAuth,\n dryRun = false,\n } = options;\n\n logger.info(`Importing from OpenAPI: ${source}`);\n\n // Parse the OpenAPI document\n // Use globalThis.fetch because adapters don't have networking yet\n // but we use fs.readFile for local files\n const parseResult = await parseOpenApi(source, {\n fetch: globalThis.fetch,\n readFile: (path) => fs.readFile(path),\n });\n\n if (parseResult.warnings.length > 0) {\n for (const warning of parseResult.warnings) {\n logger.warn(`Parse warning: ${warning}`);\n }\n }\n\n logger.info(\n `Parsed ${parseResult.operations.length} operations from ${parseResult.info.title} v${parseResult.info.version}`\n );\n\n // Import operations\n const importResult = importFromOpenApi(parseResult, contractspecOptions, {\n prefix,\n tags,\n exclude,\n defaultStability,\n defaultOwners,\n defaultAuth,\n });\n\n logger.info(\n `Import result: ${importResult.summary.imported} imported, ${importResult.summary.skipped} skipped, ${importResult.summary.errors} errors`\n );\n\n const files: OpenApiImportServiceResult['files'] = [];\n const skippedOperations: OpenApiImportServiceResult['skippedOperations'] = [];\n const errorMessages: OpenApiImportServiceResult['errorMessages'] = [];\n\n // Write imported specs\n const specsByDir = new Map<\n string,\n { file: string; name: string; type: 'operation' | 'event' | 'model' }[]\n >();\n\n for (const spec of importResult.operationSpecs) {\n // Determine type FIRST to resolve output directory\n let type: 'operation' | 'event' | 'model' = 'operation';\n let match: RegExpMatchArray | null = null;\n if (spec.code.includes('defineEvent(')) {\n type = 'event';\n match = spec.code.match(/export const (\\w+)\\s*=\\s*defineEvent/);\n } else if (\n (spec.code.includes('defineSchemaModel(') ||\n spec.code.includes('new EnumType(') ||\n spec.code.includes('ScalarTypeEnum.') ||\n spec.code.includes('new ZodSchemaType(') ||\n spec.code.includes('z.enum(') ||\n spec.code.includes('new JsonSchemaType(') ||\n spec.code.includes('new GraphQLSchemaType(')) &&\n !spec.code.includes('defineCommand(') &&\n !spec.code.includes('defineQuery(')\n ) {\n type = 'model';\n } else {\n type = 'operation';\n match = spec.code.match(\n /export const (\\w+)\\s*=\\s*define(?:Command|Query)/\n );\n }\n\n // Resolve output directory based on type\n const targetDir = getOutputDir(type, options, contractspecOptions);\n const filePath = join(targetDir, spec.fileName);\n\n if (!match && type === 'model') {\n if (spec.code.includes('new ZodSchemaType(')) {\n match = spec.code.match(/export const (\\w+)\\s*=\\s*new ZodSchemaType\\(/);\n } else if (spec.code.includes('new JsonSchemaType(')) {\n match = spec.code.match(\n /export const (\\w+)\\s*=\\s*new JsonSchemaType\\(/\n );\n } else if (spec.code.includes('new GraphQLSchemaType(')) {\n match = spec.code.match(\n /export const (\\w+)\\s*=\\s*new GraphQLSchemaType\\(/\n );\n }\n if (!match) {\n match = spec.code.match(/export const (\\w+)\\s*=/);\n }\n }\n\n if (dryRun) {\n logger.info(`[DRY RUN] Would create: ${filePath}`);\n } else {\n // Ensure directory exists\n const dir = dirname(filePath);\n const exists = await fs.exists(dir);\n if (!exists) {\n await fs.mkdir(dir);\n }\n\n // Write spec file\n await fs.writeFile(filePath, spec.code);\n logger.info(`Created: ${filePath}`);\n }\n\n files.push({\n path: filePath,\n operationId: spec.source.sourceId,\n specName: spec.fileName.replace('.ts', ''),\n });\n\n if (match) {\n const dir = dirname(filePath);\n const existing = specsByDir.get(dir) || [];\n existing.push({\n file: basename(filePath),\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n name: match[1]!,\n type,\n });\n specsByDir.set(dir, existing);\n }\n }\n\n // Generate registries\n if (!dryRun && files.length > 0) {\n for (const [dir, specs] of specsByDir.entries()) {\n if (specs.length === 0) continue;\n\n // Detect dominant type\n const types = specs.map((s) => s.type);\n const isOperations = types.every((t) => t === 'operation');\n const isEvents = types.every((t) => t === 'event');\n const isModels = types.every((t) => t === 'model');\n\n // Generate Registry File\n let registryCode = `/**\\n * Auto-generated registry file.\\n */\\n`;\n specs.forEach((s) => {\n const importPath = `./${basename(s.file, '.ts')}`;\n registryCode += `import { ${s.name} } from '${importPath}';\\n`;\n });\n registryCode += '\\n';\n\n let hasRegistry = false;\n if (isOperations) {\n registryCode += `import { OperationSpecRegistry } from '@contractspec/lib.contracts';\\n\\n`;\n registryCode += `export const operationRegistry = new OperationSpecRegistry();\\n`;\n specs.forEach((s) => {\n registryCode += `operationRegistry.register(${s.name});\\n`;\n });\n hasRegistry = true;\n } else if (isEvents) {\n registryCode += `import { EventRegistry } from '@contractspec/lib.contracts';\\n\\n`;\n registryCode += `export const eventRegistry = new EventRegistry();\\n`;\n specs.forEach((s) => {\n registryCode += `eventRegistry.register(${s.name});\\n`;\n });\n hasRegistry = true;\n } else if (isModels) {\n registryCode += `import { ModelRegistry } from '@contractspec/lib.contracts';\\n\\n`;\n registryCode += `export const modelRegistry = new ModelRegistry();\\n`;\n specs.forEach((s) => {\n registryCode += `modelRegistry.register(${s.name});\\n`;\n });\n hasRegistry = true;\n }\n\n if (hasRegistry) {\n const registryPath = join(dir, 'registry.ts');\n await fs.writeFile(registryPath, registryCode);\n logger.info(`Created/Updated registry: ${registryPath}`);\n }\n\n // Generate Index File\n let indexCode = `/**\\n * Auto-generated barrel file.\\n */\\n\\n`;\n specs.forEach((s) => {\n const importPath = `./${basename(s.file, '.ts')}`;\n indexCode += `export * from '${importPath}';\\n`;\n });\n\n if (hasRegistry) {\n indexCode += `export * from './registry';\\n`;\n }\n\n const indexPath = join(dir, 'index.ts');\n await fs.writeFile(indexPath, indexCode);\n logger.info(`Created/Updated index: ${indexPath}`);\n }\n }\n\n // Record skipped operations\n for (const skipped of importResult.skipped) {\n skippedOperations.push({\n operationId: skipped.sourceId,\n reason: skipped.reason,\n });\n logger.debug(`Skipped: ${skipped.sourceId} - ${skipped.reason}`);\n }\n\n // Record errors\n for (const error of importResult.errors) {\n errorMessages.push({\n operationId: error.sourceId,\n error: error.error,\n });\n logger.error(`Error: ${error.sourceId} - ${error.error}`);\n }\n\n return {\n imported: importResult.summary.imported,\n skipped: importResult.summary.skipped,\n errors: importResult.summary.errors,\n files,\n skippedOperations,\n errorMessages,\n };\n}\n"],"mappings":";;;;;;;;;;AAoBA,SAAS,aACP,MACA,SACA,QACQ;AAER,KAAI,QAAQ,UACV,QAAO,QAAQ;CAIjB,MAAM,UAAU,OAAO,aAAa;CACpC,MAAM,cAAc,OAAO,eAAe;EACxC,YAAY;EACZ,QAAQ;EACT;AAED,SAAQ,MAAR;EACE,KAAK,YAEH,QAAO,KACL,SACA,YAAY,WAAW,MAAM,IAAI,CAAC,MAAM,aACzC;EACH,KAAK,QACH,QAAO,KAAK,SAAS,YAAY,UAAU,SAAS;EACtD,KAAK,QACH,QAAO,KAAK,SAAS,SAAS;EAChC,QACE,QAAO;;;;;;AAOb,eAAsB,yBACpB,qBACA,SACA,UACqC;CACrC,MAAM,EAAE,IAAI,WAAW;CACvB,MAAM,EACJ,QAEA,QACA,MACA,SACA,kBACA,eACA,aACA,SAAS,UACP;AAEJ,QAAO,KAAK,2BAA2B,SAAS;CAKhD,MAAM,cAAc,MAAM,aAAa,QAAQ;EAC7C,OAAO,WAAW;EAClB,WAAW,WAAS,GAAG,SAASA,OAAK;EACtC,CAAC;AAEF,KAAI,YAAY,SAAS,SAAS,EAChC,MAAK,MAAM,WAAW,YAAY,SAChC,QAAO,KAAK,kBAAkB,UAAU;AAI5C,QAAO,KACL,UAAU,YAAY,WAAW,OAAO,mBAAmB,YAAY,KAAK,MAAM,IAAI,YAAY,KAAK,UACxG;CAGD,MAAM,eAAe,kBAAkB,aAAa,qBAAqB;EACvE;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;AAEF,QAAO,KACL,kBAAkB,aAAa,QAAQ,SAAS,aAAa,aAAa,QAAQ,QAAQ,YAAY,aAAa,QAAQ,OAAO,SACnI;CAED,MAAM,QAA6C,EAAE;CACrD,MAAM,oBAAqE,EAAE;CAC7E,MAAM,gBAA6D,EAAE;CAGrE,MAAM,6BAAa,IAAI,KAGpB;AAEH,MAAK,MAAM,QAAQ,aAAa,gBAAgB;EAE9C,IAAI,OAAwC;EAC5C,IAAI,QAAiC;AACrC,MAAI,KAAK,KAAK,SAAS,eAAe,EAAE;AACtC,UAAO;AACP,WAAQ,KAAK,KAAK,MAAM,uCAAuC;cAE9D,KAAK,KAAK,SAAS,qBAAqB,IACvC,KAAK,KAAK,SAAS,gBAAgB,IACnC,KAAK,KAAK,SAAS,kBAAkB,IACrC,KAAK,KAAK,SAAS,qBAAqB,IACxC,KAAK,KAAK,SAAS,UAAU,IAC7B,KAAK,KAAK,SAAS,sBAAsB,IACzC,KAAK,KAAK,SAAS,yBAAyB,KAC9C,CAAC,KAAK,KAAK,SAAS,iBAAiB,IACrC,CAAC,KAAK,KAAK,SAAS,eAAe,CAEnC,QAAO;OACF;AACL,UAAO;AACP,WAAQ,KAAK,KAAK,MAChB,mDACD;;EAKH,MAAM,WAAW,KADC,aAAa,MAAM,SAAS,oBAAoB,EACjC,KAAK,SAAS;AAE/C,MAAI,CAAC,SAAS,SAAS,SAAS;AAC9B,OAAI,KAAK,KAAK,SAAS,qBAAqB,CAC1C,SAAQ,KAAK,KAAK,MAAM,+CAA+C;YAC9D,KAAK,KAAK,SAAS,sBAAsB,CAClD,SAAQ,KAAK,KAAK,MAChB,gDACD;YACQ,KAAK,KAAK,SAAS,yBAAyB,CACrD,SAAQ,KAAK,KAAK,MAChB,mDACD;AAEH,OAAI,CAAC,MACH,SAAQ,KAAK,KAAK,MAAM,yBAAyB;;AAIrD,MAAI,OACF,QAAO,KAAK,2BAA2B,WAAW;OAC7C;GAEL,MAAM,MAAM,QAAQ,SAAS;AAE7B,OAAI,CADW,MAAM,GAAG,OAAO,IAAI,CAEjC,OAAM,GAAG,MAAM,IAAI;AAIrB,SAAM,GAAG,UAAU,UAAU,KAAK,KAAK;AACvC,UAAO,KAAK,YAAY,WAAW;;AAGrC,QAAM,KAAK;GACT,MAAM;GACN,aAAa,KAAK,OAAO;GACzB,UAAU,KAAK,SAAS,QAAQ,OAAO,GAAG;GAC3C,CAAC;AAEF,MAAI,OAAO;GACT,MAAM,MAAM,QAAQ,SAAS;GAC7B,MAAM,WAAW,WAAW,IAAI,IAAI,IAAI,EAAE;AAC1C,YAAS,KAAK;IACZ,MAAM,SAAS,SAAS;IAExB,MAAM,MAAM;IACZ;IACD,CAAC;AACF,cAAW,IAAI,KAAK,SAAS;;;AAKjC,KAAI,CAAC,UAAU,MAAM,SAAS,EAC5B,MAAK,MAAM,CAAC,KAAK,UAAU,WAAW,SAAS,EAAE;AAC/C,MAAI,MAAM,WAAW,EAAG;EAGxB,MAAM,QAAQ,MAAM,KAAK,MAAM,EAAE,KAAK;EACtC,MAAM,eAAe,MAAM,OAAO,MAAM,MAAM,YAAY;EAC1D,MAAM,WAAW,MAAM,OAAO,MAAM,MAAM,QAAQ;EAClD,MAAM,WAAW,MAAM,OAAO,MAAM,MAAM,QAAQ;EAGlD,IAAI,eAAe;AACnB,QAAM,SAAS,MAAM;GACnB,MAAM,aAAa,KAAK,SAAS,EAAE,MAAM,MAAM;AAC/C,mBAAgB,YAAY,EAAE,KAAK,WAAW,WAAW;IACzD;AACF,kBAAgB;EAEhB,IAAI,cAAc;AAClB,MAAI,cAAc;AAChB,mBAAgB;AAChB,mBAAgB;AAChB,SAAM,SAAS,MAAM;AACnB,oBAAgB,8BAA8B,EAAE,KAAK;KACrD;AACF,iBAAc;aACL,UAAU;AACnB,mBAAgB;AAChB,mBAAgB;AAChB,SAAM,SAAS,MAAM;AACnB,oBAAgB,0BAA0B,EAAE,KAAK;KACjD;AACF,iBAAc;aACL,UAAU;AACnB,mBAAgB;AAChB,mBAAgB;AAChB,SAAM,SAAS,MAAM;AACnB,oBAAgB,0BAA0B,EAAE,KAAK;KACjD;AACF,iBAAc;;AAGhB,MAAI,aAAa;GACf,MAAM,eAAe,KAAK,KAAK,cAAc;AAC7C,SAAM,GAAG,UAAU,cAAc,aAAa;AAC9C,UAAO,KAAK,6BAA6B,eAAe;;EAI1D,IAAI,YAAY;AAChB,QAAM,SAAS,MAAM;GACnB,MAAM,aAAa,KAAK,SAAS,EAAE,MAAM,MAAM;AAC/C,gBAAa,kBAAkB,WAAW;IAC1C;AAEF,MAAI,YACF,cAAa;EAGf,MAAM,YAAY,KAAK,KAAK,WAAW;AACvC,QAAM,GAAG,UAAU,WAAW,UAAU;AACxC,SAAO,KAAK,0BAA0B,YAAY;;AAKtD,MAAK,MAAM,WAAW,aAAa,SAAS;AAC1C,oBAAkB,KAAK;GACrB,aAAa,QAAQ;GACrB,QAAQ,QAAQ;GACjB,CAAC;AACF,SAAO,MAAM,YAAY,QAAQ,SAAS,KAAK,QAAQ,SAAS;;AAIlE,MAAK,MAAM,SAAS,aAAa,QAAQ;AACvC,gBAAc,KAAK;GACjB,aAAa,MAAM;GACnB,OAAO,MAAM;GACd,CAAC;AACF,SAAO,MAAM,UAAU,MAAM,SAAS,KAAK,MAAM,QAAQ;;AAG3D,QAAO;EACL,UAAU,aAAa,QAAQ;EAC/B,SAAS,aAAa,QAAQ;EAC9B,QAAQ,aAAa,QAAQ;EAC7B;EACA;EACA;EACD"}
|
|
1
|
+
{"version":3,"file":"import-service.mjs","names":["path"],"sources":["../../../src/services/openapi/import-service.ts"],"sourcesContent":["/**\n * OpenAPI import service - imports specs from OpenAPI documents.\n */\n\nimport {\n importFromOpenApi,\n parseOpenApi,\n} from '@contractspec/lib.contracts-transformers/openapi';\nimport type { FsAdapter } from '../../ports/fs';\nimport type { LoggerAdapter } from '../../ports/logger';\nimport type {\n OpenApiImportServiceOptions,\n OpenApiImportServiceResult,\n} from './types';\nimport { dirname, join, basename } from 'path';\nimport type { ResolvedContractsrcConfig } from '@contractspec/lib.contracts';\n\n/**\n * Get output directory for spec type\n */\nfunction getOutputDir(\n type: 'operation' | 'event' | 'model',\n options: OpenApiImportServiceOptions,\n config: ResolvedContractsrcConfig\n): string {\n // If outputDir is explicitly set in options, use it for all types\n if (options.outputDir) {\n return options.outputDir;\n }\n\n // Default base\n const baseDir = config.outputDir;\n const conventions = config.conventions;\n\n switch (type) {\n case 'operation':\n // Conventions usually format like \"operations/**\" or \"operations\"\n return join(\n baseDir,\n conventions.operations.split('|')[0] ?? 'operations'\n );\n case 'event':\n return join(baseDir, conventions.events);\n case 'model':\n return join(baseDir, 'models'); // Standardize on 'models' for now\n default:\n return baseDir;\n }\n}\n\n/**\n * Import ContractSpec specs from an OpenAPI document.\n */\nexport async function importFromOpenApiService(\n contractspecOptions: ResolvedContractsrcConfig,\n options: OpenApiImportServiceOptions,\n adapters: { fs: FsAdapter; logger: LoggerAdapter }\n): Promise<OpenApiImportServiceResult> {\n const { fs, logger } = adapters;\n const {\n source,\n\n prefix,\n tags,\n exclude,\n defaultStability,\n defaultOwners,\n defaultAuth,\n dryRun = false,\n } = options;\n\n logger.info(`Importing from OpenAPI: ${source}`);\n\n // Parse the OpenAPI document\n // Use globalThis.fetch because adapters don't have networking yet\n // but we use fs.readFile for local files\n const parseResult = await parseOpenApi(source, {\n fetch: globalThis.fetch,\n readFile: (path) => fs.readFile(path),\n });\n\n if (parseResult.warnings.length > 0) {\n for (const warning of parseResult.warnings) {\n logger.warn(`Parse warning: ${warning}`);\n }\n }\n\n logger.info(\n `Parsed ${parseResult.operations.length} operations from ${parseResult.info.title} v${parseResult.info.version}`\n );\n\n // Import operations\n const importResult = importFromOpenApi(parseResult, contractspecOptions, {\n prefix,\n tags,\n exclude,\n defaultStability,\n defaultOwners,\n defaultAuth,\n });\n\n logger.info(\n `Import result: ${importResult.summary.imported} imported, ${importResult.summary.skipped} skipped, ${importResult.summary.errors} errors`\n );\n\n const files: OpenApiImportServiceResult['files'] = [];\n const skippedOperations: OpenApiImportServiceResult['skippedOperations'] = [];\n const errorMessages: OpenApiImportServiceResult['errorMessages'] = [];\n\n // Write imported specs\n const specsByDir = new Map<\n string,\n { file: string; name: string; type: 'operation' | 'event' | 'model' }[]\n >();\n\n for (const spec of importResult.operationSpecs) {\n // Determine type FIRST to resolve output directory\n let type: 'operation' | 'event' | 'model' = 'operation';\n let match: RegExpMatchArray | null = null;\n if (spec.code.includes('defineEvent(')) {\n type = 'event';\n match = spec.code.match(/export const (\\w+)\\s*=\\s*defineEvent/);\n } else if (\n (spec.code.includes('defineSchemaModel(') ||\n spec.code.includes('new EnumType(') ||\n spec.code.includes('ScalarTypeEnum.') ||\n spec.code.includes('new ZodSchemaType(') ||\n spec.code.includes('z.enum(') ||\n spec.code.includes('new JsonSchemaType(') ||\n spec.code.includes('new GraphQLSchemaType(')) &&\n !spec.code.includes('defineCommand(') &&\n !spec.code.includes('defineQuery(')\n ) {\n type = 'model';\n } else {\n type = 'operation';\n match = spec.code.match(\n /export const (\\w+)\\s*=\\s*define(?:Command|Query)/\n );\n }\n\n // Resolve output directory based on type\n const targetDir = getOutputDir(type, options, contractspecOptions);\n const filePath = join(targetDir, spec.fileName);\n\n if (!match && type === 'model') {\n if (spec.code.includes('new ZodSchemaType(')) {\n match = spec.code.match(/export const (\\w+)\\s*=\\s*new ZodSchemaType\\(/);\n } else if (spec.code.includes('new JsonSchemaType(')) {\n match = spec.code.match(\n /export const (\\w+)\\s*=\\s*new JsonSchemaType\\(/\n );\n } else if (spec.code.includes('new GraphQLSchemaType(')) {\n match = spec.code.match(\n /export const (\\w+)\\s*=\\s*new GraphQLSchemaType\\(/\n );\n }\n if (!match) {\n match = spec.code.match(/export const (\\w+)\\s*=/);\n }\n }\n\n if (dryRun) {\n logger.info(`[DRY RUN] Would create: ${filePath}`);\n } else {\n // Ensure directory exists\n const dir = dirname(filePath);\n const exists = await fs.exists(dir);\n if (!exists) {\n await fs.mkdir(dir);\n }\n\n // Write spec file\n await fs.writeFile(filePath, spec.code);\n logger.info(`Created: ${filePath}`);\n }\n\n files.push({\n path: filePath,\n operationId: spec.source.sourceId,\n specName: spec.fileName.replace('.ts', ''),\n });\n\n if (match) {\n const dir = dirname(filePath);\n const existing = specsByDir.get(dir) || [];\n existing.push({\n file: basename(filePath),\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n name: match[1]!,\n type,\n });\n specsByDir.set(dir, existing);\n }\n }\n\n // Generate registries\n if (!dryRun && files.length > 0) {\n for (const [dir, specs] of specsByDir.entries()) {\n if (specs.length === 0) continue;\n\n // Detect dominant type\n const types = specs.map((s) => s.type);\n const isOperations = types.every((t) => t === 'operation');\n const isEvents = types.every((t) => t === 'event');\n const isModels = types.every((t) => t === 'model');\n\n // Generate Registry File\n let registryCode = `/**\\n * Auto-generated registry file.\\n */\\n`;\n specs.forEach((s) => {\n const importPath = `./${basename(s.file, '.ts')}`;\n registryCode += `import { ${s.name} } from '${importPath}';\\n`;\n });\n registryCode += '\\n';\n\n let hasRegistry = false;\n if (isOperations) {\n registryCode += `import { OperationSpecRegistry } from '@contractspec/lib.contracts';\\n\\n`;\n registryCode += `export const operationRegistry = new OperationSpecRegistry();\\n`;\n specs.forEach((s) => {\n registryCode += `operationRegistry.register(${s.name});\\n`;\n });\n hasRegistry = true;\n } else if (isEvents) {\n registryCode += `import { EventRegistry } from '@contractspec/lib.contracts';\\n\\n`;\n registryCode += `export const eventRegistry = new EventRegistry();\\n`;\n specs.forEach((s) => {\n registryCode += `eventRegistry.register(${s.name});\\n`;\n });\n hasRegistry = true;\n } else if (isModels) {\n registryCode += `import { ModelRegistry } from '@contractspec/lib.contracts';\\n\\n`;\n registryCode += `export const modelRegistry = new ModelRegistry();\\n`;\n specs.forEach((s) => {\n registryCode += `modelRegistry.register(${s.name});\\n`;\n });\n hasRegistry = true;\n }\n\n if (hasRegistry) {\n const registryPath = join(dir, 'registry.ts');\n await fs.writeFile(registryPath, registryCode);\n logger.info(`Created/Updated registry: ${registryPath}`);\n }\n\n // Generate Index File\n let indexCode = `/**\\n * Auto-generated barrel file.\\n */\\n\\n`;\n specs.forEach((s) => {\n const importPath = `./${basename(s.file, '.ts')}`;\n indexCode += `export * from '${importPath}';\\n`;\n });\n\n if (hasRegistry) {\n indexCode += `export * from './registry';\\n`;\n }\n\n const indexPath = join(dir, 'index.ts');\n await fs.writeFile(indexPath, indexCode);\n logger.info(`Created/Updated index: ${indexPath}`);\n }\n }\n\n // Record skipped operations\n for (const skipped of importResult.skipped) {\n skippedOperations.push({\n operationId: skipped.sourceId,\n reason: skipped.reason,\n });\n logger.debug(`Skipped: ${skipped.sourceId} - ${skipped.reason}`);\n }\n\n // Record errors\n for (const error of importResult.errors) {\n errorMessages.push({\n operationId: error.sourceId,\n error: error.error,\n });\n logger.error(`Error: ${error.sourceId} - ${error.error}`);\n }\n\n return {\n imported: importResult.summary.imported,\n skipped: importResult.summary.skipped,\n errors: importResult.summary.errors,\n files,\n skippedOperations,\n errorMessages,\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { FsAdapter } from "../../ports/fs.mjs";
|
|
2
2
|
import { LoggerAdapter } from "../../ports/logger.mjs";
|
|
3
3
|
import { OpenApiSyncServiceOptions, OpenApiSyncServiceResult } from "./types.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { ResolvedContractsrcConfig } from "@contractspec/lib.contracts";
|
|
5
5
|
|
|
6
6
|
//#region src/services/openapi/sync-service.d.ts
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Sync ContractSpec specs with OpenAPI sources.
|
|
10
10
|
*/
|
|
11
|
-
declare function syncWithOpenApiService(options: OpenApiSyncServiceOptions, config:
|
|
11
|
+
declare function syncWithOpenApiService(options: OpenApiSyncServiceOptions, config: ResolvedContractsrcConfig, adapters: {
|
|
12
12
|
fs: FsAdapter;
|
|
13
13
|
logger: LoggerAdapter;
|
|
14
14
|
}): Promise<OpenApiSyncServiceResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync-service.d.mts","names":[],"sources":["../../../src/services/openapi/sync-service.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AAuBqC,iBAHf,sBAAA,CAGe,OAAA,EAF1B,yBAE0B,EAAA,MAAA,EAD3B,
|
|
1
|
+
{"version":3,"file":"sync-service.d.mts","names":[],"sources":["../../../src/services/openapi/sync-service.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AAuBqC,iBAHf,sBAAA,CAGe,OAAA,EAF1B,yBAE0B,EAAA,MAAA,EAD3B,yBAC2B,EAAA,QAAA,EAAA;EAC1B,EAAA,EADO,SACP;EAAR,MAAA,EADkC,aAClC;CAAO,CAAA,EAAP,OAAO,CAAC,wBAAD,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync-service.mjs","names":["path"],"sources":["../../../src/services/openapi/sync-service.ts"],"sourcesContent":["/**\n * OpenAPI sync service - syncs specs with OpenAPI sources.\n */\n\nimport {\n importFromOpenApi,\n parseOpenApi,\n} from '@contractspec/lib.contracts-transformers/openapi';\nimport type { FsAdapter } from '../../ports/fs';\nimport type { LoggerAdapter } from '../../ports/logger';\nimport type {\n OpenApiSyncServiceOptions,\n OpenApiSyncServiceResult,\n} from './types';\nimport { dirname, join } from 'path';\nimport type {
|
|
1
|
+
{"version":3,"file":"sync-service.mjs","names":["path"],"sources":["../../../src/services/openapi/sync-service.ts"],"sourcesContent":["/**\n * OpenAPI sync service - syncs specs with OpenAPI sources.\n */\n\nimport {\n importFromOpenApi,\n parseOpenApi,\n} from '@contractspec/lib.contracts-transformers/openapi';\nimport type { FsAdapter } from '../../ports/fs';\nimport type { LoggerAdapter } from '../../ports/logger';\nimport type {\n OpenApiSyncServiceOptions,\n OpenApiSyncServiceResult,\n} from './types';\nimport { dirname, join } from 'path';\nimport type { ResolvedContractsrcConfig } from '@contractspec/lib.contracts';\n\n/**\n * Sync ContractSpec specs with OpenAPI sources.\n */\nexport async function syncWithOpenApiService(\n options: OpenApiSyncServiceOptions,\n config: ResolvedContractsrcConfig,\n adapters: { fs: FsAdapter; logger: LoggerAdapter }\n): Promise<OpenApiSyncServiceResult> {\n const { fs, logger } = adapters;\n const {\n sources: optSources,\n sourceName,\n interactive,\n force,\n dryRun,\n } = options;\n const { outputDir } = config;\n\n // Determine which sources to sync\n let sourcesToSync = optSources ?? config.openapi?.sources ?? [];\n\n if (sourceName) {\n sourcesToSync = sourcesToSync.filter((s) => s.name === sourceName);\n if (sourcesToSync.length === 0) {\n throw new Error(`Source not found: ${sourceName}`);\n }\n }\n\n if (sourcesToSync.length === 0) {\n logger.warn(\n 'No OpenAPI sources configured. Add sources to .contractsrc.json'\n );\n return {\n added: 0,\n updated: 0,\n unchanged: 0,\n conflicts: 0,\n changes: [],\n };\n }\n\n const result: OpenApiSyncServiceResult = {\n added: 0,\n updated: 0,\n unchanged: 0,\n conflicts: 0,\n changes: [],\n };\n\n for (const source of sourcesToSync) {\n logger.info(`Syncing with source: ${source.name}`);\n\n // Get source URL or file\n const sourceLocation = source.url ?? source.file;\n if (!sourceLocation) {\n logger.warn(`Source ${source.name} has no url or file configured`);\n continue;\n }\n\n // Parse the OpenAPI document\n const parseResult = await parseOpenApi(sourceLocation, {\n fetch: globalThis.fetch,\n readFile: (path) => fs.readFile(path),\n });\n\n logger.info(\n `Parsed ${parseResult.operations.length} operations from ${source.name}`\n );\n\n // Import operations to get the new specs\n const importResult = importFromOpenApi(parseResult, config, {\n prefix: source.prefix,\n tags: source.tags,\n exclude: source.exclude,\n defaultStability: source.defaultStability,\n defaultAuth: source.defaultAuth,\n });\n\n // Process each imported spec\n for (const imported of importResult.operationSpecs) {\n const filePath = join(outputDir, imported.fileName);\n const exists = await fs.exists(filePath);\n\n if (!exists) {\n // New spec - add it\n if (!dryRun) {\n const dir = dirname(filePath);\n await fs.mkdir(dir);\n await fs.writeFile(filePath, imported.code);\n }\n\n result.added++;\n result.changes.push({\n operationId: imported.source.sourceId,\n action: 'added',\n path: filePath,\n });\n logger.info(`Added: ${imported.source.sourceId}`);\n } else {\n // Existing spec - check for differences\n const existingCode = await fs.readFile(filePath);\n\n if (existingCode === imported.code) {\n // No changes\n result.unchanged++;\n result.changes.push({\n operationId: imported.source.sourceId,\n action: 'unchanged',\n path: filePath,\n });\n } else {\n // Differences detected\n if (force === 'openapi') {\n // Overwrite with OpenAPI version\n if (!dryRun) {\n await fs.writeFile(filePath, imported.code);\n }\n result.updated++;\n result.changes.push({\n operationId: imported.source.sourceId,\n action: 'updated',\n path: filePath,\n });\n logger.info(`Updated: ${imported.source.sourceId}`);\n } else if (force === 'contractspec') {\n // Keep ContractSpec version\n result.unchanged++;\n result.changes.push({\n operationId: imported.source.sourceId,\n action: 'unchanged',\n path: filePath,\n });\n logger.info(`Kept: ${imported.source.sourceId}`);\n } else if (interactive) {\n // Would prompt for resolution in CLI\n result.conflicts++;\n result.changes.push({\n operationId: imported.source.sourceId,\n action: 'conflict',\n path: filePath,\n });\n logger.warn(\n `Conflict: ${imported.source.sourceId} - needs resolution`\n );\n } else {\n // Default: report conflict\n result.conflicts++;\n result.changes.push({\n operationId: imported.source.sourceId,\n action: 'conflict',\n path: filePath,\n });\n logger.warn(`Conflict: ${imported.source.sourceId}`);\n }\n }\n }\n }\n }\n\n logger.info(\n `Sync complete: ${result.added} added, ${result.updated} updated, ${result.unchanged} unchanged, ${result.conflicts} conflicts`\n );\n\n return result;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rulesync.d.mts","names":[],"sources":["../../src/services/rulesync.ts"],"sourcesContent":[],"mappings":";;;;;;AAkByB,cAFZ,eAAA,YAA2B,YAEf,CAAA;EACI,iBAAA,EAAA;EAGP,iBAAA,MAAA;EAA0B,WAAA,CAAA,EAAA,EAJvB,SAIuB,EAAA,MAAA,EAHnB,aAGmB;EAAR,IAAA,CAAA,OAAA,EAAlB,eAAkB,CAAA,EAAA,OAAA,CAAQ,cAAR,CAAA;
|
|
1
|
+
{"version":3,"file":"rulesync.d.mts","names":[],"sources":["../../src/services/rulesync.ts"],"sourcesContent":[],"mappings":";;;;;;AAkByB,cAFZ,eAAA,YAA2B,YAEf,CAAA;EACI,iBAAA,EAAA;EAGP,iBAAA,MAAA;EAA0B,WAAA,CAAA,EAAA,EAJvB,SAIuB,EAAA,MAAA,EAHnB,aAGmB;EAAR,IAAA,CAAA,OAAA,EAAlB,eAAkB,CAAA,EAAA,OAAA,CAAQ,cAAR,CAAA;EA0ER,cAAA,CAAA,OAAA,EAAA,eAAA,CAAA,EAAkB,OAAlB,CAAA,MAAA,CAAA;EAAkB,QAAA,iBAAA"}
|
|
@@ -5,7 +5,8 @@ var RuleSyncService = class {
|
|
|
5
5
|
this.logger = logger;
|
|
6
6
|
}
|
|
7
7
|
async sync(options) {
|
|
8
|
-
const { config, cwd
|
|
8
|
+
const { config, cwd } = options;
|
|
9
|
+
const targets = options.targets ?? config.targets ?? [];
|
|
9
10
|
if (!config.enabled && !options.targets) return {
|
|
10
11
|
success: true,
|
|
11
12
|
files: [],
|
|
@@ -13,7 +14,7 @@ var RuleSyncService = class {
|
|
|
13
14
|
};
|
|
14
15
|
this.logger.info(`Synchronizing rules for targets: ${targets.join(", ")}...`);
|
|
15
16
|
try {
|
|
16
|
-
const rulesDir = this.fs.join(cwd, config.rulesDir);
|
|
17
|
+
const rulesDir = this.fs.join(cwd, config.rulesDir ?? ".rules");
|
|
17
18
|
if (!await this.fs.exists(rulesDir)) return {
|
|
18
19
|
success: false,
|
|
19
20
|
files: [],
|
|
@@ -47,7 +48,7 @@ var RuleSyncService = class {
|
|
|
47
48
|
async generateConfig(options) {
|
|
48
49
|
const { config } = options;
|
|
49
50
|
const rulesyncConfig = {
|
|
50
|
-
rules: config.rules.map((r) => this.fs.join(config.rulesDir, r)),
|
|
51
|
+
rules: (config.rules ?? []).map((r) => this.fs.join(config.rulesDir ?? ".rules", r)),
|
|
51
52
|
targets: options.targets || config.targets
|
|
52
53
|
};
|
|
53
54
|
return JSON.stringify(rulesyncConfig, null, 2);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rulesync.mjs","names":[],"sources":["../../src/services/rulesync.ts"],"sourcesContent":["/**\n * Service for synchronizing AI agent rules.\n */\n\nimport type {\n RuleSyncPort,\n RuleSyncOptions,\n RuleSyncResult,\n} from '../ports/rulesync';\nimport type { FsAdapter } from '../ports/fs';\nimport type { LoggerAdapter } from '../ports/logger';\n\n// We import rulesync dynamically to avoid issues if it's not present or has ESM/CJS mismatches\n// In a real implementation, we would use the actual programmatic API of rulesync if available.\n// For now, we'll implement it by generating the config and potentially calling the CLI or using its core logic.\n\nexport class RuleSyncService implements RuleSyncPort {\n constructor(\n private readonly fs: FsAdapter,\n private readonly logger: LoggerAdapter\n ) {}\n\n async sync(options: RuleSyncOptions): Promise<RuleSyncResult> {\n const { config, cwd
|
|
1
|
+
{"version":3,"file":"rulesync.mjs","names":[],"sources":["../../src/services/rulesync.ts"],"sourcesContent":["/**\n * Service for synchronizing AI agent rules.\n */\n\nimport type {\n RuleSyncPort,\n RuleSyncOptions,\n RuleSyncResult,\n} from '../ports/rulesync';\nimport type { FsAdapter } from '../ports/fs';\nimport type { LoggerAdapter } from '../ports/logger';\n\n// We import rulesync dynamically to avoid issues if it's not present or has ESM/CJS mismatches\n// In a real implementation, we would use the actual programmatic API of rulesync if available.\n// For now, we'll implement it by generating the config and potentially calling the CLI or using its core logic.\n\nexport class RuleSyncService implements RuleSyncPort {\n constructor(\n private readonly fs: FsAdapter,\n private readonly logger: LoggerAdapter\n ) {}\n\n async sync(options: RuleSyncOptions): Promise<RuleSyncResult> {\n const { config, cwd } = options;\n const targets = options.targets ?? config.targets ?? [];\n\n if (!config.enabled && !options.targets) {\n return {\n success: true,\n files: [],\n logs: ['Rule synchronization is disabled.'],\n };\n }\n\n this.logger.info(\n `Synchronizing rules for targets: ${targets.join(', ')}...`\n );\n\n try {\n // 1. Ensure rules directory exists\n const rulesDir = this.fs.join(cwd, config.rulesDir ?? '.rules');\n const rulesDirExists = await this.fs.exists(rulesDir);\n if (!rulesDirExists) {\n return {\n success: false,\n files: [],\n errors: [`Rules directory not found: ${rulesDir}`],\n };\n }\n\n // 2. Implementation choice:\n // If ejectMode is true, we just copy rules to targets if possible,\n // but usually rulesync is better for merging and formatting.\n\n // For now, let's implement the logic that leverages 'rulesync' patterns.\n // We generate a temporary rulesync.config.json if needed, or use their API.\n\n const rsConfig = await this.generateConfig(options);\n\n // In a real-world scenario, we would call the rulesync programmatic API here:\n // const { sync } = await import('rulesync');\n // await sync({ config: JSON.parse(rsConfig), cwd });\n\n this.logger.debug(`Generated rulesync config: ${rsConfig}`);\n\n // For this implementation, we'll simulate the file generation matching rulesync's behavior\n // until we have the full API details of the library.\n\n const files: string[] = [];\n for (const target of targets) {\n const targetFile = this.getTargetFileName(target);\n if (targetFile) {\n const fullPath = this.fs.join(cwd, targetFile);\n // Logic to aggregate rules from config.rulesDir and write to fullPath\n // would go here. rulesync handles this by parsing the files and\n // combining them based on the target.\n files.push(fullPath);\n }\n }\n\n return {\n success: true,\n files,\n logs: [`Successfully synchronized rules to ${files.length} targets.`],\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n this.logger.error(`Rule synchronization failed: ${message}`);\n return {\n success: false,\n files: [],\n errors: [message],\n };\n }\n }\n\n async generateConfig(options: RuleSyncOptions): Promise<string> {\n const { config } = options;\n\n // Convert our ContractSpec RuleSyncConfig to rulesync's config format\n const rulesyncConfig = {\n rules: (config.rules ?? []).map((r: string) =>\n this.fs.join(config.rulesDir ?? '.rules', r)\n ),\n targets: options.targets || config.targets,\n // Add other rulesync specific options here\n };\n\n return JSON.stringify(rulesyncConfig, null, 2);\n }\n\n private getTargetFileName(target: string): string | undefined {\n switch (target) {\n case 'cursor':\n return '.cursorrules';\n case 'windsurf':\n return '.windsurfrules';\n case 'cline':\n return '.clinerules';\n case 'claude-code':\n return 'CLAUDE.md';\n case 'copilot':\n return '.github/copilot-instructions.md';\n case 'subagent':\n return '.subagent';\n case 'skill':\n return '.skill';\n default:\n return undefined;\n }\n }\n}\n"],"mappings":";AAgBA,IAAa,kBAAb,MAAqD;CACnD,YACE,AAAiB,IACjB,AAAiB,QACjB;EAFiB;EACA;;CAGnB,MAAM,KAAK,SAAmD;EAC5D,MAAM,EAAE,QAAQ,QAAQ;EACxB,MAAM,UAAU,QAAQ,WAAW,OAAO,WAAW,EAAE;AAEvD,MAAI,CAAC,OAAO,WAAW,CAAC,QAAQ,QAC9B,QAAO;GACL,SAAS;GACT,OAAO,EAAE;GACT,MAAM,CAAC,oCAAoC;GAC5C;AAGH,OAAK,OAAO,KACV,oCAAoC,QAAQ,KAAK,KAAK,CAAC,KACxD;AAED,MAAI;GAEF,MAAM,WAAW,KAAK,GAAG,KAAK,KAAK,OAAO,YAAY,SAAS;AAE/D,OAAI,CADmB,MAAM,KAAK,GAAG,OAAO,SAAS,CAEnD,QAAO;IACL,SAAS;IACT,OAAO,EAAE;IACT,QAAQ,CAAC,8BAA8B,WAAW;IACnD;GAUH,MAAM,WAAW,MAAM,KAAK,eAAe,QAAQ;AAMnD,QAAK,OAAO,MAAM,8BAA8B,WAAW;GAK3D,MAAM,QAAkB,EAAE;AAC1B,QAAK,MAAM,UAAU,SAAS;IAC5B,MAAM,aAAa,KAAK,kBAAkB,OAAO;AACjD,QAAI,YAAY;KACd,MAAM,WAAW,KAAK,GAAG,KAAK,KAAK,WAAW;AAI9C,WAAM,KAAK,SAAS;;;AAIxB,UAAO;IACL,SAAS;IACT;IACA,MAAM,CAAC,sCAAsC,MAAM,OAAO,WAAW;IACtE;WACM,OAAO;GACd,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AACtE,QAAK,OAAO,MAAM,gCAAgC,UAAU;AAC5D,UAAO;IACL,SAAS;IACT,OAAO,EAAE;IACT,QAAQ,CAAC,QAAQ;IAClB;;;CAIL,MAAM,eAAe,SAA2C;EAC9D,MAAM,EAAE,WAAW;EAGnB,MAAM,iBAAiB;GACrB,QAAQ,OAAO,SAAS,EAAE,EAAE,KAAK,MAC/B,KAAK,GAAG,KAAK,OAAO,YAAY,UAAU,EAAE,CAC7C;GACD,SAAS,QAAQ,WAAW,OAAO;GAEpC;AAED,SAAO,KAAK,UAAU,gBAAgB,MAAM,EAAE;;CAGhD,AAAQ,kBAAkB,QAAoC;AAC5D,UAAQ,QAAR;GACE,KAAK,SACH,QAAO;GACT,KAAK,WACH,QAAO;GACT,KAAK,QACH,QAAO;GACT,KAAK,cACH,QAAO;GACT,KAAK,UACH,QAAO;GACT,KAAK,WACH,QAAO;GACT,KAAK,QACH,QAAO;GACT,QACE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync.d.mts","names":[],"sources":["../../src/services/sync.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAqCY,UAxBK,gBAAA,
|
|
1
|
+
{"version":3,"file":"sync.d.mts","names":[],"sources":["../../src/services/sync.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAqCY,UAxBK,gBAAA,CA2BZ;EAEO,OAAA,CAAA,EAAA,MAAA;EAEU,UAAA,CAAA,EAAS,CAAA,MAAA,GAAA,SAAA,CAAA,EAAA;EACb,QAAA,CAAA,EAAA,OAAA;EAAmB,YAAA,CAAA,EA5BpB,IA4BoB,CA5Bf,gBA4Be,EAAA,WAAA,CAAA;EAC3B,MAAA,CAAA,EAAA,OAAA;;AAGE,UA5BK,kBAAA,CA4BL;EACG,QAAA,EAAA,MAAA;EAEJ,SAAA,CAAA,EAAA,MAAA;EAAR,UAAA,CAAA,EA5BY,kBA4BZ;EAAO,KAAA,CAAA,EAAA,OAAA;;;;;;UApBO,eAAA;;QAET;;KAGI,sEAGP,QAAQ;KAED,cAAA,yBAAuC,QAAQ;iBAErC,SAAA;MACJ;UAAmB;WAC3B,2BACC;UAEC;aACG;IAEZ,QAAQ"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contractspec/bundle.workspace",
|
|
3
|
-
"version": "0.0.0-canary-
|
|
3
|
+
"version": "0.0.0-canary-20260114030712",
|
|
4
4
|
"description": "Workspace utilities for monorepo development",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"contractspec",
|
|
@@ -31,11 +31,11 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@ai-sdk/anthropic": "3.0.11",
|
|
33
33
|
"@ai-sdk/openai": "3.0.8",
|
|
34
|
-
"@contractspec/lib.ai-agent": "0.0.0-canary-
|
|
35
|
-
"@contractspec/lib.ai-providers": "0.0.0-canary-
|
|
36
|
-
"@contractspec/lib.contracts": "0.0.0-canary-
|
|
37
|
-
"@contractspec/lib.contracts-transformers": "0.0.0-canary-
|
|
38
|
-
"@contractspec/module.workspace": "0.0.0-canary-
|
|
34
|
+
"@contractspec/lib.ai-agent": "0.0.0-canary-20260114030712",
|
|
35
|
+
"@contractspec/lib.ai-providers": "0.0.0-canary-20260114030712",
|
|
36
|
+
"@contractspec/lib.contracts": "0.0.0-canary-20260114030712",
|
|
37
|
+
"@contractspec/lib.contracts-transformers": "0.0.0-canary-20260114030712",
|
|
38
|
+
"@contractspec/module.workspace": "0.0.0-canary-20260114030712",
|
|
39
39
|
"ai": "6.0.29",
|
|
40
40
|
"chalk": "^5.6.2",
|
|
41
41
|
"chokidar": "^5.0.0",
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
"zod": "^4.3.5"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@contractspec/tool.tsdown": "0.0.0-canary-
|
|
51
|
-
"@contractspec/tool.typescript": "0.0.0-canary-
|
|
50
|
+
"@contractspec/tool.tsdown": "0.0.0-canary-20260114030712",
|
|
51
|
+
"@contractspec/tool.typescript": "0.0.0-canary-20260114030712",
|
|
52
52
|
"@types/micromatch": "^4.0.10",
|
|
53
53
|
"@types/node": "^25.0.6",
|
|
54
54
|
"tsdown": "^0.19.0",
|