@better-openclaw/core 1.0.8 → 1.0.10
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/bare-metal-partition.d.mts.map +1 -1
- package/dist/bare-metal-partition.mjs +1 -0
- package/dist/bare-metal-partition.mjs.map +1 -1
- package/dist/bare-metal-partition.test.mjs +1 -1
- package/dist/composer.d.mts.map +1 -1
- package/dist/composer.mjs +11 -1
- package/dist/composer.mjs.map +1 -1
- package/dist/composer.snapshot.test.mjs +1 -1
- package/dist/composer.test.mjs +1 -1
- package/dist/errors.d.mts +17 -0
- package/dist/errors.d.mts.map +1 -0
- package/dist/errors.mjs +24 -0
- package/dist/errors.mjs.map +1 -0
- package/dist/generate.d.mts +4 -3
- package/dist/generate.d.mts.map +1 -1
- package/dist/generate.mjs +13 -5
- package/dist/generate.mjs.map +1 -1
- package/dist/generate.test.mjs +1 -1
- package/dist/generators/bare-metal-install.test.mjs +1 -1
- package/dist/generators/traefik.d.mts +19 -0
- package/dist/generators/traefik.d.mts.map +1 -0
- package/dist/generators/traefik.mjs +86 -0
- package/dist/generators/traefik.mjs.map +1 -0
- package/dist/generators/traefik.test.d.mts +1 -0
- package/dist/generators/traefik.test.mjs +69 -0
- package/dist/generators/traefik.test.mjs.map +1 -0
- package/dist/index.d.mts +4 -2
- package/dist/index.mjs +4 -2
- package/dist/migrations.d.mts +14 -0
- package/dist/migrations.d.mts.map +1 -0
- package/dist/migrations.mjs +33 -0
- package/dist/migrations.mjs.map +1 -0
- package/dist/migrations.test.d.mts +1 -0
- package/dist/migrations.test.mjs +42 -0
- package/dist/migrations.test.mjs.map +1 -0
- package/dist/presets/registry.test.mjs +1 -1
- package/dist/resolver.d.mts +6 -1
- package/dist/resolver.d.mts.map +1 -1
- package/dist/resolver.mjs +21 -3
- package/dist/resolver.mjs.map +1 -1
- package/dist/resolver.test.mjs +1 -1
- package/dist/schema.d.mts +1 -0
- package/dist/schema.d.mts.map +1 -1
- package/dist/schema.mjs +1 -0
- package/dist/schema.mjs.map +1 -1
- package/dist/schema.test.mjs +1 -1
- package/dist/services/definitions/caddy.mjs +20 -1
- package/dist/services/definitions/caddy.mjs.map +1 -1
- package/dist/services/definitions/cal-com.d.mts +7 -0
- package/dist/services/definitions/cal-com.d.mts.map +1 -0
- package/dist/services/definitions/cal-com.mjs +88 -0
- package/dist/services/definitions/cal-com.mjs.map +1 -0
- package/dist/services/definitions/grafana.mjs +13 -1
- package/dist/services/definitions/grafana.mjs.map +1 -1
- package/dist/services/definitions/index.d.mts +4 -1
- package/dist/services/definitions/index.d.mts.map +1 -1
- package/dist/services/definitions/index.mjs +8 -2
- package/dist/services/definitions/index.mjs.map +1 -1
- package/dist/services/definitions/neo4j.d.mts +7 -0
- package/dist/services/definitions/neo4j.d.mts.map +1 -0
- package/dist/services/definitions/neo4j.mjs +91 -0
- package/dist/services/definitions/neo4j.mjs.map +1 -0
- package/dist/services/definitions/traefik.mjs +0 -1
- package/dist/services/definitions/traefik.mjs.map +1 -1
- package/dist/services/definitions/xyops.d.mts +7 -0
- package/dist/services/definitions/xyops.d.mts.map +1 -0
- package/dist/services/definitions/xyops.mjs +86 -0
- package/dist/services/definitions/xyops.mjs.map +1 -0
- package/dist/services/registry.test.mjs +1 -1
- package/dist/skills/registry.d.mts.map +1 -1
- package/dist/skills/registry.mjs +454 -6
- package/dist/skills/registry.mjs.map +1 -1
- package/dist/types.d.mts +8 -1
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/dist/validator.mjs +11 -0
- package/dist/validator.mjs.map +1 -1
- package/dist/validator.test.mjs +1 -1
- package/dist/version-manager.d.mts +1 -1
- package/dist/version-manager.d.mts.map +1 -1
- package/dist/version-manager.mjs +11 -5
- package/dist/version-manager.mjs.map +1 -1
- package/dist/version-manager.test.d.mts +1 -0
- package/dist/version-manager.test.mjs +102 -0
- package/dist/version-manager.test.mjs.map +1 -0
- package/dist/{vi.2VT5v0um-Qk6MgAnK.mjs → vi.2VT5v0um-YSByewHe.mjs} +5 -5
- package/dist/{vi.2VT5v0um-Qk6MgAnK.mjs.map → vi.2VT5v0um-YSByewHe.mjs.map} +1 -1
- package/package.json +1 -1
- package/src/__snapshots__/composer.snapshot.test.ts.snap +15 -1
- package/src/bare-metal-partition.ts +1 -0
- package/src/composer.ts +22 -1
- package/src/errors.ts +23 -0
- package/src/generate.ts +22 -4
- package/src/generators/traefik.test.ts +97 -0
- package/src/generators/traefik.ts +104 -0
- package/src/index.ts +7 -1
- package/src/migrations.test.ts +36 -0
- package/src/migrations.ts +49 -0
- package/src/resolver.ts +37 -3
- package/src/schema.ts +1 -0
- package/src/services/definitions/caddy.ts +23 -1
- package/src/services/definitions/cal-com.ts +91 -0
- package/src/services/definitions/grafana.ts +16 -1
- package/src/services/definitions/index.ts +9 -0
- package/src/services/definitions/neo4j.ts +96 -0
- package/src/services/definitions/traefik.ts +0 -2
- package/src/services/definitions/xyops.ts +94 -0
- package/src/skills/registry.ts +352 -6
- package/src/types.ts +5 -1
- package/src/validator.ts +16 -0
- package/src/version-manager.test.ts +134 -0
- package/src/version-manager.ts +12 -5
package/dist/validator.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.mjs","names":[],"sources":["../src/validator.ts"],"sourcesContent":["import { parse } from \"yaml\";\nimport type { ResolverError, ResolverOutput, Warning } from \"./types.js\";\n\nexport interface ValidationResult {\n\tvalid: boolean;\n\terrors: ResolverError[];\n\twarnings: Warning[];\n}\n\n/**\n * Validates a complete generated stack before writing files.\n * Checks for port conflicts, volume uniqueness, env completeness,\n * dependency ordering, YAML validity, and more.\n */\nexport function validate(\n\tresolved: ResolverOutput,\n\tcomposedYaml: string,\n\toptions: { domain?: string; generateSecrets?: boolean } = {},\n): ValidationResult {\n\tconst errors: ResolverError[] = [];\n\tconst warnings: Warning[] = [];\n\n\tcheckPortConflicts(resolved, errors);\n\tcheckVolumeUniqueness(resolved, errors);\n\tcheckEnvCompleteness(resolved, errors, warnings, options.generateSecrets ?? true);\n\tcheckNetworkConsistency(resolved, warnings);\n\tcheckDependencyDAG(resolved, errors);\n\tcheckYamlValidity(composedYaml, errors);\n\n\tif (options.domain) {\n\t\tcheckDomainFormat(options.domain, errors);\n\t}\n\n\treturn {\n\t\tvalid: errors.length === 0,\n\t\terrors,\n\t\twarnings,\n\t};\n}\n\nfunction checkPortConflicts(resolved: ResolverOutput, errors: ResolverError[]): void {\n\tconst hostPorts = new Map<number, string>();\n\tfor (const svc of resolved.services) {\n\t\tfor (const port of svc.definition.ports) {\n\t\t\tif (!port.exposed) continue;\n\t\t\tconst existing = hostPorts.get(port.host);\n\t\t\tif (existing) {\n\t\t\t\terrors.push({\n\t\t\t\t\ttype: \"port_conflict\",\n\t\t\t\t\tmessage: `Port ${port.host} is used by both \"${existing}\" and \"${svc.definition.name}\"`,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\thostPorts.set(port.host, svc.definition.name);\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction checkVolumeUniqueness(resolved: ResolverOutput, errors: ResolverError[]): void {\n\tconst volumeNames = new Map<string, string>();\n\tfor (const svc of resolved.services) {\n\t\tfor (const vol of svc.definition.volumes) {\n\t\t\tconst existing = volumeNames.get(vol.name);\n\t\t\tif (existing && existing !== svc.definition.id) {\n\t\t\t\terrors.push({\n\t\t\t\t\ttype: \"volume_conflict\",\n\t\t\t\t\tmessage: `Volume name \"${vol.name}\" is used by both \"${existing}\" and \"${svc.definition.id}\"`,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tvolumeNames.set(vol.name, svc.definition.id);\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction checkEnvCompleteness(\n\tresolved: ResolverOutput,\n\terrors: ResolverError[],\n\twarnings: Warning[],\n\tgenerateSecrets: boolean,\n): void {\n\tfor (const svc of resolved.services) {\n\t\tfor (const envVar of svc.definition.environment) {\n\t\t\tif (envVar.required && !envVar.defaultValue && !envVar.secret) {\n\t\t\t\terrors.push({\n\t\t\t\t\ttype: \"missing_env\",\n\t\t\t\t\tmessage: `Required environment variable \"${envVar.key}\" for \"${svc.definition.name}\" has no default value`,\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (envVar.secret && !generateSecrets && !envVar.defaultValue) {\n\t\t\t\twarnings.push({\n\t\t\t\t\ttype: \"secret_needed\",\n\t\t\t\t\tmessage: `Secret \"${envVar.key}\" for \"${svc.definition.name}\" needs to be configured manually`,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction checkNetworkConsistency(resolved: ResolverOutput, warnings: Warning[]): void {\n\tfor (const svc of resolved.services) {\n\t\tif (!svc.definition.networks.includes(\"openclaw-network\")) {\n\t\t\twarnings.push({\n\t\t\t\ttype: \"network\",\n\t\t\t\tmessage: `Service \"${svc.definition.name}\" is not on openclaw-network — it may not be reachable from OpenClaw`,\n\t\t\t});\n\t\t}\n\t}\n}\n\nfunction checkDependencyDAG(resolved: ResolverOutput, errors: ResolverError[]): void {\n\tconst ids = new Set(resolved.services.map((s) => s.definition.id));\n\tconst visited = new Set<string>();\n\tconst inStack = new Set<string>();\n\n\tconst adjList = new Map<string, string[]>();\n\tfor (const svc of resolved.services) {\n\t\tconst deps = [...svc.definition.requires, ...svc.definition.dependsOn].filter((d) =>\n\t\t\tids.has(d),\n\t\t);\n\t\tadjList.set(svc.definition.id, deps);\n\t}\n\n\tfunction hasCycle(node: string): boolean {\n\t\tif (inStack.has(node)) return true;\n\t\tif (visited.has(node)) return false;\n\t\tvisited.add(node);\n\t\tinStack.add(node);\n\t\tfor (const dep of adjList.get(node) ?? []) {\n\t\t\tif (hasCycle(dep)) return true;\n\t\t}\n\t\tinStack.delete(node);\n\t\treturn false;\n\t}\n\n\tfor (const id of ids) {\n\t\tif (hasCycle(id)) {\n\t\t\terrors.push({\n\t\t\t\ttype: \"cycle\",\n\t\t\t\tmessage: `Circular dependency detected involving \"${id}\"`,\n\t\t\t});\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nfunction checkYamlValidity(yaml: string, errors: ResolverError[]): void {\n\ttry {\n\t\tparse(yaml);\n\t} catch (e) {\n\t\terrors.push({\n\t\t\ttype: \"yaml_invalid\",\n\t\t\tmessage: `Generated YAML is not valid: ${e instanceof Error ? e.message : String(e)}`,\n\t\t});\n\t}\n}\n\nfunction checkDomainFormat(domain: string, errors: ResolverError[]): void {\n\tconst domainRegex = /^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,}$/;\n\tif (!domainRegex.test(domain)) {\n\t\terrors.push({\n\t\t\ttype: \"invalid_domain\",\n\t\t\tmessage: `\"${domain}\" is not a valid domain name`,\n\t\t});\n\t}\n}\n"],"mappings":";;;;;;;;AAcA,SAAgB,SACf,UACA,cACA,UAA0D,EAAE,EACzC;CACnB,MAAM,SAA0B,EAAE;CAClC,MAAM,WAAsB,EAAE;AAE9B,oBAAmB,UAAU,OAAO;AACpC,uBAAsB,UAAU,OAAO;AACvC,sBAAqB,UAAU,QAAQ,UAAU,QAAQ,mBAAmB,KAAK;AACjF,yBAAwB,UAAU,SAAS;AAC3C,oBAAmB,UAAU,OAAO;AACpC,mBAAkB,cAAc,OAAO;AAEvC,KAAI,QAAQ,OACX,mBAAkB,QAAQ,QAAQ,OAAO;AAG1C,QAAO;EACN,OAAO,OAAO,WAAW;EACzB;EACA;EACA;;AAGF,SAAS,mBAAmB,UAA0B,QAA+B;CACpF,MAAM,4BAAY,IAAI,KAAqB;AAC3C,MAAK,MAAM,OAAO,SAAS,SAC1B,MAAK,MAAM,QAAQ,IAAI,WAAW,OAAO;AACxC,MAAI,CAAC,KAAK,QAAS;EACnB,MAAM,WAAW,UAAU,IAAI,KAAK,KAAK;AACzC,MAAI,SACH,QAAO,KAAK;GACX,MAAM;GACN,SAAS,QAAQ,KAAK,KAAK,oBAAoB,SAAS,SAAS,IAAI,WAAW,KAAK;GACrF,CAAC;MAEF,WAAU,IAAI,KAAK,MAAM,IAAI,WAAW,KAAK;;;AAMjD,SAAS,sBAAsB,UAA0B,QAA+B;CACvF,MAAM,8BAAc,IAAI,KAAqB;AAC7C,MAAK,MAAM,OAAO,SAAS,SAC1B,MAAK,MAAM,OAAO,IAAI,WAAW,SAAS;EACzC,MAAM,WAAW,YAAY,IAAI,IAAI,KAAK;AAC1C,MAAI,YAAY,aAAa,IAAI,WAAW,GAC3C,QAAO,KAAK;GACX,MAAM;GACN,SAAS,gBAAgB,IAAI,KAAK,qBAAqB,SAAS,SAAS,IAAI,WAAW,GAAG;GAC3F,CAAC;MAEF,aAAY,IAAI,IAAI,MAAM,IAAI,WAAW,GAAG;;;AAMhD,SAAS,qBACR,UACA,QACA,UACA,iBACO;AACP,MAAK,MAAM,OAAO,SAAS,SAC1B,MAAK,MAAM,UAAU,IAAI,WAAW,aAAa;AAChD,MAAI,OAAO,YAAY,CAAC,OAAO,gBAAgB,CAAC,OAAO,OACtD,QAAO,KAAK;GACX,MAAM;GACN,SAAS,kCAAkC,OAAO,IAAI,SAAS,IAAI,WAAW,KAAK;GACnF,CAAC;AAEH,MAAI,OAAO,UAAU,CAAC,mBAAmB,CAAC,OAAO,aAChD,UAAS,KAAK;GACb,MAAM;GACN,SAAS,WAAW,OAAO,IAAI,SAAS,IAAI,WAAW,KAAK;GAC5D,CAAC
|
|
1
|
+
{"version":3,"file":"validator.mjs","names":[],"sources":["../src/validator.ts"],"sourcesContent":["import { parse } from \"yaml\";\nimport type { ResolverError, ResolverOutput, Warning } from \"./types.js\";\n\nexport interface ValidationResult {\n\tvalid: boolean;\n\terrors: ResolverError[];\n\twarnings: Warning[];\n}\n\n/**\n * Validates a complete generated stack before writing files.\n * Checks for port conflicts, volume uniqueness, env completeness,\n * dependency ordering, YAML validity, and more.\n */\nexport function validate(\n\tresolved: ResolverOutput,\n\tcomposedYaml: string,\n\toptions: { domain?: string; generateSecrets?: boolean } = {},\n): ValidationResult {\n\tconst errors: ResolverError[] = [];\n\tconst warnings: Warning[] = [];\n\n\tcheckPortConflicts(resolved, errors);\n\tcheckVolumeUniqueness(resolved, errors);\n\tcheckEnvCompleteness(resolved, errors, warnings, options.generateSecrets ?? true);\n\tcheckNetworkConsistency(resolved, warnings);\n\tcheckDependencyDAG(resolved, errors);\n\tcheckYamlValidity(composedYaml, errors);\n\n\tif (options.domain) {\n\t\tcheckDomainFormat(options.domain, errors);\n\t}\n\n\treturn {\n\t\tvalid: errors.length === 0,\n\t\terrors,\n\t\twarnings,\n\t};\n}\n\nfunction checkPortConflicts(resolved: ResolverOutput, errors: ResolverError[]): void {\n\tconst hostPorts = new Map<number, string>();\n\tfor (const svc of resolved.services) {\n\t\tfor (const port of svc.definition.ports) {\n\t\t\tif (!port.exposed) continue;\n\t\t\tconst existing = hostPorts.get(port.host);\n\t\t\tif (existing) {\n\t\t\t\terrors.push({\n\t\t\t\t\ttype: \"port_conflict\",\n\t\t\t\t\tmessage: `Port ${port.host} is used by both \"${existing}\" and \"${svc.definition.name}\"`,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\thostPorts.set(port.host, svc.definition.name);\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction checkVolumeUniqueness(resolved: ResolverOutput, errors: ResolverError[]): void {\n\tconst volumeNames = new Map<string, string>();\n\tfor (const svc of resolved.services) {\n\t\tfor (const vol of svc.definition.volumes) {\n\t\t\tconst existing = volumeNames.get(vol.name);\n\t\t\tif (existing && existing !== svc.definition.id) {\n\t\t\t\terrors.push({\n\t\t\t\t\ttype: \"volume_conflict\",\n\t\t\t\t\tmessage: `Volume name \"${vol.name}\" is used by both \"${existing}\" and \"${svc.definition.id}\"`,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tvolumeNames.set(vol.name, svc.definition.id);\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction checkEnvCompleteness(\n\tresolved: ResolverOutput,\n\terrors: ResolverError[],\n\twarnings: Warning[],\n\tgenerateSecrets: boolean,\n): void {\n\tfor (const svc of resolved.services) {\n\t\tfor (const envVar of svc.definition.environment) {\n\t\t\tif (envVar.required && !envVar.defaultValue && !envVar.secret) {\n\t\t\t\terrors.push({\n\t\t\t\t\ttype: \"missing_env\",\n\t\t\t\t\tmessage: `Required environment variable \"${envVar.key}\" for \"${svc.definition.name}\" has no default value`,\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (envVar.secret && !generateSecrets && !envVar.defaultValue) {\n\t\t\t\twarnings.push({\n\t\t\t\t\ttype: \"secret_needed\",\n\t\t\t\t\tmessage: `Secret \"${envVar.key}\" for \"${svc.definition.name}\" needs to be configured manually`,\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (envVar.validation && envVar.defaultValue) {\n\t\t\t\ttry {\n\t\t\t\t\tconst regex = new RegExp(envVar.validation);\n\t\t\t\t\tif (!regex.test(envVar.defaultValue)) {\n\t\t\t\t\t\twarnings.push({\n\t\t\t\t\t\t\ttype: \"env_validation\",\n\t\t\t\t\t\t\tmessage: `Environment variable \"${envVar.key}\" for \"${svc.definition.name}\" default value does not match validation pattern: ${envVar.validation}`,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\twarnings.push({\n\t\t\t\t\t\ttype: \"env_validation\",\n\t\t\t\t\t\tmessage: `Environment variable \"${envVar.key}\" for \"${svc.definition.name}\" has invalid validation regex: ${envVar.validation}`,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction checkNetworkConsistency(resolved: ResolverOutput, warnings: Warning[]): void {\n\tfor (const svc of resolved.services) {\n\t\tif (!svc.definition.networks.includes(\"openclaw-network\")) {\n\t\t\twarnings.push({\n\t\t\t\ttype: \"network\",\n\t\t\t\tmessage: `Service \"${svc.definition.name}\" is not on openclaw-network — it may not be reachable from OpenClaw`,\n\t\t\t});\n\t\t}\n\t}\n}\n\nfunction checkDependencyDAG(resolved: ResolverOutput, errors: ResolverError[]): void {\n\tconst ids = new Set(resolved.services.map((s) => s.definition.id));\n\tconst visited = new Set<string>();\n\tconst inStack = new Set<string>();\n\n\tconst adjList = new Map<string, string[]>();\n\tfor (const svc of resolved.services) {\n\t\tconst deps = [...svc.definition.requires, ...svc.definition.dependsOn].filter((d) =>\n\t\t\tids.has(d),\n\t\t);\n\t\tadjList.set(svc.definition.id, deps);\n\t}\n\n\tfunction hasCycle(node: string): boolean {\n\t\tif (inStack.has(node)) return true;\n\t\tif (visited.has(node)) return false;\n\t\tvisited.add(node);\n\t\tinStack.add(node);\n\t\tfor (const dep of adjList.get(node) ?? []) {\n\t\t\tif (hasCycle(dep)) return true;\n\t\t}\n\t\tinStack.delete(node);\n\t\treturn false;\n\t}\n\n\tfor (const id of ids) {\n\t\tif (hasCycle(id)) {\n\t\t\terrors.push({\n\t\t\t\ttype: \"cycle\",\n\t\t\t\tmessage: `Circular dependency detected involving \"${id}\"`,\n\t\t\t});\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nfunction checkYamlValidity(yaml: string, errors: ResolverError[]): void {\n\ttry {\n\t\tparse(yaml);\n\t} catch (e) {\n\t\terrors.push({\n\t\t\ttype: \"yaml_invalid\",\n\t\t\tmessage: `Generated YAML is not valid: ${e instanceof Error ? e.message : String(e)}`,\n\t\t});\n\t}\n}\n\nfunction checkDomainFormat(domain: string, errors: ResolverError[]): void {\n\tconst domainRegex = /^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,}$/;\n\tif (!domainRegex.test(domain)) {\n\t\terrors.push({\n\t\t\ttype: \"invalid_domain\",\n\t\t\tmessage: `\"${domain}\" is not a valid domain name`,\n\t\t});\n\t}\n}\n"],"mappings":"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"}
|
package/dist/validator.test.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as describe, r as it, t as globalExpect } from "./vi.2VT5v0um-
|
|
1
|
+
import { n as describe, r as it, t as globalExpect } from "./vi.2VT5v0um-YSByewHe.mjs";
|
|
2
2
|
import { resolve } from "./resolver.mjs";
|
|
3
3
|
import { compose } from "./composer.mjs";
|
|
4
4
|
import { validate } from "./validator.mjs";
|
|
@@ -5,7 +5,7 @@ import { ResolverOutput, ServiceDefinition, Warning } from "./types.mjs";
|
|
|
5
5
|
declare function getImageTag(serviceId: string): string | undefined;
|
|
6
6
|
/** Get the full image reference (image:tag) for a service */
|
|
7
7
|
declare function getImageReference(serviceId: string): string | undefined;
|
|
8
|
-
/** Pin all service image tags
|
|
8
|
+
/** Pin all service image tags to the registry-defined versions (returns a copy) */
|
|
9
9
|
declare function pinImageTags(resolved: ResolverOutput): ResolverOutput;
|
|
10
10
|
/** Check for known compatibility issues between services */
|
|
11
11
|
declare function checkCompatibility(services: ServiceDefinition[]): Warning[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version-manager.d.mts","names":[],"sources":["../src/version-manager.ts"],"mappings":";;;;iBAIgB,WAAA,CAAY,SAAA;AAA5B;AAAA,iBAMgB,iBAAA,CAAkB,SAAA;;iBAOlB,YAAA,CAAa,QAAA,EAAU,cAAA,GAAiB,cAAA;;
|
|
1
|
+
{"version":3,"file":"version-manager.d.mts","names":[],"sources":["../src/version-manager.ts"],"mappings":";;;;iBAIgB,WAAA,CAAY,SAAA;AAA5B;AAAA,iBAMgB,iBAAA,CAAkB,SAAA;;iBAOlB,YAAA,CAAa,QAAA,EAAU,cAAA,GAAiB,cAAA;;iBAkBxC,kBAAA,CAAmB,QAAA,EAAU,iBAAA,KAAsB,OAAA"}
|
package/dist/version-manager.mjs
CHANGED
|
@@ -11,14 +11,20 @@ function getImageReference(serviceId) {
|
|
|
11
11
|
if (!svc) return void 0;
|
|
12
12
|
return `${svc.image}:${svc.imageTag}`;
|
|
13
13
|
}
|
|
14
|
-
/** Pin all service image tags
|
|
14
|
+
/** Pin all service image tags to the registry-defined versions (returns a copy) */
|
|
15
15
|
function pinImageTags(resolved) {
|
|
16
16
|
return {
|
|
17
17
|
...resolved,
|
|
18
|
-
services: resolved.services.map((s) =>
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
services: resolved.services.map((s) => {
|
|
19
|
+
const pinnedTag = getServiceById(s.definition.id)?.imageTag ?? s.definition.imageTag;
|
|
20
|
+
return {
|
|
21
|
+
...s,
|
|
22
|
+
definition: {
|
|
23
|
+
...s.definition,
|
|
24
|
+
imageTag: pinnedTag
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
})
|
|
22
28
|
};
|
|
23
29
|
}
|
|
24
30
|
/** Check for known compatibility issues between services */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version-manager.mjs","names":[],"sources":["../src/version-manager.ts"],"sourcesContent":["import { getServiceById } from \"./services/registry.js\";\nimport type { ResolverOutput, ServiceDefinition, Warning } from \"./types.js\";\n\n/** Get the pinned image tag for a service */\nexport function getImageTag(serviceId: string): string | undefined {\n\tconst svc = getServiceById(serviceId);\n\treturn svc?.imageTag;\n}\n\n/** Get the full image reference (image:tag) for a service */\nexport function getImageReference(serviceId: string): string | undefined {\n\tconst svc = getServiceById(serviceId);\n\tif (!svc) return undefined;\n\treturn `${svc.image}:${svc.imageTag}`;\n}\n\n/** Pin all service image tags
|
|
1
|
+
{"version":3,"file":"version-manager.mjs","names":[],"sources":["../src/version-manager.ts"],"sourcesContent":["import { getServiceById } from \"./services/registry.js\";\nimport type { ResolverOutput, ServiceDefinition, Warning } from \"./types.js\";\n\n/** Get the pinned image tag for a service */\nexport function getImageTag(serviceId: string): string | undefined {\n\tconst svc = getServiceById(serviceId);\n\treturn svc?.imageTag;\n}\n\n/** Get the full image reference (image:tag) for a service */\nexport function getImageReference(serviceId: string): string | undefined {\n\tconst svc = getServiceById(serviceId);\n\tif (!svc) return undefined;\n\treturn `${svc.image}:${svc.imageTag}`;\n}\n\n/** Pin all service image tags to the registry-defined versions (returns a copy) */\nexport function pinImageTags(resolved: ResolverOutput): ResolverOutput {\n\treturn {\n\t\t...resolved,\n\t\tservices: resolved.services.map((s) => {\n\t\t\tconst registryDef = getServiceById(s.definition.id);\n\t\t\tconst pinnedTag = registryDef?.imageTag ?? s.definition.imageTag;\n\t\t\treturn {\n\t\t\t\t...s,\n\t\t\t\tdefinition: {\n\t\t\t\t\t...s.definition,\n\t\t\t\t\timageTag: pinnedTag,\n\t\t\t\t},\n\t\t\t};\n\t\t}),\n\t};\n}\n\n/** Check for known compatibility issues between services */\nexport function checkCompatibility(services: ServiceDefinition[]): Warning[] {\n\tconst warnings: Warning[] = [];\n\tconst ids = new Set(services.map((s) => s.id));\n\n\t// Redis + Valkey conflict (should already be caught by resolver, but double-check)\n\tif (ids.has(\"redis\") && ids.has(\"valkey\")) {\n\t\twarnings.push({\n\t\t\ttype: \"compatibility\",\n\t\t\tmessage: \"Redis and Valkey cannot coexist. Choose one.\",\n\t\t});\n\t}\n\t// Caddy + Traefik conflict\n\tif (ids.has(\"caddy\") && ids.has(\"traefik\")) {\n\t\twarnings.push({\n\t\t\ttype: \"compatibility\",\n\t\t\tmessage: \"Caddy and Traefik cannot coexist. Choose one reverse proxy.\",\n\t\t});\n\t}\n\t// Multiple vector DBs warning\n\tconst vectorDbs = [\"qdrant\", \"chromadb\", \"weaviate\"].filter((id) => ids.has(id));\n\tif (vectorDbs.length > 1) {\n\t\twarnings.push({\n\t\t\ttype: \"compatibility\",\n\t\t\tmessage: `Multiple vector databases selected (${vectorDbs.join(\", \")}). Consider using just one to reduce resource usage.`,\n\t\t});\n\t}\n\t// GPU services without GPU\n\tconst gpuServices = services.filter((s) => s.gpuRequired);\n\tif (gpuServices.length > 0) {\n\t\twarnings.push({\n\t\t\ttype: \"compatibility\",\n\t\t\tmessage: `Services requiring GPU: ${gpuServices.map((s) => s.name).join(\", \")}. Ensure NVIDIA Container Toolkit is installed.`,\n\t\t});\n\t}\n\n\treturn warnings;\n}\n"],"mappings":";;;;AAIA,SAAgB,YAAY,WAAuC;AAElE,QADY,eAAe,UAAU,EACzB;;;AAIb,SAAgB,kBAAkB,WAAuC;CACxE,MAAM,MAAM,eAAe,UAAU;AACrC,KAAI,CAAC,IAAK,QAAO;AACjB,QAAO,GAAG,IAAI,MAAM,GAAG,IAAI;;;AAI5B,SAAgB,aAAa,UAA0C;AACtE,QAAO;EACN,GAAG;EACH,UAAU,SAAS,SAAS,KAAK,MAAM;GAEtC,MAAM,YADc,eAAe,EAAE,WAAW,GAAG,EACpB,YAAY,EAAE,WAAW;AACxD,UAAO;IACN,GAAG;IACH,YAAY;KACX,GAAG,EAAE;KACL,UAAU;KACV;IACD;IACA;EACF;;;AAIF,SAAgB,mBAAmB,UAA0C;CAC5E,MAAM,WAAsB,EAAE;CAC9B,MAAM,MAAM,IAAI,IAAI,SAAS,KAAK,MAAM,EAAE,GAAG,CAAC;AAG9C,KAAI,IAAI,IAAI,QAAQ,IAAI,IAAI,IAAI,SAAS,CACxC,UAAS,KAAK;EACb,MAAM;EACN,SAAS;EACT,CAAC;AAGH,KAAI,IAAI,IAAI,QAAQ,IAAI,IAAI,IAAI,UAAU,CACzC,UAAS,KAAK;EACb,MAAM;EACN,SAAS;EACT,CAAC;CAGH,MAAM,YAAY;EAAC;EAAU;EAAY;EAAW,CAAC,QAAQ,OAAO,IAAI,IAAI,GAAG,CAAC;AAChF,KAAI,UAAU,SAAS,EACtB,UAAS,KAAK;EACb,MAAM;EACN,SAAS,uCAAuC,UAAU,KAAK,KAAK,CAAC;EACrE,CAAC;CAGH,MAAM,cAAc,SAAS,QAAQ,MAAM,EAAE,YAAY;AACzD,KAAI,YAAY,SAAS,EACxB,UAAS,KAAK;EACb,MAAM;EACN,SAAS,2BAA2B,YAAY,KAAK,MAAM,EAAE,KAAK,CAAC,KAAK,KAAK,CAAC;EAC9E,CAAC;AAGH,QAAO"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { n as describe, r as it, t as globalExpect } from "./vi.2VT5v0um-YSByewHe.mjs";
|
|
2
|
+
import { getAllServices, getServiceById } from "./services/registry.mjs";
|
|
3
|
+
import { resolve } from "./resolver.mjs";
|
|
4
|
+
import { checkCompatibility, getImageReference, getImageTag, pinImageTags } from "./version-manager.mjs";
|
|
5
|
+
|
|
6
|
+
//#region src/version-manager.test.ts
|
|
7
|
+
describe("getImageTag", () => {
|
|
8
|
+
it("returns the tag for a known service", () => {
|
|
9
|
+
const tag = getImageTag("redis");
|
|
10
|
+
globalExpect(tag).toBeDefined();
|
|
11
|
+
globalExpect(typeof tag).toBe("string");
|
|
12
|
+
});
|
|
13
|
+
it("returns undefined for an unknown service", () => {
|
|
14
|
+
globalExpect(getImageTag("nonexistent-service-xyz")).toBeUndefined();
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
describe("getImageReference", () => {
|
|
18
|
+
it("returns image:tag for a known service", () => {
|
|
19
|
+
const ref = getImageReference("redis");
|
|
20
|
+
globalExpect(ref).toBeDefined();
|
|
21
|
+
globalExpect(ref).toContain(":");
|
|
22
|
+
globalExpect(ref).toMatch(/^.+:.+$/);
|
|
23
|
+
});
|
|
24
|
+
it("returns undefined for an unknown service", () => {
|
|
25
|
+
globalExpect(getImageReference("nonexistent-service-xyz")).toBeUndefined();
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
describe("pinImageTags", () => {
|
|
29
|
+
it("pins image tags from the registry for all services", () => {
|
|
30
|
+
const resolved = resolve({
|
|
31
|
+
services: ["redis", "postgresql"],
|
|
32
|
+
skillPacks: [],
|
|
33
|
+
proxy: "none",
|
|
34
|
+
gpu: false,
|
|
35
|
+
platform: "linux/amd64"
|
|
36
|
+
});
|
|
37
|
+
const pinned = pinImageTags(resolved);
|
|
38
|
+
globalExpect(pinned.services).toHaveLength(resolved.services.length);
|
|
39
|
+
for (const svc of pinned.services) {
|
|
40
|
+
globalExpect(svc.definition.imageTag).toBeDefined();
|
|
41
|
+
globalExpect(typeof svc.definition.imageTag).toBe("string");
|
|
42
|
+
globalExpect(svc.definition.imageTag.length).toBeGreaterThan(0);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
it("does not mutate the original resolved output", () => {
|
|
46
|
+
const resolved = resolve({
|
|
47
|
+
services: ["redis"],
|
|
48
|
+
skillPacks: [],
|
|
49
|
+
proxy: "none",
|
|
50
|
+
gpu: false,
|
|
51
|
+
platform: "linux/amd64"
|
|
52
|
+
});
|
|
53
|
+
const originalTag = resolved.services[0]?.definition.imageTag;
|
|
54
|
+
pinImageTags(resolved);
|
|
55
|
+
globalExpect(resolved.services[0]?.definition.imageTag).toBe(originalTag);
|
|
56
|
+
});
|
|
57
|
+
it("preserves non-tag properties", () => {
|
|
58
|
+
const resolved = resolve({
|
|
59
|
+
services: ["redis"],
|
|
60
|
+
skillPacks: [],
|
|
61
|
+
proxy: "none",
|
|
62
|
+
gpu: false,
|
|
63
|
+
platform: "linux/amd64"
|
|
64
|
+
});
|
|
65
|
+
const pinned = pinImageTags(resolved);
|
|
66
|
+
globalExpect(pinned.services[0]?.definition.id).toBe("redis");
|
|
67
|
+
globalExpect(pinned.services[0]?.definition.name).toBe("Redis");
|
|
68
|
+
globalExpect(pinned.estimatedMemoryMB).toBe(resolved.estimatedMemoryMB);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
describe("checkCompatibility", () => {
|
|
72
|
+
it("warns when Redis and Valkey are both selected", () => {
|
|
73
|
+
const all = getAllServices();
|
|
74
|
+
const redis = all.find((s) => s.id === "redis");
|
|
75
|
+
const valkey = all.find((s) => s.id === "valkey");
|
|
76
|
+
if (redis && valkey) globalExpect(checkCompatibility([redis, valkey]).some((w) => w.message.includes("Redis") && w.message.includes("Valkey"))).toBe(true);
|
|
77
|
+
});
|
|
78
|
+
it("warns when Caddy and Traefik are both selected", () => {
|
|
79
|
+
const all = getAllServices();
|
|
80
|
+
const caddy = all.find((s) => s.id === "caddy");
|
|
81
|
+
const traefik = all.find((s) => s.id === "traefik");
|
|
82
|
+
if (caddy && traefik) globalExpect(checkCompatibility([caddy, traefik]).some((w) => w.message.includes("Caddy") && w.message.includes("Traefik"))).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
it("warns about multiple vector databases", () => {
|
|
85
|
+
const all = getAllServices();
|
|
86
|
+
const qdrant = all.find((s) => s.id === "qdrant");
|
|
87
|
+
const chromadb = all.find((s) => s.id === "chromadb");
|
|
88
|
+
if (qdrant && chromadb) globalExpect(checkCompatibility([qdrant, chromadb]).some((w) => w.message.includes("vector database"))).toBe(true);
|
|
89
|
+
});
|
|
90
|
+
it("warns about GPU services", () => {
|
|
91
|
+
const gpuService = getAllServices().find((s) => s.gpuRequired);
|
|
92
|
+
if (gpuService) globalExpect(checkCompatibility([gpuService]).some((w) => w.message.includes("GPU"))).toBe(true);
|
|
93
|
+
});
|
|
94
|
+
it("returns no warnings for a single non-conflicting service", () => {
|
|
95
|
+
const redis = getServiceById("redis");
|
|
96
|
+
if (redis) globalExpect(checkCompatibility([redis]).filter((w) => w.type === "compatibility" && !w.message.includes("GPU"))).toHaveLength(0);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
//#endregion
|
|
101
|
+
export { };
|
|
102
|
+
//# sourceMappingURL=version-manager.test.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version-manager.test.mjs","names":[],"sources":["../src/version-manager.test.ts"],"sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { resolve } from \"./resolver.js\";\nimport { getAllServices, getServiceById } from \"./services/registry.js\";\nimport { checkCompatibility, getImageReference, getImageTag, pinImageTags } from \"./version-manager.js\";\n\ndescribe(\"getImageTag\", () => {\n\tit(\"returns the tag for a known service\", () => {\n\t\tconst tag = getImageTag(\"redis\");\n\t\texpect(tag).toBeDefined();\n\t\texpect(typeof tag).toBe(\"string\");\n\t});\n\n\tit(\"returns undefined for an unknown service\", () => {\n\t\texpect(getImageTag(\"nonexistent-service-xyz\")).toBeUndefined();\n\t});\n});\n\ndescribe(\"getImageReference\", () => {\n\tit(\"returns image:tag for a known service\", () => {\n\t\tconst ref = getImageReference(\"redis\");\n\t\texpect(ref).toBeDefined();\n\t\texpect(ref).toContain(\":\");\n\t\texpect(ref).toMatch(/^.+:.+$/);\n\t});\n\n\tit(\"returns undefined for an unknown service\", () => {\n\t\texpect(getImageReference(\"nonexistent-service-xyz\")).toBeUndefined();\n\t});\n});\n\ndescribe(\"pinImageTags\", () => {\n\tit(\"pins image tags from the registry for all services\", () => {\n\t\tconst resolved = resolve({\n\t\t\tservices: [\"redis\", \"postgresql\"],\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t});\n\n\t\tconst pinned = pinImageTags(resolved);\n\n\t\texpect(pinned.services).toHaveLength(resolved.services.length);\n\n\t\tfor (const svc of pinned.services) {\n\t\t\texpect(svc.definition.imageTag).toBeDefined();\n\t\t\texpect(typeof svc.definition.imageTag).toBe(\"string\");\n\t\t\texpect(svc.definition.imageTag.length).toBeGreaterThan(0);\n\t\t}\n\t});\n\n\tit(\"does not mutate the original resolved output\", () => {\n\t\tconst resolved = resolve({\n\t\t\tservices: [\"redis\"],\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t});\n\n\t\tconst originalTag = resolved.services[0]?.definition.imageTag;\n\t\tpinImageTags(resolved);\n\n\t\texpect(resolved.services[0]?.definition.imageTag).toBe(originalTag);\n\t});\n\n\tit(\"preserves non-tag properties\", () => {\n\t\tconst resolved = resolve({\n\t\t\tservices: [\"redis\"],\n\t\t\tskillPacks: [],\n\t\t\tproxy: \"none\",\n\t\t\tgpu: false,\n\t\t\tplatform: \"linux/amd64\",\n\t\t});\n\n\t\tconst pinned = pinImageTags(resolved);\n\t\texpect(pinned.services[0]?.definition.id).toBe(\"redis\");\n\t\texpect(pinned.services[0]?.definition.name).toBe(\"Redis\");\n\t\texpect(pinned.estimatedMemoryMB).toBe(resolved.estimatedMemoryMB);\n\t});\n});\n\ndescribe(\"checkCompatibility\", () => {\n\tit(\"warns when Redis and Valkey are both selected\", () => {\n\t\tconst all = getAllServices();\n\t\tconst redis = all.find((s) => s.id === \"redis\");\n\t\tconst valkey = all.find((s) => s.id === \"valkey\");\n\n\t\tif (redis && valkey) {\n\t\t\tconst warnings = checkCompatibility([redis, valkey]);\n\t\t\texpect(warnings.some((w) => w.message.includes(\"Redis\") && w.message.includes(\"Valkey\"))).toBe(true);\n\t\t}\n\t});\n\n\tit(\"warns when Caddy and Traefik are both selected\", () => {\n\t\tconst all = getAllServices();\n\t\tconst caddy = all.find((s) => s.id === \"caddy\");\n\t\tconst traefik = all.find((s) => s.id === \"traefik\");\n\n\t\tif (caddy && traefik) {\n\t\t\tconst warnings = checkCompatibility([caddy, traefik]);\n\t\t\texpect(warnings.some((w) => w.message.includes(\"Caddy\") && w.message.includes(\"Traefik\"))).toBe(true);\n\t\t}\n\t});\n\n\tit(\"warns about multiple vector databases\", () => {\n\t\tconst all = getAllServices();\n\t\tconst qdrant = all.find((s) => s.id === \"qdrant\");\n\t\tconst chromadb = all.find((s) => s.id === \"chromadb\");\n\n\t\tif (qdrant && chromadb) {\n\t\t\tconst warnings = checkCompatibility([qdrant, chromadb]);\n\t\t\texpect(warnings.some((w) => w.message.includes(\"vector database\"))).toBe(true);\n\t\t}\n\t});\n\n\tit(\"warns about GPU services\", () => {\n\t\tconst all = getAllServices();\n\t\tconst gpuService = all.find((s) => s.gpuRequired);\n\n\t\tif (gpuService) {\n\t\t\tconst warnings = checkCompatibility([gpuService]);\n\t\t\texpect(warnings.some((w) => w.message.includes(\"GPU\"))).toBe(true);\n\t\t}\n\t});\n\n\tit(\"returns no warnings for a single non-conflicting service\", () => {\n\t\tconst redis = getServiceById(\"redis\");\n\t\tif (redis) {\n\t\t\tconst warnings = checkCompatibility([redis]);\n\t\t\texpect(warnings.filter((w) => w.type === \"compatibility\" && !w.message.includes(\"GPU\"))).toHaveLength(0);\n\t\t}\n\t});\n});\n"],"mappings":"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"}
|
|
@@ -9184,7 +9184,7 @@ function manageArtifactAttachment(attachment) {
|
|
|
9184
9184
|
}
|
|
9185
9185
|
|
|
9186
9186
|
//#endregion
|
|
9187
|
-
//#region node_modules/.pnpm/vitest@4.0.18_@types+node@25.
|
|
9187
|
+
//#region node_modules/.pnpm/vitest@4.0.18_@types+node@25.3.0_yaml@2.8.2/node_modules/vitest/dist/chunks/utils.DvEY5TfP.js
|
|
9188
9188
|
const NAME_WORKER_STATE = "__vitest_worker__";
|
|
9189
9189
|
function getWorkerState() {
|
|
9190
9190
|
const workerState = globalThis[NAME_WORKER_STATE];
|
|
@@ -10296,11 +10296,11 @@ var SnapshotClient = class {
|
|
|
10296
10296
|
};
|
|
10297
10297
|
|
|
10298
10298
|
//#endregion
|
|
10299
|
-
//#region node_modules/.pnpm/vitest@4.0.18_@types+node@25.
|
|
10299
|
+
//#region node_modules/.pnpm/vitest@4.0.18_@types+node@25.3.0_yaml@2.8.2/node_modules/vitest/dist/chunks/_commonjsHelpers.D26ty3Ew.js
|
|
10300
10300
|
var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
|
|
10301
10301
|
|
|
10302
10302
|
//#endregion
|
|
10303
|
-
//#region node_modules/.pnpm/vitest@4.0.18_@types+node@25.
|
|
10303
|
+
//#region node_modules/.pnpm/vitest@4.0.18_@types+node@25.3.0_yaml@2.8.2/node_modules/vitest/dist/chunks/date.Bq6ZW5rf.js
|
|
10304
10304
|
const RealDate = Date;
|
|
10305
10305
|
let now = null;
|
|
10306
10306
|
var MockDate = class MockDate extends RealDate {
|
|
@@ -10349,7 +10349,7 @@ function resetDate() {
|
|
|
10349
10349
|
}
|
|
10350
10350
|
|
|
10351
10351
|
//#endregion
|
|
10352
|
-
//#region node_modules/.pnpm/vitest@4.0.18_@types+node@25.
|
|
10352
|
+
//#region node_modules/.pnpm/vitest@4.0.18_@types+node@25.3.0_yaml@2.8.2/node_modules/vitest/dist/chunks/vi.2VT5v0um.js
|
|
10353
10353
|
const unsupported = [
|
|
10354
10354
|
"matchSnapshot",
|
|
10355
10355
|
"toMatchSnapshot",
|
|
@@ -12913,4 +12913,4 @@ function getImporter(name) {
|
|
|
12913
12913
|
|
|
12914
12914
|
//#endregion
|
|
12915
12915
|
export { describe as n, it as r, globalExpect as t };
|
|
12916
|
-
//# sourceMappingURL=vi.2VT5v0um-
|
|
12916
|
+
//# sourceMappingURL=vi.2VT5v0um-YSByewHe.mjs.map
|