@stacksjs/defaults 0.75.361 → 0.75.362

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/ai/skills/stacks-ai/SKILL.md +22 -0
  2. package/ai/skills/stacks-native/CATALOG.md +2 -2
  3. package/ai/skills/stacks-technical-diagrams/LICENSE +202 -22
  4. package/ai/skills/stacks-technical-diagrams/LICENSE-MIT +22 -0
  5. package/ai/skills/stacks-technical-diagrams/NOTICE +26 -0
  6. package/ai/skills/stacks-technical-diagrams/SKILL.md +106 -266
  7. package/ai/skills/stacks-technical-diagrams/THIRD_PARTY_NOTICES.md +11 -2
  8. package/ai/skills/stacks-technical-diagrams/UPSTREAM.json +12 -0
  9. package/ai/skills/stacks-technical-diagrams/agents/openai.yaml +2 -2
  10. package/ai/skills/stacks-technical-diagrams/assets/template.html +3655 -3798
  11. package/ai/skills/stacks-technical-diagrams/bin/archify.mjs +7290 -0
  12. package/ai/skills/stacks-technical-diagrams/bin/delivery-update.mjs +7 -0
  13. package/ai/skills/stacks-technical-diagrams/bin/diagrams +1 -0
  14. package/ai/skills/stacks-technical-diagrams/bin/finalize.mjs +1056 -0
  15. package/ai/skills/stacks-technical-diagrams/bin/open-artifact.mjs +72 -12
  16. package/ai/skills/stacks-technical-diagrams/bin/preview.mjs +348 -31
  17. package/ai/skills/stacks-technical-diagrams/bin/recover-output.mjs +41 -0
  18. package/ai/skills/stacks-technical-diagrams/bin/technical-diagrams.mjs +3 -2097
  19. package/ai/skills/stacks-technical-diagrams/bin/visual-check.mjs +2142 -157
  20. package/ai/skills/stacks-technical-diagrams/brand-marks/README.md +3 -3
  21. package/ai/skills/stacks-technical-diagrams/delta/architecture-delta.mjs +130 -33
  22. package/ai/skills/stacks-technical-diagrams/examples/agent-run.lifecycle.json +25 -30
  23. package/ai/skills/stacks-technical-diagrams/examples/agent-tool-call.workflow.json +0 -20
  24. package/ai/skills/stacks-technical-diagrams/examples/archify-dev-activity.timeline.json +176 -0
  25. package/ai/skills/stacks-technical-diagrams/examples/archify-repository.tree.json +197 -0
  26. package/ai/skills/stacks-technical-diagrams/examples/async-job-roundtrip.sequence.json +1 -6
  27. package/ai/skills/stacks-technical-diagrams/examples/brand-aware-delivery.architecture.json +3 -16
  28. package/ai/skills/stacks-technical-diagrams/examples/cache-miss-request.sequence.json +2 -7
  29. package/ai/skills/stacks-technical-diagrams/examples/checkout-platform.base.architecture.json +2 -1
  30. package/ai/skills/stacks-technical-diagrams/examples/checkout-platform.head.architecture.json +2 -1
  31. package/ai/skills/stacks-technical-diagrams/examples/checkout-request.waterfall.json +20 -0
  32. package/ai/skills/stacks-technical-diagrams/examples/class-payment-processors-rendered.html +14987 -0
  33. package/ai/skills/stacks-technical-diagrams/examples/class-payments-rendered.html +14787 -0
  34. package/ai/skills/stacks-technical-diagrams/examples/dataflow-product-analytics.html +14880 -0
  35. package/ai/skills/stacks-technical-diagrams/examples/delivery.explainer.json +155 -0
  36. package/ai/skills/stacks-technical-diagrams/examples/deployment-release.lifecycle.json +57 -34
  37. package/ai/skills/stacks-technical-diagrams/examples/erd-orders-rendered.html +15112 -0
  38. package/ai/skills/stacks-technical-diagrams/examples/event-stream.dataflow.json +1 -6
  39. package/ai/skills/stacks-technical-diagrams/examples/example-rebuild.waterfall.json +212 -0
  40. package/ai/skills/stacks-technical-diagrams/examples/holiday-planning.workflow.json +143 -0
  41. package/ai/skills/stacks-technical-diagrams/examples/incident-response.workflow.json +1 -6
  42. package/ai/skills/stacks-technical-diagrams/examples/lifecycle-agent-run.html +14892 -0
  43. package/ai/skills/stacks-technical-diagrams/examples/locales/de.json +463 -0
  44. package/ai/skills/stacks-technical-diagrams/examples/locales/fr.json +463 -0
  45. package/ai/skills/stacks-technical-diagrams/examples/locales/fr.partial.json +22 -0
  46. package/ai/skills/stacks-technical-diagrams/examples/locales/it.json +463 -0
  47. package/ai/skills/stacks-technical-diagrams/examples/locales/ja.json +463 -0
  48. package/ai/skills/stacks-technical-diagrams/examples/locales/pt.json +463 -0
  49. package/ai/skills/stacks-technical-diagrams/examples/locales/ru.json +463 -0
  50. package/ai/skills/stacks-technical-diagrams/examples/orders.erd.json +337 -0
  51. package/ai/skills/stacks-technical-diagrams/examples/payment-incident.timeline.json +24 -0
  52. package/ai/skills/stacks-technical-diagrams/examples/payment-platform.tree.json +22 -0
  53. package/ai/skills/stacks-technical-diagrams/examples/payment-processors.class.json +177 -0
  54. package/ai/skills/stacks-technical-diagrams/examples/payments.class.json +85 -0
  55. package/ai/skills/stacks-technical-diagrams/examples/product-analytics.dataflow.json +1 -6
  56. package/ai/skills/stacks-technical-diagrams/examples/production-deployment.architecture.json +3 -23
  57. package/ai/skills/stacks-technical-diagrams/examples/release-delivery.workflow.json +1 -6
  58. package/ai/skills/stacks-technical-diagrams/examples/sequence-cache-miss-request.html +14895 -0
  59. package/ai/skills/stacks-technical-diagrams/examples/subscription-billing-rendered.html +15369 -0
  60. package/ai/skills/stacks-technical-diagrams/examples/subscription-billing.erd.json +548 -0
  61. package/ai/skills/stacks-technical-diagrams/examples/timeline-archify-dev-activity-rendered.html +15002 -0
  62. package/ai/skills/stacks-technical-diagrams/examples/timeline-payment-incident-rendered.html +14772 -0
  63. package/ai/skills/stacks-technical-diagrams/examples/tree-archify-repository-rendered.html +15071 -0
  64. package/ai/skills/stacks-technical-diagrams/examples/tree-payment-platform-rendered.html +14810 -0
  65. package/ai/skills/stacks-technical-diagrams/examples/waterfall-checkout-request-rendered.html +14754 -0
  66. package/ai/skills/stacks-technical-diagrams/examples/waterfall-example-rebuild-rendered.html +14940 -0
  67. package/ai/skills/stacks-technical-diagrams/examples/web-app-rendered.html +14889 -0
  68. package/ai/skills/stacks-technical-diagrams/examples/web-app.architecture.json +1 -6
  69. package/ai/skills/stacks-technical-diagrams/examples/workflow-agent-tool-call-rendered.html +14907 -0
  70. package/ai/skills/stacks-technical-diagrams/explainer/browser-check.mjs +208 -0
  71. package/ai/skills/stacks-technical-diagrams/explainer/charts.mjs +70 -0
  72. package/ai/skills/stacks-technical-diagrams/explainer/check.mjs +168 -0
  73. package/ai/skills/stacks-technical-diagrams/explainer/diagrams.mjs +518 -0
  74. package/ai/skills/stacks-technical-diagrams/explainer/diff.mjs +124 -0
  75. package/ai/skills/stacks-technical-diagrams/explainer/inline.mjs +155 -0
  76. package/ai/skills/stacks-technical-diagrams/explainer/layout.mjs +35 -0
  77. package/ai/skills/stacks-technical-diagrams/explainer/page-bundle.mjs +16 -0
  78. package/ai/skills/stacks-technical-diagrams/explainer/parse.mjs +300 -0
  79. package/ai/skills/stacks-technical-diagrams/explainer/patch.mjs +117 -0
  80. package/ai/skills/stacks-technical-diagrams/explainer/prepare.mjs +131 -0
  81. package/ai/skills/stacks-technical-diagrams/explainer/reader.css +472 -0
  82. package/ai/skills/stacks-technical-diagrams/explainer/reader.js +667 -0
  83. package/ai/skills/stacks-technical-diagrams/explainer/render.mjs +224 -0
  84. package/ai/skills/stacks-technical-diagrams/explainer/revision.mjs +20 -0
  85. package/ai/skills/stacks-technical-diagrams/explainer/sources.mjs +318 -0
  86. package/ai/skills/stacks-technical-diagrams/explainer/validate.mjs +138 -0
  87. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/LICENSE +40 -0
  88. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/README.md +5 -0
  89. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/3DKXF73K.js +4 -0
  90. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/42UTT654.js +3 -0
  91. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/DKTLEL5E.js +1 -0
  92. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/EEIX5ZYH.js +2 -0
  93. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/EJ5KOXYD.js +25 -0
  94. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/GUJTEFZK.js +1 -0
  95. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/IZOKQ2GT.js +4 -0
  96. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/L4LY553T.js +3 -0
  97. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/MSKIJHB2.js +2 -0
  98. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/RVARI57O.js +1 -0
  99. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/UKSIWDDN.js +2 -0
  100. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/UPTXNAXN.js +2 -0
  101. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/XTDUIGUI.js +9 -0
  102. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/YAYTP6L5.js +9 -0
  103. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/core.js +1 -0
  104. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/diagrams/pie.js +1 -0
  105. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/diagrams/quadrantChart.js +1 -0
  106. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/diagrams/radar.js +1 -0
  107. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/diagrams/sankey.js +1 -0
  108. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/diagrams/venn.js +1 -0
  109. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/diagrams/xychart.js +1 -0
  110. package/ai/skills/stacks-technical-diagrams/explainer/writing.mjs +139 -0
  111. package/ai/skills/stacks-technical-diagrams/locales/README.md +40 -0
  112. package/ai/skills/stacks-technical-diagrams/locales/ar.json +488 -0
  113. package/ai/skills/stacks-technical-diagrams/locales/en.json +488 -0
  114. package/ai/skills/stacks-technical-diagrams/locales/es.json +488 -0
  115. package/ai/skills/stacks-technical-diagrams/locales/ko.json +488 -0
  116. package/ai/skills/stacks-technical-diagrams/locales/manifest.json +10 -0
  117. package/ai/skills/stacks-technical-diagrams/locales/zh-CN.json +488 -0
  118. package/ai/skills/stacks-technical-diagrams/locales/zh-TW.json +488 -0
  119. package/ai/skills/stacks-technical-diagrams/migrations/workflow-v2.mjs +1 -0
  120. package/ai/skills/stacks-technical-diagrams/recipes/scenarios.mjs +115 -51
  121. package/ai/skills/stacks-technical-diagrams/references/architecture-layout-repair.md +18 -0
  122. package/ai/skills/stacks-technical-diagrams/references/authoring-contract.md +453 -51
  123. package/ai/skills/stacks-technical-diagrams/references/authoring-defaults.md +57 -0
  124. package/ai/skills/stacks-technical-diagrams/references/brand-marks.md +57 -3
  125. package/ai/skills/stacks-technical-diagrams/references/delivery-contract.md +603 -58
  126. package/ai/skills/stacks-technical-diagrams/references/explainer-authoring.md +274 -0
  127. package/ai/skills/stacks-technical-diagrams/references/repository-authoring.md +126 -0
  128. package/ai/skills/stacks-technical-diagrams/references/update-awareness.md +13 -0
  129. package/ai/skills/stacks-technical-diagrams/references/viewer-runtime.md +18 -11
  130. package/ai/skills/stacks-technical-diagrams/renderers/architecture/grid.mjs +1 -1
  131. package/ai/skills/stacks-technical-diagrams/renderers/architecture/labels.mjs +2 -0
  132. package/ai/skills/stacks-technical-diagrams/renderers/architecture/render-architecture.mjs +550 -424
  133. package/ai/skills/stacks-technical-diagrams/renderers/architecture/routing.mjs +1469 -0
  134. package/ai/skills/stacks-technical-diagrams/renderers/class/README.md +129 -0
  135. package/ai/skills/stacks-technical-diagrams/renderers/class/render-class.mjs +1095 -0
  136. package/ai/skills/stacks-technical-diagrams/renderers/dataflow/README.md +89 -17
  137. package/ai/skills/stacks-technical-diagrams/renderers/dataflow/dataflow-compiler.mjs +1605 -0
  138. package/ai/skills/stacks-technical-diagrams/renderers/dataflow/joint-label-routing.mjs +165 -0
  139. package/ai/skills/stacks-technical-diagrams/renderers/dataflow/label-capacity.mjs +81 -0
  140. package/ai/skills/stacks-technical-diagrams/renderers/dataflow/render-dataflow.mjs +14 -474
  141. package/ai/skills/stacks-technical-diagrams/renderers/erd/README.md +246 -0
  142. package/ai/skills/stacks-technical-diagrams/renderers/erd/grid.mjs +150 -0
  143. package/ai/skills/stacks-technical-diagrams/renderers/erd/render-erd.mjs +1588 -0
  144. package/ai/skills/stacks-technical-diagrams/renderers/explainer/render-explainer.mjs +42 -0
  145. package/ai/skills/stacks-technical-diagrams/renderers/lifecycle/README.md +83 -94
  146. package/ai/skills/stacks-technical-diagrams/renderers/lifecycle/render-lifecycle.mjs +990 -417
  147. package/ai/skills/stacks-technical-diagrams/renderers/sequence/README.md +49 -17
  148. package/ai/skills/stacks-technical-diagrams/renderers/sequence/render-sequence.mjs +735 -94
  149. package/ai/skills/stacks-technical-diagrams/renderers/shared/atomic-output.mjs +1982 -0
  150. package/ai/skills/stacks-technical-diagrams/renderers/shared/automatic-labels.mjs +290 -0
  151. package/ai/skills/stacks-technical-diagrams/renderers/shared/brand-marks.mjs +169 -26
  152. package/ai/skills/stacks-technical-diagrams/renderers/shared/cli.mjs +266 -54
  153. package/ai/skills/stacks-technical-diagrams/renderers/shared/desktop-readability.mjs +106 -0
  154. package/ai/skills/stacks-technical-diagrams/renderers/shared/diagnostics.mjs +0 -0
  155. package/ai/skills/stacks-technical-diagrams/renderers/shared/generated-validators.mjs +268 -2
  156. package/ai/skills/stacks-technical-diagrams/renderers/shared/geometry.mjs +774 -124
  157. package/ai/skills/stacks-technical-diagrams/renderers/shared/i18n.mjs +289 -556
  158. package/ai/skills/stacks-technical-diagrams/renderers/shared/layout-report.mjs +18 -1
  159. package/ai/skills/stacks-technical-diagrams/renderers/shared/legend.mjs +16 -6
  160. package/ai/skills/stacks-technical-diagrams/renderers/shared/node-card-style.mjs +32 -0
  161. package/ai/skills/stacks-technical-diagrams/renderers/shared/output-path.mjs +329 -229
  162. package/ai/skills/stacks-technical-diagrams/renderers/shared/owned-temp-directory.mjs +82 -0
  163. package/ai/skills/stacks-technical-diagrams/renderers/shared/path-semantics.mjs +1017 -0
  164. package/ai/skills/stacks-technical-diagrams/renderers/shared/portable-path.mjs +408 -0
  165. package/ai/skills/stacks-technical-diagrams/renderers/shared/repository-evidence.mjs +235 -79
  166. package/ai/skills/stacks-technical-diagrams/renderers/shared/repository-location.mjs +17 -4
  167. package/ai/skills/stacks-technical-diagrams/renderers/shared/route-quality.mjs +670 -0
  168. package/ai/skills/stacks-technical-diagrams/renderers/shared/sidecar-path.mjs +38 -0
  169. package/ai/skills/stacks-technical-diagrams/renderers/shared/software-notices.mjs +6 -0
  170. package/ai/skills/stacks-technical-diagrams/renderers/shared/spatial-grid.mjs +66 -0
  171. package/ai/skills/stacks-technical-diagrams/renderers/shared/svg-path-analysis.mjs +135 -0
  172. package/ai/skills/stacks-technical-diagrams/renderers/shared/text-fit.mjs +59 -3
  173. package/ai/skills/stacks-technical-diagrams/renderers/shared/utils.mjs +63 -35
  174. package/ai/skills/stacks-technical-diagrams/renderers/shared/validator.mjs +2 -5
  175. package/ai/skills/stacks-technical-diagrams/renderers/timeline/README.md +76 -0
  176. package/ai/skills/stacks-technical-diagrams/renderers/timeline/render-timeline.mjs +576 -0
  177. package/ai/skills/stacks-technical-diagrams/renderers/tree/README.md +96 -0
  178. package/ai/skills/stacks-technical-diagrams/renderers/tree/render-tree.mjs +573 -0
  179. package/ai/skills/stacks-technical-diagrams/renderers/waterfall/README.md +73 -0
  180. package/ai/skills/stacks-technical-diagrams/renderers/waterfall/render-waterfall.mjs +413 -0
  181. package/ai/skills/stacks-technical-diagrams/renderers/workflow/README.md +137 -23
  182. package/ai/skills/stacks-technical-diagrams/renderers/workflow/render-workflow.mjs +18 -6
  183. package/ai/skills/stacks-technical-diagrams/renderers/workflow/workflow-compiler.mjs +2230 -710
  184. package/ai/skills/stacks-technical-diagrams/renderers/workflow/workflow-migration-geometry.mjs +17 -0
  185. package/ai/skills/stacks-technical-diagrams/renderers/workflow/workflow-placement-repair.mjs +94 -0
  186. package/ai/skills/stacks-technical-diagrams/schemas/README.md +87 -40
  187. package/ai/skills/stacks-technical-diagrams/schemas/architecture.schema.json +7 -33
  188. package/ai/skills/stacks-technical-diagrams/schemas/class.schema.json +345 -0
  189. package/ai/skills/stacks-technical-diagrams/schemas/common.schema.json +68 -2
  190. package/ai/skills/stacks-technical-diagrams/schemas/dataflow.schema.json +14 -3
  191. package/ai/skills/stacks-technical-diagrams/schemas/erd.schema.json +381 -0
  192. package/ai/skills/stacks-technical-diagrams/schemas/explainer.schema.json +927 -0
  193. package/ai/skills/stacks-technical-diagrams/schemas/lifecycle.schema.json +23 -110
  194. package/ai/skills/stacks-technical-diagrams/schemas/sequence.schema.json +15 -4
  195. package/ai/skills/stacks-technical-diagrams/schemas/timeline.schema.json +160 -0
  196. package/ai/skills/stacks-technical-diagrams/schemas/tree.schema.json +141 -0
  197. package/ai/skills/stacks-technical-diagrams/schemas/waterfall.schema.json +147 -0
  198. package/ai/skills/stacks-technical-diagrams/schemas/workflow.schema.json +14 -3
  199. package/ai/skills/stacks-technical-diagrams/scripts/check-render-output.mjs +876 -243
  200. package/ai/skills/stacks-technical-diagrams/scripts/check-update.mjs +1717 -0
  201. package/ai/skills/stacks-technical-diagrams/scripts/delivery-update-child.mjs +26 -0
  202. package/ai/skills/stacks-technical-diagrams/scripts/generate-brand-marks.mjs +141 -0
  203. package/ai/skills/stacks-technical-diagrams/scripts/generate-validators.mjs +105 -0
  204. package/ai/skills/stacks-technical-diagrams/scripts/render-examples.mjs +11 -1
  205. package/ai/skills/stacks-technical-diagrams/scripts/update-contract.mjs +187 -0
  206. package/ai/skills/stacks-technical-diagrams/skill-release.json +10 -0
  207. package/ide/vscode/package.json +1 -1
  208. package/package.json +2 -2
