@ankhorage/zora 25.8.16 → 25.8.18

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 (195) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/dist/capabilities/index.js +34 -59
  4. package/dist/capabilities/index.js.map +3 -3
  5. package/dist/features/date-picker/composition/DatePickerContent.d.ts.map +1 -1
  6. package/dist/features/date-picker/composition/DatePickerContent.js +1 -11
  7. package/dist/features/date-picker/composition/DatePickerContent.js.map +1 -1
  8. package/dist/features/date-picker/composition/DatePickerField.d.ts.map +1 -1
  9. package/dist/features/date-picker/composition/DatePickerField.js +2 -17
  10. package/dist/features/date-picker/composition/DatePickerField.js.map +1 -1
  11. package/dist/features/matrix-grid/application/getMatrixGridNextFocusId.js +7 -19
  12. package/dist/features/matrix-grid/application/getMatrixGridNextFocusId.js.map +1 -1
  13. package/dist/features/picker/composition/PickerField.d.ts +14 -0
  14. package/dist/features/picker/composition/PickerField.d.ts.map +1 -0
  15. package/dist/features/picker/composition/PickerField.js +21 -0
  16. package/dist/features/picker/composition/PickerField.js.map +1 -0
  17. package/dist/features/picker/composition/PickerHeader.d.ts +9 -0
  18. package/dist/features/picker/composition/PickerHeader.d.ts.map +1 -0
  19. package/dist/features/picker/composition/PickerHeader.js +14 -0
  20. package/dist/features/picker/composition/PickerHeader.js.map +1 -0
  21. package/dist/features/tabletop/meta.d.ts +46 -46
  22. package/dist/features/tabletop/meta.d.ts.map +1 -1
  23. package/dist/features/tabletop/meta.js +33 -58
  24. package/dist/features/tabletop/meta.js.map +1 -1
  25. package/dist/features/time-picker/composition/TimePickerContent.js +1 -12
  26. package/dist/features/time-picker/composition/TimePickerContent.js.map +1 -1
  27. package/dist/features/time-picker/composition/TimePickerField.d.ts.map +1 -1
  28. package/dist/features/time-picker/composition/TimePickerField.js +2 -17
  29. package/dist/features/time-picker/composition/TimePickerField.js.map +1 -1
  30. package/dist/features/typography/utils/headingThemeRecipeMeta.d.ts +5 -5
  31. package/dist/features/typography/utils/headingThemeRecipeMeta.d.ts.map +1 -1
  32. package/dist/features/typography/utils/headingThemeRecipeMeta.js +2 -19
  33. package/dist/features/typography/utils/headingThemeRecipeMeta.js.map +1 -1
  34. package/dist/features/typography/utils/resolveHeadingRecipe.d.ts.map +1 -1
  35. package/dist/features/typography/utils/resolveHeadingRecipe.js +11 -68
  36. package/dist/features/typography/utils/resolveHeadingRecipe.js.map +1 -1
  37. package/dist/features/typography/utils/resolveHeadingThemeRecipe.d.ts +4 -4
  38. package/dist/features/typography/utils/resolveHeadingThemeRecipe.d.ts.map +1 -1
  39. package/dist/features/typography/utils/resolveHeadingThemeRecipe.js +2 -33
  40. package/dist/features/typography/utils/resolveHeadingThemeRecipe.js.map +1 -1
  41. package/dist/features/typography/utils/resolveTextStyle.d.ts.map +1 -1
  42. package/dist/features/typography/utils/resolveTextStyle.js +11 -76
  43. package/dist/features/typography/utils/resolveTextStyle.js.map +1 -1
  44. package/dist/features/typography/utils/resolveTextThemeRecipe.d.ts +4 -4
  45. package/dist/features/typography/utils/resolveTextThemeRecipe.d.ts.map +1 -1
  46. package/dist/features/typography/utils/resolveTextThemeRecipe.js +2 -33
  47. package/dist/features/typography/utils/resolveTextThemeRecipe.js.map +1 -1
  48. package/dist/features/typography/utils/resolveTypographyStyleTokens.d.ts +21 -0
  49. package/dist/features/typography/utils/resolveTypographyStyleTokens.d.ts.map +1 -0
  50. package/dist/features/typography/utils/resolveTypographyStyleTokens.js +75 -0
  51. package/dist/features/typography/utils/resolveTypographyStyleTokens.js.map +1 -0
  52. package/dist/features/typography/utils/resolveTypographyThemeRecipeFields.d.ts +10 -0
  53. package/dist/features/typography/utils/resolveTypographyThemeRecipeFields.d.ts.map +1 -0
  54. package/dist/features/typography/utils/resolveTypographyThemeRecipeFields.js +19 -0
  55. package/dist/features/typography/utils/resolveTypographyThemeRecipeFields.js.map +1 -0
  56. package/dist/features/typography/utils/textThemeRecipeMeta.d.ts +6 -6
  57. package/dist/features/typography/utils/textThemeRecipeMeta.d.ts.map +1 -1
  58. package/dist/features/typography/utils/textThemeRecipeMeta.js +2 -19
  59. package/dist/features/typography/utils/textThemeRecipeMeta.js.map +1 -1
  60. package/dist/features/typography/utils/typographyThemeRecipeFields.d.ts +30 -0
  61. package/dist/features/typography/utils/typographyThemeRecipeFields.d.ts.map +1 -0
  62. package/dist/features/typography/utils/typographyThemeRecipeFields.js +23 -0
  63. package/dist/features/typography/utils/typographyThemeRecipeFields.js.map +1 -0
  64. package/dist/features/uploader/adapters/inbound/Uploader.d.ts.map +1 -1
  65. package/dist/features/uploader/adapters/inbound/Uploader.js +1 -16
  66. package/dist/features/uploader/adapters/inbound/Uploader.js.map +1 -1
  67. package/dist/features/uploader/application/use-cases/validateUploadAsset.d.ts.map +1 -1
  68. package/dist/features/uploader/application/use-cases/validateUploadAsset.js +1 -16
  69. package/dist/features/uploader/application/use-cases/validateUploadAsset.js.map +1 -1
  70. package/dist/features/web-component-artifact/composition/createWebDesiredStateWithNodeAsync.d.ts.map +1 -1
  71. package/dist/features/web-component-artifact/composition/createWebDesiredStateWithNodeAsync.js +3 -4
  72. package/dist/features/web-component-artifact/composition/createWebDesiredStateWithNodeAsync.js.map +1 -1
  73. package/dist/features/web-component-artifact/composition/readWebComponentPackageMetadataAsync.d.ts +8 -0
  74. package/dist/features/web-component-artifact/composition/readWebComponentPackageMetadataAsync.d.ts.map +1 -0
  75. package/dist/features/web-component-artifact/composition/readWebComponentPackageMetadataAsync.js +10 -0
  76. package/dist/features/web-component-artifact/composition/readWebComponentPackageMetadataAsync.js.map +1 -0
  77. package/dist/features/web-component-artifact/composition/synchronizeDeclaredWebComponentsWithNodeAsync.d.ts.map +1 -1
  78. package/dist/features/web-component-artifact/composition/synchronizeDeclaredWebComponentsWithNodeAsync.js +4 -6
  79. package/dist/features/web-component-artifact/composition/synchronizeDeclaredWebComponentsWithNodeAsync.js.map +1 -1
  80. package/package.json +14 -4
  81. package/src/features/date-picker/composition/DatePickerContent.tsx +1 -22
  82. package/src/features/date-picker/composition/DatePickerField.tsx +2 -31
  83. package/src/features/matrix-grid/application/getMatrixGridNextFocusId.ts +11 -22
  84. package/src/features/picker/composition/PickerField.tsx +61 -0
  85. package/src/features/picker/composition/PickerHeader.tsx +25 -0
  86. package/src/features/tabletop/meta.ts +34 -58
  87. package/src/features/time-picker/composition/TimePickerContent.tsx +1 -23
  88. package/src/features/time-picker/composition/TimePickerField.tsx +2 -31
  89. package/src/features/typography/utils/headingThemeRecipeMeta.ts +2 -19
  90. package/src/features/typography/utils/resolveHeadingRecipe.ts +12 -88
  91. package/src/features/typography/utils/resolveHeadingThemeRecipe.ts +3 -39
  92. package/src/features/typography/utils/resolveTextStyle.ts +10 -98
  93. package/src/features/typography/utils/resolveTextThemeRecipe.ts +3 -39
  94. package/src/features/typography/utils/resolveTypographyStyleTokens.ts +113 -0
  95. package/src/features/typography/utils/resolveTypographyThemeRecipeFields.ts +27 -0
  96. package/src/features/typography/utils/textThemeRecipeMeta.ts +2 -19
  97. package/src/features/typography/utils/typographyThemeRecipeFields.ts +23 -0
  98. package/src/features/uploader/adapters/inbound/Uploader.tsx +1 -20
  99. package/src/features/uploader/application/use-cases/validateUploadAsset.test.ts +2 -2
  100. package/src/features/uploader/application/use-cases/validateUploadAsset.ts +2 -20
  101. package/src/features/web-component-artifact/composition/createWebDesiredStateWithNodeAsync.ts +3 -6
  102. package/src/features/web-component-artifact/composition/readWebComponentPackageMetadataAsync.ts +18 -0
  103. package/src/features/web-component-artifact/composition/synchronizeDeclaredWebComponentsWithNodeAsync.ts +4 -8
  104. package/src/webPackageExports.test.ts +101 -0
  105. package/web-dist/chunks/{Accordion-rqr10gn4.js → Accordion-1495be5z.js} +2 -2
  106. package/web-dist/chunks/{Accordion-m2hcc8g4.js → Accordion-3qphhsb4.js} +1 -1
  107. package/web-dist/chunks/{Accordion-gn8ptzwb.js → Accordion-4x8gdqqs.js} +1 -1
  108. package/web-dist/chunks/{Accordion-9aczb0k2.js → Accordion-58bpmzdj.js} +2 -2
  109. package/web-dist/chunks/{Accordion-2jy53kzk.js → Accordion-69c4pfz2.js} +2 -2
  110. package/web-dist/chunks/{Accordion-rqhh2m5b.js → Accordion-7n93d7fa.js} +1 -1
  111. package/web-dist/chunks/{Accordion-m6mp8aea.js → Accordion-a42brn0p.js} +1 -1
  112. package/web-dist/chunks/{Accordion-g08740h4.js → Accordion-c912kzs0.js} +1 -1
  113. package/web-dist/chunks/{Accordion-tyh8jj9d.js → Accordion-cbbafww1.js} +1 -1
  114. package/web-dist/chunks/{Accordion-11ej93t5.js → Accordion-cjjg81ry.js} +2 -2
  115. package/web-dist/chunks/{Accordion-vkd002gw.js → Accordion-d0bebw9j.js} +3 -3
  116. package/web-dist/chunks/{Accordion-vjcd5kxc.js → Accordion-gntmfcmz.js} +1 -1
  117. package/web-dist/chunks/{Accordion-wqyrrptg.js → Accordion-jyemxzat.js} +1 -1
  118. package/web-dist/chunks/{Accordion-p7d5svfq.js → Accordion-kmwhjw1k.js} +1 -1
  119. package/web-dist/chunks/{Accordion-26prtnm7.js → Accordion-ptxa6trg.js} +1 -1
  120. package/web-dist/chunks/{Accordion-4yq92mxp.js → Accordion-ve81xazy.js} +1 -1
  121. package/web-dist/chunks/Accordion-vnb3vntm.js +79 -0
  122. package/web-dist/chunks/{Accordion-9rnsg9pk.js → Accordion-vqx3exsq.js} +129 -243
  123. package/web-dist/chunks/{Accordion-1hbt9k69.js → Accordion-w95t2pr3.js} +1 -1
  124. package/web-dist/chunks/{Accordion-g2aqhct0.js → Accordion-z5dpzw3g.js} +2 -2
  125. package/web-dist/chunks/{Accordion-a4pz3vjj.js → Accordion-zf1g5422.js} +1 -1
  126. package/web-dist/components/accordion/Accordion.js +2 -2
  127. package/web-dist/components/accordion-item/AccordionItem.js +2 -2
  128. package/web-dist/components/app-bar/AppBar.js +2 -2
  129. package/web-dist/components/app-header/AppHeader.js +2 -2
  130. package/web-dist/components/auth-screen/AuthScreen.js +4 -4
  131. package/web-dist/components/avatar/Avatar.js +2 -2
  132. package/web-dist/components/avatar-group/AvatarGroup.js +2 -2
  133. package/web-dist/components/barcode-scanner-view/BarcodeScannerView.js +4 -4
  134. package/web-dist/components/breadcrumbs/Breadcrumbs.js +1 -1
  135. package/web-dist/components/camera-permission-view/CameraPermissionView.js +4 -4
  136. package/web-dist/components/card/Card.js +3 -3
  137. package/web-dist/components/chat-list-item/ChatListItem.js +3 -3
  138. package/web-dist/components/checkbox/Checkbox.js +2 -2
  139. package/web-dist/components/checkbox-group/CheckboxGroup.js +2 -2
  140. package/web-dist/components/chip/Chip.js +2 -2
  141. package/web-dist/components/chip-group/ChipGroup.js +2 -2
  142. package/web-dist/components/collection-editor/CollectionEditor.js +3 -3
  143. package/web-dist/components/content-rail/ContentRail.js +1 -1
  144. package/web-dist/components/data-table/DataTable.js +7 -7
  145. package/web-dist/components/date-picker/DatePicker.js +12 -49
  146. package/web-dist/components/dialog/Dialog.js +2 -2
  147. package/web-dist/components/empty-state/EmptyState.js +4 -4
  148. package/web-dist/components/explorer/Explorer.js +2 -2
  149. package/web-dist/components/field/Field.js +2 -2
  150. package/web-dist/components/file-explorer/FileExplorer.js +2 -2
  151. package/web-dist/components/flat-list/FlatList.js +3 -3
  152. package/web-dist/components/forgot-password-form/ForgotPasswordForm.js +4 -4
  153. package/web-dist/components/form/Form.js +3 -3
  154. package/web-dist/components/form-actions/FormActions.js +3 -3
  155. package/web-dist/components/form-error/FormError.js +3 -3
  156. package/web-dist/components/heading/Heading.js +1 -1
  157. package/web-dist/components/hero/Hero.js +3 -3
  158. package/web-dist/components/list/List.js +3 -3
  159. package/web-dist/components/list-item/ListItem.js +3 -3
  160. package/web-dist/components/list-section/ListSection.js +3 -3
  161. package/web-dist/components/matrix-grid/MatrixGrid.js +5 -17
  162. package/web-dist/components/media-card/MediaCard.js +3 -3
  163. package/web-dist/components/media-explorer/MediaExplorer.js +2 -2
  164. package/web-dist/components/message-bubble/MessageBubble.js +3 -3
  165. package/web-dist/components/metric-card/MetricCard.js +3 -3
  166. package/web-dist/components/missing-element/MissingElement.js +1 -1
  167. package/web-dist/components/o-auth-provider-button/OAuthProviderButton.js +4 -4
  168. package/web-dist/components/o-auth-provider-list/OAuthProviderList.js +4 -4
  169. package/web-dist/components/otp-form/OtpForm.js +4 -4
  170. package/web-dist/components/pagination/Pagination.js +1 -1
  171. package/web-dist/components/palette-item/PaletteItem.js +3 -3
  172. package/web-dist/components/post-card/PostCard.js +3 -3
  173. package/web-dist/components/product-card/ProductCard.js +3 -3
  174. package/web-dist/components/progress/Progress.js +2 -2
  175. package/web-dist/components/progress-ring/ProgressRing.js +2 -2
  176. package/web-dist/components/radio/Radio.js +2 -2
  177. package/web-dist/components/radio-group/RadioGroup.js +2 -2
  178. package/web-dist/components/reader/Reader.js +4 -4
  179. package/web-dist/components/scan-overlay/ScanOverlay.js +4 -4
  180. package/web-dist/components/screen-section/ScreenSection.js +2 -2
  181. package/web-dist/components/section-header/SectionHeader.js +2 -2
  182. package/web-dist/components/section-list/SectionList.js +3 -3
  183. package/web-dist/components/sign-in-form/SignInForm.js +4 -4
  184. package/web-dist/components/sign-up-form/SignUpForm.js +4 -4
  185. package/web-dist/components/skeleton/Skeleton.js +6 -6
  186. package/web-dist/components/skeleton-card/SkeletonCard.js +6 -6
  187. package/web-dist/components/skeleton-list/SkeletonList.js +6 -6
  188. package/web-dist/components/skeleton-text/SkeletonText.js +6 -6
  189. package/web-dist/components/text/Text.js +1 -1
  190. package/web-dist/components/time-picker/TimePicker.js +12 -51
  191. package/web-dist/components/timeline/Timeline.js +1 -1
  192. package/web-dist/components/toolbar/Toolbar.js +3 -3
  193. package/web-dist/components/tree-item/TreeItem.js +3 -3
  194. package/web-dist/components/uploader/Uploader.js +21 -37
  195. package/web-dist/manifest.json +204 -202