@@ -0,0 +1,408 @@
1
+ const PORTABLE_PATH_PROFILES = new Set(['output', 'repo', 'archive']);
2
+ const WINDOWS_SAFE_PROFILES = new Set(['output', 'archive']);
3
+ const WINDOWS_RESERVED_NAME = /^(?:con|prn|aux|nul|conin\$|conout\$|com[1-9¹²³]|lpt[1-9¹²³])(?:\.|$)/iu;
4
+ const WINDOWS_SHORT_NAME = /~[1-9][0-9]*(?:\.|$)/iu;
5
+ const URI_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*:/u;
6
+ const CONTROL_CHARACTER = /\p{Cc}/u;
7
+ const WINDOWS_INVALID_CHARACTER = /[<>"|?*]/u;
8
+ const MAX_SEMANTIC_VARIANTS = 32;
9
+
10
+ export class PortablePathError extends Error {
11
+ constructor(message, {
12
+ code,
13
+ reason,
14
+ value,
15
+ profile,
16
+ segment,
17
+ segmentIndex,
18
+ character,
19
+ index,
20
+ conflictIndex,
21
+ conflictValue,
22
+ collisionKind,
23
+ semantics,
24
+ pathPart,
25
+ conflictPathPart,
26
+ utf8Bytes,
27
+ utf16CodeUnits,
28
+ limit,
29
+ } = {}) {
30
+ super(message);
31
+ this.name = 'PortablePathError';
32
+ this.code = code;
33
+ this.reason = reason;
34
+ this.value = value;
35
+ this.profile = profile;
36
+ if (segment !== undefined) this.segment = segment;
37
+ if (segmentIndex !== undefined) this.segmentIndex = segmentIndex;
38
+ if (character !== undefined) this.character = character;
39
+ if (index !== undefined) this.index = index;
40
+ if (conflictIndex !== undefined) this.conflictIndex = conflictIndex;
41
+ if (conflictValue !== undefined) this.conflictValue = conflictValue;
42
+ if (collisionKind !== undefined) this.collisionKind = collisionKind;
43
+ if (semantics !== undefined) this.semantics = semantics;
44
+ if (pathPart !== undefined) this.pathPart = pathPart;
45
+ if (conflictPathPart !== undefined) this.conflictPathPart = conflictPathPart;
46
+ if (utf8Bytes !== undefined) this.utf8Bytes = utf8Bytes;
47
+ if (utf16CodeUnits !== undefined) this.utf16CodeUnits = utf16CodeUnits;
48
+ if (limit !== undefined) this.limit = limit;
49
+ }
50
+ }
51
+
52
+ function pathError(value, profile, reason, message, details = {}) {
53
+ return new PortablePathError(message, {
54
+ code: `portable-path/${reason}`,
55
+ reason,
56
+ value,
57
+ profile,
58
+ ...details,
59
+ });
60
+ }
61
+
62
+ function assertProfile(profile) {
63
+ if (PORTABLE_PATH_PROFILES.has(profile)) return;
64
+ throw pathError(
65
+ undefined,
66
+ profile,
67
+ 'profile',
68
+ `Portable path profile must be one of: ${[...PORTABLE_PATH_PROFILES].join(', ')}.`,
69
+ );
70
+ }
71
+
72
+ export function validatePortablePath(value, options = {}) {
73
+ const profile = options?.profile;
74
+ assertProfile(profile);
75
+
76
+ if (typeof value !== 'string') {
77
+ throw pathError(value, profile, 'type', 'Portable path must be a string.');
78
+ }
79
+ if (value.length === 0) {
80
+ throw pathError(value, profile, 'empty', 'Portable path must not be empty.');
81
+ }
82
+ if (/^[A-Za-z]:[\\/]/u.test(value) || value.startsWith('/') || value.startsWith('\\')) {
83
+ throw pathError(value, profile, 'absolute', 'Portable path must be relative.');
84
+ }
85
+ if (/^[A-Za-z]:/u.test(value)) {
86
+ throw pathError(
87
+ value,
88
+ profile,
89
+ 'drive-relative',
90
+ 'Portable path must not use a drive-relative Windows path.',
91
+ );
92
+ }
93
+ if (URI_SCHEME.test(value)) {
94
+ throw pathError(value, profile, 'uri', 'Portable path must not be a URI.');
95
+ }
96
+ if (value.includes('\\')) {
97
+ throw pathError(
98
+ value,
99
+ profile,
100
+ 'backslash',
101
+ 'Portable path must use forward slashes as separators.',
102
+ );
103
+ }
104
+
105
+ const segments = value.split('/');
106
+ for (const [segmentIndex, segment] of segments.entries()) {
107
+ const segmentDetails = { segment, segmentIndex };
108
+ if (segment.length === 0) {
109
+ throw pathError(
110
+ value,
111
+ profile,
112
+ 'empty-segment',
113
+ 'Portable path must not contain empty segments.',
114
+ segmentDetails,
115
+ );
116
+ }
117
+ if (segment === '.' || segment === '..') {
118
+ throw pathError(
119
+ value,
120
+ profile,
121
+ 'dot-segment',
122
+ 'Portable path must not contain dot segments.',
123
+ segmentDetails,
124
+ );
125
+ }
126
+ const controlMatch = segment.match(CONTROL_CHARACTER);
127
+ if (controlMatch) {
128
+ throw pathError(
129
+ value,
130
+ profile,
131
+ 'control',
132
+ 'Portable path must not contain control characters.',
133
+ { ...segmentDetails, character: controlMatch[0] },
134
+ );
135
+ }
136
+ const surrogateMatch = segment.match(/[\uD800-\uDFFF]/u);
137
+ if (surrogateMatch) {
138
+ throw pathError(
139
+ value,
140
+ profile,
141
+ 'unpaired-surrogate',
142
+ 'Portable path must not contain unpaired UTF-16 surrogates.',
143
+ { ...segmentDetails, character: surrogateMatch[0] },
144
+ );
145
+ }
146
+
147
+ if (!WINDOWS_SAFE_PROFILES.has(profile)) continue;
148
+ if (segment.includes(':')) {
149
+ throw pathError(
150
+ value,
151
+ profile,
152
+ 'windows-ads',
153
+ 'Portable path must not select a Windows alternate data stream.',
154
+ { ...segmentDetails, character: ':' },
155
+ );
156
+ }
157
+ const invalidMatch = segment.match(WINDOWS_INVALID_CHARACTER);
158
+ if (invalidMatch) {
159
+ throw pathError(
160
+ value,
161
+ profile,
162
+ 'windows-invalid-character',
163
+ 'Portable path contains a character that is invalid in Windows file names.',
164
+ { ...segmentDetails, character: invalidMatch[0] },
165
+ );
166
+ }
167
+ if (/[. ]$/u.test(segment)) {
168
+ throw pathError(
169
+ value,
170
+ profile,
171
+ 'windows-trailing-dot-space',
172
+ 'Portable path segments must not end with a dot or space.',
173
+ segmentDetails,
174
+ );
175
+ }
176
+ if (WINDOWS_RESERVED_NAME.test(segment)) {
177
+ throw pathError(
178
+ value,
179
+ profile,
180
+ 'windows-reserved-name',
181
+ 'Portable path must not use a reserved Windows device name.',
182
+ segmentDetails,
183
+ );
184
+ }
185
+ if (WINDOWS_SHORT_NAME.test(segment)) {
186
+ throw pathError(
187
+ value,
188
+ profile,
189
+ 'windows-short-name',
190
+ 'Portable path must not use a Windows 8.3 short-name shape.',
191
+ segmentDetails,
192
+ );
193
+ }
194
+ const utf8Bytes = Buffer.byteLength(segment, 'utf8');
195
+ const utf16CodeUnits = segment.length;
196
+ if (utf8Bytes > 255 || utf16CodeUnits > 255) {
197
+ throw pathError(
198
+ value,
199
+ profile,
200
+ 'component-too-long',
201
+ 'Portable path segments must fit both UTF-8 and UTF-16 filesystem component limits.',
202
+ {
203
+ ...segmentDetails,
204
+ utf8Bytes,
205
+ utf16CodeUnits,
206
+ limit: 255,
207
+ },
208
+ );
209
+ }
210
+ }
211
+
212
+ return value;
213
+ }
214
+
215
+ function collisionError(values, profile, index, conflictIndex, reason, details = {}) {
216
+ return new PortablePathError(
217
+ `Portable paths at indexes ${conflictIndex} and ${index} collide under portable filesystem semantics.`,
218
+ {
219
+ code: 'portable-path/collision',
220
+ reason,
221
+ value: values[index],
222
+ profile,
223
+ index,
224
+ conflictIndex,
225
+ conflictValue: values[conflictIndex],
226
+ ...details,
227
+ },
228
+ );
229
+ }
230
+
231
+ function createSemanticIndex() {
232
+ return new Map();
233
+ }
234
+
235
+ function semanticEnvelope(value, profile, index) {
236
+ const variants = [value];
237
+ const seen = new Set(variants);
238
+ const transforms = [
239
+ (candidate) => candidate.normalize('NFC'),
240
+ (candidate) => candidate.normalize('NFD'),
241
+ // path-contract-allow: portable-logical-path -- Archive names require conservative case-collision closure.
242
+ (candidate) => candidate.toLocaleLowerCase('en-US'),
243
+ // path-contract-allow: portable-logical-path -- Archive names require conservative case-collision closure.
244
+ (candidate) => candidate.toLocaleUpperCase('en-US'),
245
+ ];
246
+
247
+ for (let cursor = 0; cursor < variants.length; cursor += 1) {
248
+ for (const transform of transforms) {
249
+ const transformed = transform(variants[cursor]);
250
+ if (seen.has(transformed)) continue;
251
+ if (variants.length >= MAX_SEMANTIC_VARIANTS) {
252
+ throw pathError(
253
+ value,
254
+ profile,
255
+ 'semantic-expansion',
256
+ 'Portable path semantic comparison exceeded its bounded Unicode expansion.',
257
+ { index, limit: MAX_SEMANTIC_VARIANTS },
258
+ );
259
+ }
260
+ seen.add(transformed);
261
+ variants.push(transformed);
262
+ }
263
+ }
264
+
265
+ return variants;
266
+ }
267
+
268
+ function classifySemanticCollision(value, conflictValue) {
269
+ if (value === conflictValue) return 'exact';
270
+ if (value.normalize('NFC') === conflictValue.normalize('NFC')) return 'normalization';
271
+
272
+ const directLowerMatch = value.toLocaleLowerCase('en-US')
273
+ === conflictValue.toLocaleLowerCase('en-US');
274
+ const directUpperMatch = value.toLocaleUpperCase('en-US')
275
+ === conflictValue.toLocaleUpperCase('en-US');
276
+ return directLowerMatch || directUpperMatch ? 'case' : 'case-and-normalization';
277
+ }
278
+
279
+ function findSemanticCollision(semanticIndex, value, profile, sourceIndex) {
280
+ const variants = semanticEnvelope(value, profile, sourceIndex);
281
+ for (const variant of variants) {
282
+ const entry = semanticIndex.get(variant);
283
+ if (entry) {
284
+ return {
285
+ entry,
286
+ variants,
287
+ semantics: classifySemanticCollision(value, entry.value),
288
+ };
289
+ }
290
+ }
291
+ return { entry: null, variants, semantics: null };
292
+ }
293
+
294
+ function rememberSemanticEntry(semanticIndex, entry, variants) {
295
+ for (const variant of variants) {
296
+ if (!semanticIndex.has(variant)) semanticIndex.set(variant, entry);
297
+ }
298
+ }
299
+
300
+ export function validatePortablePathSet(values, options = {}) {
301
+ const profile = options?.profile;
302
+ assertProfile(profile);
303
+ if (!Array.isArray(values)) {
304
+ throw pathError(values, profile, 'set-type', 'Portable path set must be an array.');
305
+ }
306
+
307
+ const leafEntries = createSemanticIndex();
308
+ const directoryEntries = createSemanticIndex();
309
+
310
+ for (const [index, value] of values.entries()) {
311
+ try {
312
+ validatePortablePath(value, { profile });
313
+ } catch (error) {
314
+ if (error instanceof PortablePathError && error.index === undefined) error.index = index;
315
+ throw error;
316
+ }
317
+
318
+ const leafCollision = findSemanticCollision(leafEntries, value, profile, index);
319
+ if (leafCollision.entry) {
320
+ const conflictIndex = leafCollision.entry.index;
321
+ throw collisionError(
322
+ values,
323
+ profile,
324
+ index,
325
+ conflictIndex,
326
+ leafCollision.semantics === 'exact' ? 'duplicate' : leafCollision.semantics,
327
+ {
328
+ collisionKind: 'entry',
329
+ semantics: leafCollision.semantics,
330
+ pathPart: value,
331
+ conflictPathPart: leafCollision.entry.value,
332
+ },
333
+ );
334
+ }
335
+
336
+ const segments = value.split('/');
337
+ const prefixes = segments.slice(0, -1).map((_, prefixIndex) => (
338
+ segments.slice(0, prefixIndex + 1).join('/')
339
+ ));
340
+ for (const prefix of prefixes) {
341
+ const directoryCollision = findSemanticCollision(directoryEntries, prefix, profile, index);
342
+ if (directoryCollision.entry && directoryCollision.semantics !== 'exact') {
343
+ throw collisionError(
344
+ values,
345
+ profile,
346
+ index,
347
+ directoryCollision.entry.index,
348
+ `directory-${directoryCollision.semantics}`,
349
+ {
350
+ collisionKind: 'directory-spelling',
351
+ semantics: directoryCollision.semantics,
352
+ pathPart: prefix,
353
+ conflictPathPart: directoryCollision.entry.value,
354
+ },
355
+ );
356
+ }
357
+
358
+ const fileCollision = findSemanticCollision(leafEntries, prefix, profile, index);
359
+ if (fileCollision.entry) {
360
+ throw collisionError(
361
+ values,
362
+ profile,
363
+ index,
364
+ fileCollision.entry.index,
365
+ fileCollision.semantics === 'exact'
366
+ ? 'tree-file'
367
+ : `tree-file-${fileCollision.semantics}`,
368
+ {
369
+ collisionKind: 'tree-file',
370
+ semantics: fileCollision.semantics,
371
+ pathPart: prefix,
372
+ conflictPathPart: fileCollision.entry.value,
373
+ },
374
+ );
375
+ }
376
+ }
377
+
378
+ const directoryCollision = findSemanticCollision(directoryEntries, value, profile, index);
379
+ if (directoryCollision.entry) {
380
+ throw collisionError(
381
+ values,
382
+ profile,
383
+ index,
384
+ directoryCollision.entry.index,
385
+ directoryCollision.semantics === 'exact'
386
+ ? 'tree-file'
387
+ : `tree-file-${directoryCollision.semantics}`,
388
+ {
389
+ collisionKind: 'tree-file',
390
+ semantics: directoryCollision.semantics,
391
+ pathPart: value,
392
+ conflictPathPart: directoryCollision.entry.value,
393
+ },
394
+ );
395
+ }
396
+
397
+ rememberSemanticEntry(leafEntries, { index, value }, leafCollision.variants);
398
+ for (const prefix of prefixes) {
399
+ rememberSemanticEntry(
400
+ directoryEntries,
401
+ { index, value: prefix },
402
+ semanticEnvelope(prefix, profile, index),
403
+ );
404
+ }
405
+ }
406
+
407
+ return values;
408
+ }