@@ -1,3 +1,4 @@
1
+ import { formatBytes } from '@ankhorage/utility/number';
1
2
  /*** Validates a picked upload asset against MIME, extension, size, and caller rules. */
2
3
  export function validateUploadAsset({ asset, accept, maxSizeBytes, validate, }) {
3
4
  if (!isAccepted(asset.contentType, asset.fileName, accept)) {
@@ -58,20 +59,4 @@ function parseAccept(accept) {
58
59
  .map((token) => token.trim())
59
60
  .filter((token) => token.length > 0);
60
61
  }
61
- /*** Formats a byte count for concise upload validation feedback. */
62
- function formatBytes(value) {
63
- if (!Number.isFinite(value) || value <= 0) {
64
- return '0 B';
65
- }
66
- const units = ['B', 'KB', 'MB', 'GB'];
67
- let size = value;
68
- let unitIndex = 0;
69
- while (size >= 1024 && unitIndex < units.length - 1) {
70
- size /= 1024;
71
- unitIndex += 1;
72
- }
73
- const unit = units.at(unitIndex) ?? 'B';
74
- const rounded = unitIndex === 0 ? Math.round(size) : Math.round(size * 10) / 10;
75
- return `${rounded} ${unit}`;
76
- }
77
62
  //# sourceMappingURL=validateUploadAsset.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"validateUploadAsset.js","sourceRoot":"","sources":["../../../../../src/features/uploader/application/use-cases/validateUploadAsset.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ValidateUploadAssetInput } from '../../../../types/upload';\n\n/*** Validates a picked upload asset against MIME, extension, size, and caller rules. */\nexport function validateUploadAsset({\n asset,\n accept,\n maxSizeBytes,\n validate,\n}: ValidateUploadAssetInput): string | undefined {\n if (!isAccepted(asset.contentType, asset.fileName, accept)) {\n return accept ? `File type not accepted. Expected ${accept}.` : 'File type not accepted.';\n }\n\n if (\n maxSizeBytes !== undefined &&\n Number.isFinite(maxSizeBytes) &&\n maxSizeBytes > 0 &&\n asset.sizeBytes !== undefined &&\n Number.isFinite(asset.sizeBytes) &&\n asset.sizeBytes > maxSizeBytes\n ) {\n return `File is too large (${formatBytes(asset.sizeBytes)}). Max ${formatBytes(maxSizeBytes)}.`;\n }\n\n return validate?.(asset);\n}\n\n/*** Returns whether available file metadata satisfies at least one accept token. */\nfunction isAccepted(\n contentType: string | undefined,\n fileName: string | undefined,\n accept: string | undefined,\n): boolean {\n const tokens = parseAccept(accept);\n if (tokens.length === 0) {\n return true;\n }\n\n let hadSignal = false;\n\n for (const token of tokens) {\n const matches = matchesAcceptToken(token, contentType, fileName);\n if (matches === null) {\n continue;\n }\n\n hadSignal = true;\n if (matches) {\n return true;\n }\n }\n\n return !hadSignal;\n}\n\n/*** Matches one MIME or extension accept token against available file metadata. */\nfunction matchesAcceptToken(\n token: string,\n contentType: string | undefined,\n fileName: string | undefined,\n): boolean | null {\n const normalizedToken = token.toLowerCase();\n const normalizedContentType = contentType?.toLowerCase();\n const normalizedFileName = fileName?.toLowerCase();\n\n if (normalizedToken.startsWith('.')) {\n return normalizedFileName ? normalizedFileName.endsWith(normalizedToken) : null;\n }\n\n if (normalizedToken.endsWith('/*')) {\n if (!normalizedContentType) {\n return null;\n }\n\n return normalizedContentType.startsWith(normalizedToken.slice(0, -1));\n }\n\n return normalizedContentType ? normalizedContentType === normalizedToken : null;\n}\n\n/*** Parses a browser-style accept string into normalized non-empty tokens. */\nfunction parseAccept(accept: string | undefined): readonly string[] {\n if (!accept) {\n return [];\n }\n\n return accept\n .split(',')\n .map((token) => token.trim())\n .filter((token) => token.length > 0);\n}\n\n/*** Formats a byte count for concise upload validation feedback. */\nfunction formatBytes(value: number): string {\n if (!Number.isFinite(value) || value <= 0) {\n return '0 B';\n }\n\n const units = ['B', 'KB', 'MB', 'GB'] as const;\n let size = value;\n let unitIndex = 0;\n\n while (size >= 1024 && unitIndex < units.length - 1) {\n size /= 1024;\n unitIndex += 1;\n }\n\n const unit = units.at(unitIndex) ?? 'B';\n const rounded = unitIndex === 0 ? Math.round(size) : Math.round(size * 10) / 10;\n return `${rounded} ${unit}`;\n}\n"]}
1
+ {"version":3,"file":"validateUploadAsset.js","sourceRoot":"","sources":["../../../../../src/features/uploader/application/use-cases/validateUploadAsset.ts"],"names":[],"mappings":"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","sourcesContent":["import { formatBytes } from '@ankhorage/utility/number';\n\nimport type { ValidateUploadAssetInput } from '../../../../types/upload';\n\n/*** Validates a picked upload asset against MIME, extension, size, and caller rules. */\nexport function validateUploadAsset({\n asset,\n accept,\n maxSizeBytes,\n validate,\n}: ValidateUploadAssetInput): string | undefined {\n if (!isAccepted(asset.contentType, asset.fileName, accept)) {\n return accept ? `File type not accepted. Expected ${accept}.` : 'File type not accepted.';\n }\n\n if (\n maxSizeBytes !== undefined &&\n Number.isFinite(maxSizeBytes) &&\n maxSizeBytes > 0 &&\n asset.sizeBytes !== undefined &&\n Number.isFinite(asset.sizeBytes) &&\n asset.sizeBytes > maxSizeBytes\n ) {\n return `File is too large (${formatBytes(asset.sizeBytes)}). Max ${formatBytes(maxSizeBytes)}.`;\n }\n\n return validate?.(asset);\n}\n\n/*** Returns whether available file metadata satisfies at least one accept token. */\nfunction isAccepted(\n contentType: string | undefined,\n fileName: string | undefined,\n accept: string | undefined,\n): boolean {\n const tokens = parseAccept(accept);\n if (tokens.length === 0) {\n return true;\n }\n\n let hadSignal = false;\n\n for (const token of tokens) {\n const matches = matchesAcceptToken(token, contentType, fileName);\n if (matches === null) {\n continue;\n }\n\n hadSignal = true;\n if (matches) {\n return true;\n }\n }\n\n return !hadSignal;\n}\n\n/*** Matches one MIME or extension accept token against available file metadata. */\nfunction matchesAcceptToken(\n token: string,\n contentType: string | undefined,\n fileName: string | undefined,\n): boolean | null {\n const normalizedToken = token.toLowerCase();\n const normalizedContentType = contentType?.toLowerCase();\n const normalizedFileName = fileName?.toLowerCase();\n\n if (normalizedToken.startsWith('.')) {\n return normalizedFileName ? normalizedFileName.endsWith(normalizedToken) : null;\n }\n\n if (normalizedToken.endsWith('/*')) {\n if (!normalizedContentType) {\n return null;\n }\n\n return normalizedContentType.startsWith(normalizedToken.slice(0, -1));\n }\n\n return normalizedContentType ? normalizedContentType === normalizedToken : null;\n}\n\n/*** Parses a browser-style accept string into normalized non-empty tokens. */\nfunction parseAccept(accept: string | undefined): readonly string[] {\n if (!accept) {\n return [];\n }\n\n return accept\n .split(',')\n .map((token) => token.trim())\n .filter((token) => token.length > 0);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"createWebDesiredStateWithNodeAsync.d.ts","sourceRoot":"","sources":["../../../../src/features/web-component-artifact/composition/createWebDesiredStateWithNodeAsync.ts"],"names":[],"mappings":"AAQA,iFAAiF;AACjF,wBAAsB,kCAAkC,CAAC,KAAK,EAAE;IAC9D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B,4HAkCA"}
1
+ {"version":3,"file":"createWebDesiredStateWithNodeAsync.d.ts","sourceRoot":"","sources":["../../../../src/features/web-component-artifact/composition/createWebDesiredStateWithNodeAsync.ts"],"names":[],"mappings":"AAQA,iFAAiF;AACjF,wBAAsB,kCAAkC,CAAC,KAAK,EAAE;IAC9D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B,4HA+BA"}
@@ -1,15 +1,14 @@
1
1
  import { randomUUID } from 'node:crypto';
2
2
  import { readFile, rename, rm, writeFile } from 'node:fs/promises';
3
3
  import { dirname, join, resolve } from 'node:path';
4
- import { fileURLToPath } from 'node:url';
5
4
  import { readWebDesiredStateWithNodeAsync } from '../adapters/outbound/node/readWebDesiredStateWithNodeAsync';
5
+ import { readWebComponentPackageMetadataAsync } from './readWebComponentPackageMetadataAsync';
6
6
  import { synchronizeWebComponentArtifactsWithNodeAsync } from './synchronizeWebComponentArtifactsWithNodeAsync';
7
7
  /*** Add one requested web component and regenerate the complete declared set. */
8
8
  export async function createWebDesiredStateWithNodeAsync(input) {
9
9
  const desired = await readWebDesiredStateWithNodeAsync(input.projectRoot, true);
10
10
  const components = [...new Set([...desired.components, input.component])].sort();
11
- const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '../../../..');
12
- const packageJson = JSON.parse(await readFile(resolve(packageRoot, 'package.json'), 'utf8'));
11
+ const { packageRoot, packageVersion } = await readWebComponentPackageMetadataAsync();
13
12
  const desiredPath = resolve(input.projectRoot, 'zora.web.json');
14
13
  const previous = await readFile(desiredPath, 'utf8').catch((error) => {
15
14
  if (isMissingFile(error))
@@ -24,7 +23,7 @@ export async function createWebDesiredStateWithNodeAsync(input) {
24
23
  components,
25
24
  outputDirectory: resolve(input.projectRoot, '.ankh', 'zora', 'web'),
26
25
  packageRoot,
27
- packageVersion: packageJson.version,
26
+ packageVersion,
28
27
  });
29
28
  }
30
29
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"createWebDesiredStateWithNodeAsync.js","sourceRoot":"","sources":["../../../../src/features/web-component-artifact/composition/createWebDesiredStateWithNodeAsync.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { readFile, rename, rm, writeFile } from 'node:fs/promises';\nimport { dirname, join, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport { readWebDesiredStateWithNodeAsync } from '../adapters/outbound/node/readWebDesiredStateWithNodeAsync';\nimport { synchronizeWebComponentArtifactsWithNodeAsync } from './synchronizeWebComponentArtifactsWithNodeAsync';\n\n/*** Add one requested web component and regenerate the complete declared set. */\nexport async function createWebDesiredStateWithNodeAsync(input: {\n readonly component: string;\n readonly projectRoot: string;\n}) {\n const desired = await readWebDesiredStateWithNodeAsync(input.projectRoot, true);\n const components = [...new Set([...desired.components, input.component])].sort();\n const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '../../../..');\n const packageJson = JSON.parse(await readFile(resolve(packageRoot, 'package.json'), 'utf8')) as {\n readonly version: string;\n };\n const desiredPath = resolve(input.projectRoot, 'zora.web.json');\n const previous = await readFile(desiredPath, 'utf8').catch((error: unknown) => {\n if (isMissingFile(error)) return undefined;\n throw error;\n });\n const next = `${JSON.stringify({ schemaVersion: 1, components }, null, 2)}\\n`;\n if (next !== previous) await replaceDesiredStateAsync(desiredPath, next);\n try {\n return await synchronizeWebComponentArtifactsWithNodeAsync({\n components,\n outputDirectory: resolve(input.projectRoot, '.ankh', 'zora', 'web'),\n packageRoot,\n packageVersion: packageJson.version,\n });\n } catch (error) {\n if (next !== previous) {\n try {\n if (previous === undefined) await rm(desiredPath);\n else await replaceDesiredStateAsync(desiredPath, previous);\n } catch (rollbackError) {\n throw new AggregateError([error, rollbackError], 'ZORA create and rollback failed.', {\n cause: rollbackError,\n });\n }\n }\n throw error;\n }\n}\n\n/*** Replace the authoritative project declaration only after its complete contents are staged. */\nasync function replaceDesiredStateAsync(path: string, content: string): Promise<void> {\n const temporaryPath = join(dirname(path), `.zora-web-${randomUUID()}.tmp`);\n try {\n await writeFile(temporaryPath, content, 'utf8');\n await rename(temporaryPath, path);\n } finally {\n await rm(temporaryPath, { force: true });\n }\n}\n\n/*** Recognize only absent desired state, not unrelated filesystem failures. */\nfunction isMissingFile(error: unknown): boolean {\n return error instanceof Error && 'code' in error && error.code === 'ENOENT';\n}\n"]}
1
+ {"version":3,"file":"createWebDesiredStateWithNodeAsync.js","sourceRoot":"","sources":["../../../../src/features/web-component-artifact/composition/createWebDesiredStateWithNodeAsync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnD,OAAO,EAAE,gCAAgC,EAAE,MAAM,4DAA4D,CAAC;AAC9G,OAAO,EAAE,oCAAoC,EAAE,MAAM,wCAAwC,CAAC;AAC9F,OAAO,EAAE,6CAA6C,EAAE,MAAM,iDAAiD,CAAC;AAEhH,iFAAiF;AACjF,MAAM,CAAC,KAAK,UAAU,kCAAkC,CAAC,KAGxD;IACC,MAAM,OAAO,GAAG,MAAM,gCAAgC,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IAChF,MAAM,UAAU,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACjF,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,GAAG,MAAM,oCAAoC,EAAE,CAAC;IACrF,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;IAChE,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;QAC5E,IAAI,aAAa,CAAC,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QAC3C,MAAM,KAAK,CAAC;IACd,CAAC,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;IAC9E,IAAI,IAAI,KAAK,QAAQ;QAAE,MAAM,wBAAwB,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IACzE,IAAI,CAAC;QACH,OAAO,MAAM,6CAA6C,CAAC;YACzD,UAAU;YACV,eAAe,EAAE,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC;YACnE,WAAW;YACX,cAAc;SACf,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtB,IAAI,CAAC;gBACH,IAAI,QAAQ,KAAK,SAAS;oBAAE,MAAM,EAAE,CAAC,WAAW,CAAC,CAAC;;oBAC7C,MAAM,wBAAwB,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;YAC7D,CAAC;YAAC,OAAO,aAAa,EAAE,CAAC;gBACvB,MAAM,IAAI,cAAc,CAAC,CAAC,KAAK,EAAE,aAAa,CAAC,EAAE,kCAAkC,EAAE;oBACnF,KAAK,EAAE,aAAa;iBACrB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,kGAAkG;AAClG,KAAK,UAAU,wBAAwB,CAAC,IAAY,EAAE,OAAe;IACnE,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,aAAa,UAAU,EAAE,MAAM,CAAC,CAAC;IAC3E,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,aAAa,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QAChD,MAAM,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,CAAC;AACH,CAAC;AAED,+EAA+E;AAC/E,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC;AAC9E,CAAC","sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { readFile, rename, rm, writeFile } from 'node:fs/promises';\nimport { dirname, join, resolve } from 'node:path';\n\nimport { readWebDesiredStateWithNodeAsync } from '../adapters/outbound/node/readWebDesiredStateWithNodeAsync';\nimport { readWebComponentPackageMetadataAsync } from './readWebComponentPackageMetadataAsync';\nimport { synchronizeWebComponentArtifactsWithNodeAsync } from './synchronizeWebComponentArtifactsWithNodeAsync';\n\n/*** Add one requested web component and regenerate the complete declared set. */\nexport async function createWebDesiredStateWithNodeAsync(input: {\n readonly component: string;\n readonly projectRoot: string;\n}) {\n const desired = await readWebDesiredStateWithNodeAsync(input.projectRoot, true);\n const components = [...new Set([...desired.components, input.component])].sort();\n const { packageRoot, packageVersion } = await readWebComponentPackageMetadataAsync();\n const desiredPath = resolve(input.projectRoot, 'zora.web.json');\n const previous = await readFile(desiredPath, 'utf8').catch((error: unknown) => {\n if (isMissingFile(error)) return undefined;\n throw error;\n });\n const next = `${JSON.stringify({ schemaVersion: 1, components }, null, 2)}\\n`;\n if (next !== previous) await replaceDesiredStateAsync(desiredPath, next);\n try {\n return await synchronizeWebComponentArtifactsWithNodeAsync({\n components,\n outputDirectory: resolve(input.projectRoot, '.ankh', 'zora', 'web'),\n packageRoot,\n packageVersion,\n });\n } catch (error) {\n if (next !== previous) {\n try {\n if (previous === undefined) await rm(desiredPath);\n else await replaceDesiredStateAsync(desiredPath, previous);\n } catch (rollbackError) {\n throw new AggregateError([error, rollbackError], 'ZORA create and rollback failed.', {\n cause: rollbackError,\n });\n }\n }\n throw error;\n }\n}\n\n/*** Replace the authoritative project declaration only after its complete contents are staged. */\nasync function replaceDesiredStateAsync(path: string, content: string): Promise<void> {\n const temporaryPath = join(dirname(path), `.zora-web-${randomUUID()}.tmp`);\n try {\n await writeFile(temporaryPath, content, 'utf8');\n await rename(temporaryPath, path);\n } finally {\n await rm(temporaryPath, { force: true });\n }\n}\n\n/*** Recognize only absent desired state, not unrelated filesystem failures. */\nfunction isMissingFile(error: unknown): boolean {\n return error instanceof Error && 'code' in error && error.code === 'ENOENT';\n}\n"]}
@@ -0,0 +1,8 @@
1
+ /*** Reads the active ZORA package location and version for generated web artifacts. */
2
+ export declare function readWebComponentPackageMetadataAsync(): Promise<WebComponentPackageMetadata>;
3
+ interface WebComponentPackageMetadata {
4
+ readonly packageRoot: string;
5
+ readonly packageVersion: string;
6
+ }
7
+ export {};
8
+ //# sourceMappingURL=readWebComponentPackageMetadataAsync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readWebComponentPackageMetadataAsync.d.ts","sourceRoot":"","sources":["../../../../src/features/web-component-artifact/composition/readWebComponentPackageMetadataAsync.ts"],"names":[],"mappings":"AAIA,uFAAuF;AACvF,wBAAsB,oCAAoC,IAAI,OAAO,CAAC,2BAA2B,CAAC,CAOjG;AAED,UAAU,2BAA2B;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC"}
@@ -0,0 +1,10 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { dirname, resolve } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ /*** Reads the active ZORA package location and version for generated web artifacts. */
5
+ export async function readWebComponentPackageMetadataAsync() {
6
+ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '../../../..');
7
+ const packageJson = JSON.parse(await readFile(resolve(packageRoot, 'package.json'), 'utf8'));
8
+ return { packageRoot, packageVersion: packageJson.version };
9
+ }
10
+ //# sourceMappingURL=readWebComponentPackageMetadataAsync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readWebComponentPackageMetadataAsync.js","sourceRoot":"","sources":["../../../../src/features/web-component-artifact/composition/readWebComponentPackageMetadataAsync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,uFAAuF;AACvF,MAAM,CAAC,KAAK,UAAU,oCAAoC;IACxD,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC;IACpF,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,OAAO,CAAC,WAAW,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAE1F,CAAC;IAEF,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC;AAC9D,CAAC","sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/*** Reads the active ZORA package location and version for generated web artifacts. */\nexport async function readWebComponentPackageMetadataAsync(): Promise<WebComponentPackageMetadata> {\n const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '../../../..');\n const packageJson = JSON.parse(await readFile(resolve(packageRoot, 'package.json'), 'utf8')) as {\n readonly version: string;\n };\n\n return { packageRoot, packageVersion: packageJson.version };\n}\n\ninterface WebComponentPackageMetadata {\n readonly packageRoot: string;\n readonly packageVersion: string;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"synchronizeDeclaredWebComponentsWithNodeAsync.d.ts","sourceRoot":"","sources":["../../../../src/features/web-component-artifact/composition/synchronizeDeclaredWebComponentsWithNodeAsync.ts"],"names":[],"mappings":"AAOA,oFAAoF;AACpF,wBAAsB,6CAA6C,CAAC,WAAW,EAAE,MAAM,4HAYtF"}
1
+ {"version":3,"file":"synchronizeDeclaredWebComponentsWithNodeAsync.d.ts","sourceRoot":"","sources":["../../../../src/features/web-component-artifact/composition/synchronizeDeclaredWebComponentsWithNodeAsync.ts"],"names":[],"mappings":"AAMA,oFAAoF;AACpF,wBAAsB,6CAA6C,CAAC,WAAW,EAAE,MAAM,4HAStF"}
@@ -1,18 +1,16 @@
1
- import { readFile } from 'node:fs/promises';
2
- import { dirname, resolve } from 'node:path';
3
- import { fileURLToPath } from 'node:url';
1
+ import { resolve } from 'node:path';
4
2
  import { readWebDesiredStateWithNodeAsync } from '../adapters/outbound/node/readWebDesiredStateWithNodeAsync';
3
+ import { readWebComponentPackageMetadataAsync } from './readWebComponentPackageMetadataAsync';
5
4
  import { synchronizeWebComponentArtifactsWithNodeAsync } from './synchronizeWebComponentArtifactsWithNodeAsync';
6
5
  /*** Load project desired state and sync it from the active ZORA package release. */
7
6
  export async function synchronizeDeclaredWebComponentsWithNodeAsync(projectRoot) {
8
7
  const desired = await readWebDesiredStateWithNodeAsync(projectRoot, false);
9
- const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '../../../..');
10
- const packageJson = JSON.parse(await readFile(resolve(packageRoot, 'package.json'), 'utf8'));
8
+ const { packageRoot, packageVersion } = await readWebComponentPackageMetadataAsync();
11
9
  return await synchronizeWebComponentArtifactsWithNodeAsync({
12
10
  components: desired.components,
13
11
  outputDirectory: resolve(projectRoot, '.ankh', 'zora', 'web'),
14
12
  packageRoot,
15
- packageVersion: packageJson.version,
13
+ packageVersion,
16
14
  });
17
15
  }
18
16
  //# sourceMappingURL=synchronizeDeclaredWebComponentsWithNodeAsync.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"synchronizeDeclaredWebComponentsWithNodeAsync.js","sourceRoot":"","sources":["../../../../src/features/web-component-artifact/composition/synchronizeDeclaredWebComponentsWithNodeAsync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,gCAAgC,EAAE,MAAM,4DAA4D,CAAC;AAC9G,OAAO,EAAE,6CAA6C,EAAE,MAAM,iDAAiD,CAAC;AAEhH,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,6CAA6C,CAAC,WAAmB;IACrF,MAAM,OAAO,GAAG,MAAM,gCAAgC,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAC3E,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC;IACpF,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,OAAO,CAAC,WAAW,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAE1F,CAAC;IACF,OAAO,MAAM,6CAA6C,CAAC;QACzD,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,eAAe,EAAE,OAAO,CAAC,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC;QAC7D,WAAW;QACX,cAAc,EAAE,WAAW,CAAC,OAAO;KACpC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { readFile } from 'node:fs/promises';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport { readWebDesiredStateWithNodeAsync } from '../adapters/outbound/node/readWebDesiredStateWithNodeAsync';\nimport { synchronizeWebComponentArtifactsWithNodeAsync } from './synchronizeWebComponentArtifactsWithNodeAsync';\n\n/*** Load project desired state and sync it from the active ZORA package release. */\nexport async function synchronizeDeclaredWebComponentsWithNodeAsync(projectRoot: string) {\n const desired = await readWebDesiredStateWithNodeAsync(projectRoot, false);\n const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '../../../..');\n const packageJson = JSON.parse(await readFile(resolve(packageRoot, 'package.json'), 'utf8')) as {\n readonly version: string;\n };\n return await synchronizeWebComponentArtifactsWithNodeAsync({\n components: desired.components,\n outputDirectory: resolve(projectRoot, '.ankh', 'zora', 'web'),\n packageRoot,\n packageVersion: packageJson.version,\n });\n}\n"]}
1
+ {"version":3,"file":"synchronizeDeclaredWebComponentsWithNodeAsync.js","sourceRoot":"","sources":["../../../../src/features/web-component-artifact/composition/synchronizeDeclaredWebComponentsWithNodeAsync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,gCAAgC,EAAE,MAAM,4DAA4D,CAAC;AAC9G,OAAO,EAAE,oCAAoC,EAAE,MAAM,wCAAwC,CAAC;AAC9F,OAAO,EAAE,6CAA6C,EAAE,MAAM,iDAAiD,CAAC;AAEhH,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,6CAA6C,CAAC,WAAmB;IACrF,MAAM,OAAO,GAAG,MAAM,gCAAgC,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAC3E,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,GAAG,MAAM,oCAAoC,EAAE,CAAC;IACrF,OAAO,MAAM,6CAA6C,CAAC;QACzD,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,eAAe,EAAE,OAAO,CAAC,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC;QAC7D,WAAW;QACX,cAAc;KACf,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { resolve } from 'node:path';\n\nimport { readWebDesiredStateWithNodeAsync } from '../adapters/outbound/node/readWebDesiredStateWithNodeAsync';\nimport { readWebComponentPackageMetadataAsync } from './readWebComponentPackageMetadataAsync';\nimport { synchronizeWebComponentArtifactsWithNodeAsync } from './synchronizeWebComponentArtifactsWithNodeAsync';\n\n/*** Load project desired state and sync it from the active ZORA package release. */\nexport async function synchronizeDeclaredWebComponentsWithNodeAsync(projectRoot: string) {\n const desired = await readWebDesiredStateWithNodeAsync(projectRoot, false);\n const { packageRoot, packageVersion } = await readWebComponentPackageMetadataAsync();\n return await synchronizeWebComponentArtifactsWithNodeAsync({\n components: desired.components,\n outputDirectory: resolve(projectRoot, '.ankh', 'zora', 'web'),\n packageRoot,\n packageVersion,\n });\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ankhorage/zora",
3
3
  "type": "module",
4
- "version": "25.8.16",
4
+ "version": "25.8.18",
5
5
  "description": "Opinionated React Native and React Native Web UI kit built on @ankhorage/surface.",
6
6
  "homepage": "https://github.com/ankhorage/zora#readme",
7
7
  "bugs": {
@@ -50,7 +50,7 @@
50
50
  "@ankhorage/reader": "^0.1.1",
51
51
  "@ankhorage/runtime": "^5.0.3",
52
52
  "@ankhorage/surface": "^9.4.6",
53
- "@ankhorage/utility": "^1.9.85",
53
+ "@ankhorage/utility": "^1.9.86",
54
54
  "chess.js": "^1.4.0",
55
55
  "expo-document-picker": "~57.0.3",
56
56
  "expo-image-picker": "~57.0.20"
@@ -120,6 +120,16 @@
120
120
  "import": "./dist/features/theme/public.js",
121
121
  "default": "./dist/features/theme/public.js"
122
122
  },
123
+ "./web/runtime": {
124
+ "types": "./web-dist/runtime/ZoraProvider.d.ts",
125
+ "import": "./web-dist/runtime/ZoraProvider.js",
126
+ "default": "./web-dist/runtime/ZoraProvider.js"
127
+ },
128
+ "./web/*": {
129
+ "types": "./web-dist/components/*/index.d.ts",
130
+ "import": "./web-dist/components/*/index.js",
131
+ "default": "./web-dist/components/*/index.js"
132
+ },
123
133
  "./package.json": "./package.json",
124
134
  "./cli": {
125
135
  "types": "./dist/cli/index.d.ts",
@@ -149,7 +159,7 @@
149
159
  "version-packages": "ankhorage-changeset version"
150
160
  },
151
161
  "devDependencies": {
152
- "@ankhorage/devtools": "^2.3.18",
162
+ "@ankhorage/devtools": "^2.3.21",
153
163
  "@ankhorage/paradox": "^0.3.5",
154
164
  "@react-native-vector-icons/fontawesome": "^14.0.0",
155
165
  "@react-native-vector-icons/fontawesome5": "^14.0.0",
@@ -163,7 +173,7 @@
163
173
  "@types/react-native-web": "^0.19.3",
164
174
  "cytoscape": "^3.34.4",
165
175
  "cytoscape-elk": "^2.3.0",
166
- "happy-dom": "^21.0.3",
176
+ "happy-dom": "^21.0.4",
167
177
  "react": "19.2.3",
168
178
  "react-dom": "19.2.3",
169
179
  "react-native": "0.86.3",
@@ -3,6 +3,7 @@ import React from 'react';
3
3
  import type { DatePickerProps } from '../../../types/date-picker';
4
4
  import { Button } from '../../button/public';
5
5
  import { View } from '../../layout/public';
6
+ import { PickerHeader } from '../../picker/composition/PickerHeader';
6
7
  import { Text } from '../../typography/public';
7
8
  import { formatLocalDate, parseLocalDate } from '../utils/localDate';
8
9
 
@@ -61,28 +62,6 @@ interface DatePickerContentProps extends Pick<
61
62
  onSelect: (value: Date) => void;
62
63
  }
63
64
 
64
- /*** Renders the shared title block used by picker overlays. */
65
- function PickerHeader({
66
- description,
67
- title,
68
- }: {
69
- description: React.ReactNode | undefined;
70
- title: React.ReactNode;
71
- }) {
72
- return (
73
- <View gap="xxs">
74
- <Text align="center" variant="label" weight="semiBold">
75
- {title}
76
- </Text>
77
- {description ? (
78
- <Text align="center" emphasis="muted" variant="bodySmall">
79
- {description}
80
- </Text>
81
- ) : null}
82
- </View>
83
- );
84
- }
85
-
86
65
  /*** Renders previous and next month controls for the calendar. */
87
66
  function DatePickerMonthNavigation({
88
67
  displayMonth,
@@ -1,9 +1,7 @@
1
- import { Field } from '@ankhorage/surface';
2
1
  import type React from 'react';
3
2
 
4
3
  import type { DatePickerProps } from '../../../types/date-picker';
5
- import { View } from '../../layout/public';
6
- import { Text } from '../../typography/public';
4
+ import { PickerField } from '../../picker/composition/PickerField';
7
5
 
8
6
  /*** Renders the shared DatePicker field frame around a platform-specific trigger. */
9
7
  export function DatePickerField({
@@ -15,18 +13,7 @@ export function DatePickerField({
15
13
  required,
16
14
  testID,
17
15
  }: DatePickerFieldProps) {
18
- return (
19
- <Field
20
- disabled={disabled}
21
- errorText={error}
22
- invalid={Boolean(error)}
23
- label={label ? renderLabel(label, description) : undefined}
24
- required={required}
25
- testID={testID}
26
- >
27
- {children}
28
- </Field>
29
- );
16
+ return <PickerField {...{ children, description, disabled, error, label, required, testID }} />;
30
17
  }
31
18
 
32
19
  interface DatePickerFieldProps extends Pick<
@@ -35,19 +22,3 @@ interface DatePickerFieldProps extends Pick<
35
22
  > {
36
23
  children: React.ReactNode;
37
24
  }
38
-
39
- /*** Renders the picker label and optional supporting description. */
40
- function renderLabel(label: React.ReactNode, description: React.ReactNode | undefined) {
41
- return (
42
- <View gap="xs">
43
- <Text variant="label" weight="semiBold">
44
- {label}
45
- </Text>
46
- {description ? (
47
- <Text emphasis="muted" variant="bodySmall">
48
- {description}
49
- </Text>
50
- ) : null}
51
- </View>
52
- );
53
- }
@@ -64,40 +64,29 @@ function findDirectionalNeighbour(
64
64
  ): MatrixGridSparseIndexEntry | undefined {
65
65
  if (!entries) return undefined;
66
66
  const coordinate = getAxisCoordinate(current, axis);
67
- const insertionIndex = findFirstGreaterOrEqual(entries, coordinate, axis);
67
+ const insertionIndex = findFirstAtOrAfter(entries, coordinate, axis, false);
68
68
  if (direction === -1) return insertionIndex === 0 ? undefined : entries[insertionIndex - 1];
69
- return entries[findFirstGreater(entries, coordinate, axis)];
69
+ return entries[findFirstAtOrAfter(entries, coordinate, axis, true)];
70
70
  }
71
71
 
72
- /*** Locate the first entry strictly after a matrix coordinate in a sorted sparse axis. */
73
- function findFirstGreater(
72
+ /*** Locates the first entry at or after a matrix coordinate in a sorted sparse axis. */
73
+ function findFirstAtOrAfter(
74
74
  entries: readonly MatrixGridSparseIndexEntry[],
75
75
  coordinate: number,
76
76
  axis: 'columnIndex' | 'rowIndex',
77
+ strictlyAfter: boolean,
77
78
  ): number {
78
79
  let lower = 0;
79
80
  let upper = entries.length;
80
81
  while (lower < upper) {
81
82
  const middle = Math.floor((lower + upper) / 2);
82
83
  const entry = entries.at(middle);
83
- if (entry && getAxisCoordinate(entry, axis) <= coordinate) lower = middle + 1;
84
- else upper = middle;
85
- }
86
- return lower;
87
- }
88
-
89
- /*** Locate the first index at or after a matrix coordinate in a sorted sparse axis. */
90
- function findFirstGreaterOrEqual(
91
- entries: readonly MatrixGridSparseIndexEntry[],
92
- coordinate: number,
93
- axis: 'columnIndex' | 'rowIndex',
94
- ): number {
95
- let lower = 0;
96
- let upper = entries.length;
97
- while (lower < upper) {
98
- const middle = Math.floor((lower + upper) / 2);
99
- const entry = entries.at(middle);
100
- if (entry && getAxisCoordinate(entry, axis) < coordinate) lower = middle + 1;
84
+ const entryCoordinate = entry ? getAxisCoordinate(entry, axis) : undefined;
85
+ if (
86
+ entryCoordinate !== undefined &&
87
+ (strictlyAfter ? entryCoordinate <= coordinate : entryCoordinate < coordinate)
88
+ )
89
+ lower = middle + 1;
101
90
  else upper = middle;
102
91
  }
103
92
  return lower;
@@ -0,0 +1,61 @@
1
+ import { Field } from '@ankhorage/surface';
2
+ import type React from 'react';
3
+
4
+ import { View } from '../../layout/public';
5
+ import { Text } from '../../typography/public';
6
+
7
+ /*** Renders the common field frame and label treatment for picker controls. */
8
+ export function PickerField({
9
+ children,
10
+ description,
11
+ disabled,
12
+ error,
13
+ label,
14
+ required,
15
+ testID,
16
+ }: PickerFieldProps) {
17
+ return (
18
+ <Field
19
+ disabled={disabled}
20
+ errorText={error}
21
+ invalid={Boolean(error)}
22
+ label={label ? <PickerFieldLabel description={description} label={label} /> : undefined}
23
+ required={required}
24
+ testID={testID}
25
+ >
26
+ {children}
27
+ </Field>
28
+ );
29
+ }
30
+
31
+ interface PickerFieldProps {
32
+ readonly children: React.ReactNode;
33
+ readonly description: React.ReactNode | undefined;
34
+ readonly disabled: boolean | undefined;
35
+ readonly error: React.ReactNode | undefined;
36
+ readonly label: React.ReactNode | undefined;
37
+ readonly required: boolean | undefined;
38
+ readonly testID: string | undefined;
39
+ }
40
+
41
+ /*** Renders a picker field label and its optional supporting description. */
42
+ function PickerFieldLabel({
43
+ description,
44
+ label,
45
+ }: {
46
+ readonly description: React.ReactNode | undefined;
47
+ readonly label: React.ReactNode;
48
+ }) {
49
+ return (
50
+ <View gap="xs">
51
+ <Text variant="label" weight="semiBold">
52
+ {label}
53
+ </Text>
54
+ {description ? (
55
+ <Text emphasis="muted" variant="bodySmall">
56
+ {description}
57
+ </Text>
58
+ ) : null}
59
+ </View>
60
+ );
61
+ }
@@ -0,0 +1,25 @@
1
+ import type React from 'react';
2
+
3
+ import { View } from '../../layout/public';
4
+ import { Text } from '../../typography/public';
5
+
6
+ /*** Renders the title block shared by picker overlay content. */
7
+ export function PickerHeader({ description, title }: PickerHeaderProps) {
8
+ return (
9
+ <View gap="xxs">
10
+ <Text align="center" variant="label" weight="semiBold">
11
+ {title}
12
+ </Text>
13
+ {description ? (
14
+ <Text align="center" emphasis="muted" variant="bodySmall">
15
+ {description}
16
+ </Text>
17
+ ) : null}
18
+ </View>
19
+ );
20
+ }
21
+
22
+ interface PickerHeaderProps {
23
+ readonly description: React.ReactNode | undefined;
24
+ readonly title: React.ReactNode;
25
+ }
@@ -1,5 +1,37 @@
1
1
  import type { ZoraComponentMeta } from '../../types/authoring';
2
2
 
3
+ const tabletopPresentationProps = {
4
+ shape: {
5
+ type: 'enum',
6
+ category: 'Table',
7
+ label: 'Shape',
8
+ enum: ['circle', 'oval', 'rounded'],
9
+ default: 'oval',
10
+ authoring: { authority: 'instance' },
11
+ },
12
+ cardSize: {
13
+ type: 'enum',
14
+ category: 'Cards',
15
+ label: 'Card size',
16
+ enum: ['small', 'medium', 'large'],
17
+ default: 'small',
18
+ authoring: { authority: 'instance' },
19
+ },
20
+ disabled: {
21
+ type: 'boolean',
22
+ category: 'State',
23
+ label: 'Disabled',
24
+ default: false,
25
+ authoring: { authority: 'instance' },
26
+ },
27
+ accessibilityLabel: {
28
+ type: 'string',
29
+ category: 'Accessibility',
30
+ label: 'Accessibility label',
31
+ authoring: { authority: 'instance' },
32
+ },
33
+ } as const;
34
+
3
35
  export const cardBackMeta = {
4
36
  name: 'CardBack',
5
37
  category: 'component',
@@ -163,14 +195,6 @@ export const tabletopTableMeta = {
163
195
  label: 'Center sublabel',
164
196
  authoring: { authority: 'instance' },
165
197
  },
166
- shape: {
167
- type: 'enum',
168
- category: 'Table',
169
- label: 'Shape',
170
- enum: ['circle', 'oval', 'rounded'],
171
- default: 'oval',
172
- authoring: { authority: 'instance' },
173
- },
174
198
  seatCount: {
175
199
  type: 'enum',
176
200
  category: 'Players',
@@ -178,27 +202,7 @@ export const tabletopTableMeta = {
178
202
  enum: [2, 3, 4, 5, 6, 7, 8, 9, 10],
179
203
  authoring: { authority: 'instance' },
180
204
  },
181
- cardSize: {
182
- type: 'enum',
183
- category: 'Cards',
184
- label: 'Card size',
185
- enum: ['small', 'medium', 'large'],
186
- default: 'small',
187
- authoring: { authority: 'instance' },
188
- },
189
- disabled: {
190
- type: 'boolean',
191
- category: 'State',
192
- label: 'Disabled',
193
- default: false,
194
- authoring: { authority: 'instance' },
195
- },
196
- accessibilityLabel: {
197
- type: 'string',
198
- category: 'Accessibility',
199
- label: 'Accessibility label',
200
- authoring: { authority: 'instance' },
201
- },
205
+ ...tabletopPresentationProps,
202
206
  },
203
207
  } as const satisfies ZoraComponentMeta;
204
208
  export const pokerTrainingTableMeta = {
@@ -245,34 +249,6 @@ export const pokerTrainingTableMeta = {
245
249
  default: 100,
246
250
  authoring: { authority: 'instance' },
247
251
  },
248
- shape: {
249
- type: 'enum',
250
- category: 'Table',
251
- label: 'Shape',
252
- enum: ['circle', 'oval', 'rounded'],
253
- default: 'oval',
254
- authoring: { authority: 'instance' },
255
- },
256
- cardSize: {
257
- type: 'enum',
258
- category: 'Cards',
259
- label: 'Card size',
260
- enum: ['small', 'medium', 'large'],
261
- default: 'small',
262
- authoring: { authority: 'instance' },
263
- },
264
- disabled: {
265
- type: 'boolean',
266
- category: 'State',
267
- label: 'Disabled',
268
- default: false,
269
- authoring: { authority: 'instance' },
270
- },
271
- accessibilityLabel: {
272
- type: 'string',
273
- category: 'Accessibility',
274
- label: 'Accessibility label',
275
- authoring: { authority: 'instance' },
276
- },
252
+ ...tabletopPresentationProps,
277
253
  },
278
254
  } as const satisfies ZoraComponentMeta;
@@ -4,7 +4,7 @@ import { ScrollView, StyleSheet } from 'react-native';
4
4
  import type { TimePickerProps } from '../../../types/time-picker';
5
5
  import { Button } from '../../button/public';
6
6
  import { View } from '../../layout/public';
7
- import { Text } from '../../typography/public';
7
+ import { PickerHeader } from '../../picker/composition/PickerHeader';
8
8
 
9
9
  const MINUTES_PER_DAY = 24 * 60;
10
10
  const DEFAULT_STEP_MINUTES = 30;
@@ -69,28 +69,6 @@ interface TimePickerContentProps extends Pick<
69
69
  onSelect: (value: string) => void;
70
70
  }
71
71
 
72
- /*** Renders the shared title block used by picker overlays. */
73
- function PickerHeader({
74
- description,
75
- title,
76
- }: {
77
- description: React.ReactNode | undefined;
78
- title: React.ReactNode;
79
- }) {
80
- return (
81
- <View gap="xxs">
82
- <Text align="center" variant="label" weight="semiBold">
83
- {title}
84
- </Text>
85
- {description ? (
86
- <Text align="center" emphasis="muted" variant="bodySmall">
87
- {description}
88
- </Text>
89
- ) : null}
90
- </View>
91
- );
92
- }
93
-
94
72
  /*** Renders one accessible selectable time option. */
95
73
  function TimePickerOption({
96
74
  formatTime,