@shipfox/client-shell 0.2.0

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 (252) hide show
  1. package/.turbo/turbo-build.log +2 -0
  2. package/.turbo/turbo-type$colon$emit.log +1 -0
  3. package/.turbo/turbo-type.log +1 -0
  4. package/CHANGELOG.md +26 -0
  5. package/LICENSE +21 -0
  6. package/dist/components/main-layout.d.ts +11 -0
  7. package/dist/components/main-layout.d.ts.map +1 -0
  8. package/dist/components/main-layout.js +39 -0
  9. package/dist/components/main-layout.js.map +1 -0
  10. package/dist/components/nav-bar.d.ts +4 -0
  11. package/dist/components/nav-bar.d.ts.map +1 -0
  12. package/dist/components/nav-bar.js +62 -0
  13. package/dist/components/nav-bar.js.map +1 -0
  14. package/dist/components/nav-tabs.d.ts +6 -0
  15. package/dist/components/nav-tabs.d.ts.map +1 -0
  16. package/dist/components/nav-tabs.js +38 -0
  17. package/dist/components/nav-tabs.js.map +1 -0
  18. package/dist/components/not-found-page.d.ts +2 -0
  19. package/dist/components/not-found-page.d.ts.map +1 -0
  20. package/dist/components/not-found-page.js +25 -0
  21. package/dist/components/not-found-page.js.map +1 -0
  22. package/dist/components/settings-nav.d.ts +5 -0
  23. package/dist/components/settings-nav.d.ts.map +1 -0
  24. package/dist/components/settings-nav.js +45 -0
  25. package/dist/components/settings-nav.js.map +1 -0
  26. package/dist/components/user-menu.d.ts +2 -0
  27. package/dist/components/user-menu.d.ts.map +1 -0
  28. package/dist/components/user-menu.js +76 -0
  29. package/dist/components/user-menu.js.map +1 -0
  30. package/dist/components/workspace-crumb.d.ts +7 -0
  31. package/dist/components/workspace-crumb.d.ts.map +1 -0
  32. package/dist/components/workspace-crumb.js +54 -0
  33. package/dist/components/workspace-crumb.js.map +1 -0
  34. package/dist/components/workspace-switcher.d.ts +5 -0
  35. package/dist/components/workspace-switcher.d.ts.map +1 -0
  36. package/dist/components/workspace-switcher.js +90 -0
  37. package/dist/components/workspace-switcher.js.map +1 -0
  38. package/dist/compose/compose-routes.d.ts +6 -0
  39. package/dist/compose/compose-routes.d.ts.map +1 -0
  40. package/dist/compose/compose-routes.js +53 -0
  41. package/dist/compose/compose-routes.js.map +1 -0
  42. package/dist/compose/errors.d.ts +26 -0
  43. package/dist/compose/errors.d.ts.map +1 -0
  44. package/dist/compose/errors.js +39 -0
  45. package/dist/compose/errors.js.map +1 -0
  46. package/dist/compose/merge-config.d.ts +4 -0
  47. package/dist/compose/merge-config.d.ts.map +1 -0
  48. package/dist/compose/merge-config.js +28 -0
  49. package/dist/compose/merge-config.js.map +1 -0
  50. package/dist/compose/normalize-route-path.d.ts +2 -0
  51. package/dist/compose/normalize-route-path.d.ts.map +1 -0
  52. package/dist/compose/normalize-route-path.js +7 -0
  53. package/dist/compose/normalize-route-path.js.map +1 -0
  54. package/dist/compose/validate-providers.d.ts +3 -0
  55. package/dist/compose/validate-providers.d.ts.map +1 -0
  56. package/dist/compose/validate-providers.js +32 -0
  57. package/dist/compose/validate-providers.js.map +1 -0
  58. package/dist/compose/validate-registries.d.ts +4 -0
  59. package/dist/compose/validate-registries.d.ts.map +1 -0
  60. package/dist/compose/validate-registries.js +50 -0
  61. package/dist/compose/validate-registries.js.map +1 -0
  62. package/dist/config/shell-config.d.ts +10 -0
  63. package/dist/config/shell-config.d.ts.map +1 -0
  64. package/dist/config/shell-config.js +12 -0
  65. package/dist/config/shell-config.js.map +1 -0
  66. package/dist/contract.d.ts +39 -0
  67. package/dist/contract.d.ts.map +1 -0
  68. package/dist/contract.js +5 -0
  69. package/dist/contract.js.map +1 -0
  70. package/dist/index.d.ts +2 -0
  71. package/dist/index.d.ts.map +1 -0
  72. package/dist/index.js +3 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/runtime/active-workspace.d.ts +4 -0
  75. package/dist/runtime/active-workspace.d.ts.map +1 -0
  76. package/dist/runtime/active-workspace.js +15 -0
  77. package/dist/runtime/active-workspace.js.map +1 -0
  78. package/dist/runtime/anchor-paths.d.ts +9 -0
  79. package/dist/runtime/anchor-paths.d.ts.map +1 -0
  80. package/dist/runtime/anchor-paths.js +18 -0
  81. package/dist/runtime/anchor-paths.js.map +1 -0
  82. package/dist/runtime/anchors.d.ts +19 -0
  83. package/dist/runtime/anchors.d.ts.map +1 -0
  84. package/dist/runtime/anchors.js +125 -0
  85. package/dist/runtime/anchors.js.map +1 -0
  86. package/dist/runtime/assemble-route-tree.d.ts +10 -0
  87. package/dist/runtime/assemble-route-tree.d.ts.map +1 -0
  88. package/dist/runtime/assemble-route-tree.js +30 -0
  89. package/dist/runtime/assemble-route-tree.js.map +1 -0
  90. package/dist/runtime/auth.d.ts +50 -0
  91. package/dist/runtime/auth.d.ts.map +1 -0
  92. package/dist/runtime/auth.js +194 -0
  93. package/dist/runtime/auth.js.map +1 -0
  94. package/dist/runtime/chrome-context.d.ts +10 -0
  95. package/dist/runtime/chrome-context.d.ts.map +1 -0
  96. package/dist/runtime/chrome-context.js +16 -0
  97. package/dist/runtime/chrome-context.js.map +1 -0
  98. package/dist/runtime/compose-client-app.d.ts +13 -0
  99. package/dist/runtime/compose-client-app.d.ts.map +1 -0
  100. package/dist/runtime/compose-client-app.js +93 -0
  101. package/dist/runtime/compose-client-app.js.map +1 -0
  102. package/dist/runtime/define-route.d.ts +19 -0
  103. package/dist/runtime/define-route.d.ts.map +1 -0
  104. package/dist/runtime/define-route.js +12 -0
  105. package/dist/runtime/define-route.js.map +1 -0
  106. package/dist/runtime/index.d.ts +20 -0
  107. package/dist/runtime/index.d.ts.map +1 -0
  108. package/dist/runtime/index.js +21 -0
  109. package/dist/runtime/index.js.map +1 -0
  110. package/dist/runtime/last-workspace.d.ts +4 -0
  111. package/dist/runtime/last-workspace.d.ts.map +1 -0
  112. package/dist/runtime/last-workspace.js +20 -0
  113. package/dist/runtime/last-workspace.js.map +1 -0
  114. package/dist/runtime/nav-order.d.ts +7 -0
  115. package/dist/runtime/nav-order.d.ts.map +1 -0
  116. package/dist/runtime/nav-order.js +8 -0
  117. package/dist/runtime/nav-order.js.map +1 -0
  118. package/dist/runtime/provider-stack.d.ts +14 -0
  119. package/dist/runtime/provider-stack.d.ts.map +1 -0
  120. package/dist/runtime/provider-stack.js +28 -0
  121. package/dist/runtime/provider-stack.js.map +1 -0
  122. package/dist/runtime/registries.d.ts +4 -0
  123. package/dist/runtime/registries.d.ts.map +1 -0
  124. package/dist/runtime/registries.js +23 -0
  125. package/dist/runtime/registries.js.map +1 -0
  126. package/dist/runtime/router-context.d.ts +9 -0
  127. package/dist/runtime/router-context.d.ts.map +1 -0
  128. package/dist/runtime/router-context.js +3 -0
  129. package/dist/runtime/router-context.js.map +1 -0
  130. package/dist/runtime/workspace-setup.d.ts +18 -0
  131. package/dist/runtime/workspace-setup.d.ts.map +1 -0
  132. package/dist/runtime/workspace-setup.js +64 -0
  133. package/dist/runtime/workspace-setup.js.map +1 -0
  134. package/dist/testing/index.d.ts +17 -0
  135. package/dist/testing/index.d.ts.map +1 -0
  136. package/dist/testing/index.js +35 -0
  137. package/dist/testing/index.js.map +1 -0
  138. package/dist/tsconfig.test.tsbuildinfo +1 -0
  139. package/dist/vite/evaluate-features.d.ts +8 -0
  140. package/dist/vite/evaluate-features.d.ts.map +1 -0
  141. package/dist/vite/evaluate-features.js +80 -0
  142. package/dist/vite/evaluate-features.js.map +1 -0
  143. package/dist/vite/generate.d.ts +9 -0
  144. package/dist/vite/generate.d.ts.map +1 -0
  145. package/dist/vite/generate.js +80 -0
  146. package/dist/vite/generate.js.map +1 -0
  147. package/dist/vite/index.d.ts +2 -0
  148. package/dist/vite/index.d.ts.map +1 -0
  149. package/dist/vite/index.js +3 -0
  150. package/dist/vite/index.js.map +1 -0
  151. package/dist/vite/plugin.d.ts +7 -0
  152. package/dist/vite/plugin.d.ts.map +1 -0
  153. package/dist/vite/plugin.js +77 -0
  154. package/dist/vite/plugin.js.map +1 -0
  155. package/package.json +113 -0
  156. package/src/components/main-layout.tsx +45 -0
  157. package/src/components/nav-bar.tsx +41 -0
  158. package/src/components/nav-tabs.test.tsx +60 -0
  159. package/src/components/nav-tabs.tsx +47 -0
  160. package/src/components/not-found-page.tsx +14 -0
  161. package/src/components/settings-nav.tsx +35 -0
  162. package/src/components/user-menu.tsx +59 -0
  163. package/src/components/workspace-crumb.tsx +43 -0
  164. package/src/components/workspace-switcher.tsx +75 -0
  165. package/src/compose/compose-routes.test.ts +112 -0
  166. package/src/compose/compose-routes.ts +60 -0
  167. package/src/compose/errors.ts +54 -0
  168. package/src/compose/merge-config.test.ts +26 -0
  169. package/src/compose/merge-config.ts +27 -0
  170. package/src/compose/normalize-route-path.ts +6 -0
  171. package/src/compose/validate-providers.test.ts +31 -0
  172. package/src/compose/validate-providers.ts +36 -0
  173. package/src/compose/validate-registries.test.ts +92 -0
  174. package/src/compose/validate-registries.ts +61 -0
  175. package/src/config/config-screen.test.tsx +37 -0
  176. package/src/config/shell-config.ts +10 -0
  177. package/src/contract.ts +47 -0
  178. package/src/index.ts +1 -0
  179. package/src/runtime/active-workspace.ts +13 -0
  180. package/src/runtime/anchor-paths.ts +18 -0
  181. package/src/runtime/anchors.tsx +106 -0
  182. package/src/runtime/assemble-route-tree.ts +42 -0
  183. package/src/runtime/auth.tsx +213 -0
  184. package/src/runtime/chrome-context.tsx +22 -0
  185. package/src/runtime/compose-client-app.tsx +122 -0
  186. package/src/runtime/define-route.test.ts +15 -0
  187. package/src/runtime/define-route.ts +33 -0
  188. package/src/runtime/index.ts +19 -0
  189. package/src/runtime/last-workspace.ts +22 -0
  190. package/src/runtime/nav-order.ts +1 -0
  191. package/src/runtime/provider-stack.tsx +39 -0
  192. package/src/runtime/providers.test.tsx +51 -0
  193. package/src/runtime/registries.test.tsx +100 -0
  194. package/src/runtime/registries.ts +42 -0
  195. package/src/runtime/router-context.ts +9 -0
  196. package/src/runtime/routes.test.tsx +48 -0
  197. package/src/runtime/workspace-setup.tsx +61 -0
  198. package/src/testing/index.test.tsx +57 -0
  199. package/src/testing/index.tsx +51 -0
  200. package/src/vite/evaluate-features.test.ts +34 -0
  201. package/src/vite/evaluate-features.ts +93 -0
  202. package/src/vite/generate.test.ts +22 -0
  203. package/src/vite/generate.ts +99 -0
  204. package/src/vite/generated-router.test.tsx +58 -0
  205. package/src/vite/index.ts +1 -0
  206. package/src/vite/plugin.test.ts +181 -0
  207. package/src/vite/plugin.ts +114 -0
  208. package/src/vite/validate.test.ts +166 -0
  209. package/test/default-route-impl.tsx +3 -0
  210. package/test/external/FINDINGS.md +53 -0
  211. package/test/external/README.md +39 -0
  212. package/test/external/fixture/index.html +12 -0
  213. package/test/external/fixture/package.json +32 -0
  214. package/test/external/fixture/src/app.fixture.tsx +72 -0
  215. package/test/external/fixture/src/features/override-impl.tsx +12 -0
  216. package/test/external/fixture/src/features/override.tsx +20 -0
  217. package/test/external/fixture/src/features.collision.ts +15 -0
  218. package/test/external/fixture/src/features.ts +4 -0
  219. package/test/external/fixture/src/link-typecheck.tsx +10 -0
  220. package/test/external/fixture/src/main.tsx +4 -0
  221. package/test/external/fixture/tsconfig.json +15 -0
  222. package/test/external/fixture/vite.config.ts +10 -0
  223. package/test/external/fixture/vitest.config.ts +15 -0
  224. package/test/external/toy-feature/CHANGELOG.md +15 -0
  225. package/test/external/toy-feature/node_modules/.bin/shipfox-biome-check +43 -0
  226. package/test/external/toy-feature/node_modules/.bin/shipfox-biome-format +43 -0
  227. package/test/external/toy-feature/node_modules/.bin/shipfox-biome-lint +43 -0
  228. package/test/external/toy-feature/node_modules/.bin/shipfox-swc +43 -0
  229. package/test/external/toy-feature/node_modules/.bin/shipfox-tsc-check +43 -0
  230. package/test/external/toy-feature/node_modules/.bin/shipfox-tsc-emit +43 -0
  231. package/test/external/toy-feature/package.json +57 -0
  232. package/test/external/toy-feature/src/config.ts +5 -0
  233. package/test/external/toy-feature/src/index.ts +64 -0
  234. package/test/external/toy-feature/src/provider.tsx +33 -0
  235. package/test/external/toy-feature/src/routes/insights.tsx +7 -0
  236. package/test/external/toy-feature/src/routes/settings.tsx +7 -0
  237. package/test/external/toy-feature/tsconfig.build.json +5 -0
  238. package/test/external/toy-feature/tsconfig.json +1 -0
  239. package/test/external/verify.mjs +318 -0
  240. package/test/fixtures/features.ts +56 -0
  241. package/test/named-route-impl.tsx +3 -0
  242. package/test/not-route-impl.ts +1 -0
  243. package/test/render.tsx +67 -0
  244. package/test/search-route-impl.tsx +7 -0
  245. package/test/setup.ts +3 -0
  246. package/test/typecheck/shipfox-app.gen.ts +85 -0
  247. package/test/typecheck/types.tsx +18 -0
  248. package/tsconfig.build.json +10 -0
  249. package/tsconfig.build.tsbuildinfo +1 -0
  250. package/tsconfig.json +1 -0
  251. package/tsconfig.test.json +12 -0
  252. package/vitest.config.ts +22 -0
@@ -0,0 +1,43 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+ basedir_win="$basedir"
4
+ exe=""
5
+ msys=""
6
+
7
+ case `uname -a` in
8
+ *CYGWIN*|*MINGW*|*MSYS*)
9
+ if command -v cygpath > /dev/null 2>&1; then
10
+ basedir_win=`cygpath -w "$basedir"`
11
+ fi
12
+ exe=".exe"
13
+ msys="true"
14
+ ;;
15
+ *WSL2*)
16
+ if command -v wslpath > /dev/null 2>&1; then
17
+ basedir_win="$(wslpath -w "$basedir" 2> /dev/null)"
18
+ if [ $? -ne 0 ] || [ -z "$basedir_win" ]; then
19
+ basedir_win="$basedir"
20
+ else
21
+ exe=".exe"
22
+ fi
23
+ fi
24
+ ;;
25
+ esac
26
+
27
+ if [ -z "$NODE_PATH" ]; then
28
+ export NODE_PATH="/Users/noe.charmet/conductor/workspaces/platform-v1/singapore/node_modules/.pnpm/node_modules"
29
+ else
30
+ export NODE_PATH="/Users/noe.charmet/conductor/workspaces/platform-v1/singapore/node_modules/.pnpm/node_modules:$NODE_PATH"
31
+ fi
32
+ if [ -n "$exe" ] && [ -x "$basedir/node.exe" ]; then
33
+ exec "$basedir/node.exe" "$basedir_win/../@shipfox/swc/bin/swc.js" "$@"
34
+ elif [ -x "$basedir/node" ]; then
35
+ exec "$basedir/node" "$basedir/../@shipfox/swc/bin/swc.js" "$@"
36
+ elif command -v node >/dev/null 2>&1; then
37
+ exec node "$basedir/../@shipfox/swc/bin/swc.js" "$@"
38
+ elif [ -n "$exe" ] && command -v node.exe >/dev/null 2>&1; then
39
+ exec node.exe "$basedir_win/../@shipfox/swc/bin/swc.js" "$@"
40
+ else
41
+ exec node "$basedir/../@shipfox/swc/bin/swc.js" "$@"
42
+ fi
43
+ # cmd-shim-target=/Users/noe.charmet/conductor/workspaces/platform-v1/singapore/libs/client/shell/test/external/toy-feature/node_modules/@shipfox/swc/bin/swc.js
@@ -0,0 +1,43 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+ basedir_win="$basedir"
4
+ exe=""
5
+ msys=""
6
+
7
+ case `uname -a` in
8
+ *CYGWIN*|*MINGW*|*MSYS*)
9
+ if command -v cygpath > /dev/null 2>&1; then
10
+ basedir_win=`cygpath -w "$basedir"`
11
+ fi
12
+ exe=".exe"
13
+ msys="true"
14
+ ;;
15
+ *WSL2*)
16
+ if command -v wslpath > /dev/null 2>&1; then
17
+ basedir_win="$(wslpath -w "$basedir" 2> /dev/null)"
18
+ if [ $? -ne 0 ] || [ -z "$basedir_win" ]; then
19
+ basedir_win="$basedir"
20
+ else
21
+ exe=".exe"
22
+ fi
23
+ fi
24
+ ;;
25
+ esac
26
+
27
+ if [ -z "$NODE_PATH" ]; then
28
+ export NODE_PATH="/Users/noe.charmet/conductor/workspaces/platform-v1/singapore/node_modules/.pnpm/node_modules"
29
+ else
30
+ export NODE_PATH="/Users/noe.charmet/conductor/workspaces/platform-v1/singapore/node_modules/.pnpm/node_modules:$NODE_PATH"
31
+ fi
32
+ if [ -n "$exe" ] && [ -x "$basedir/node.exe" ]; then
33
+ exec "$basedir/node.exe" "$basedir_win/../@shipfox/typescript/bin/tsc-check.js" "$@"
34
+ elif [ -x "$basedir/node" ]; then
35
+ exec "$basedir/node" "$basedir/../@shipfox/typescript/bin/tsc-check.js" "$@"
36
+ elif command -v node >/dev/null 2>&1; then
37
+ exec node "$basedir/../@shipfox/typescript/bin/tsc-check.js" "$@"
38
+ elif [ -n "$exe" ] && command -v node.exe >/dev/null 2>&1; then
39
+ exec node.exe "$basedir_win/../@shipfox/typescript/bin/tsc-check.js" "$@"
40
+ else
41
+ exec node "$basedir/../@shipfox/typescript/bin/tsc-check.js" "$@"
42
+ fi
43
+ # cmd-shim-target=/Users/noe.charmet/conductor/workspaces/platform-v1/singapore/libs/client/shell/test/external/toy-feature/node_modules/@shipfox/typescript/bin/tsc-check.js
@@ -0,0 +1,43 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+ basedir_win="$basedir"
4
+ exe=""
5
+ msys=""
6
+
7
+ case `uname -a` in
8
+ *CYGWIN*|*MINGW*|*MSYS*)
9
+ if command -v cygpath > /dev/null 2>&1; then
10
+ basedir_win=`cygpath -w "$basedir"`
11
+ fi
12
+ exe=".exe"
13
+ msys="true"
14
+ ;;
15
+ *WSL2*)
16
+ if command -v wslpath > /dev/null 2>&1; then
17
+ basedir_win="$(wslpath -w "$basedir" 2> /dev/null)"
18
+ if [ $? -ne 0 ] || [ -z "$basedir_win" ]; then
19
+ basedir_win="$basedir"
20
+ else
21
+ exe=".exe"
22
+ fi
23
+ fi
24
+ ;;
25
+ esac
26
+
27
+ if [ -z "$NODE_PATH" ]; then
28
+ export NODE_PATH="/Users/noe.charmet/conductor/workspaces/platform-v1/singapore/node_modules/.pnpm/node_modules"
29
+ else
30
+ export NODE_PATH="/Users/noe.charmet/conductor/workspaces/platform-v1/singapore/node_modules/.pnpm/node_modules:$NODE_PATH"
31
+ fi
32
+ if [ -n "$exe" ] && [ -x "$basedir/node.exe" ]; then
33
+ exec "$basedir/node.exe" "$basedir_win/../@shipfox/typescript/bin/tsc-emit.js" "$@"
34
+ elif [ -x "$basedir/node" ]; then
35
+ exec "$basedir/node" "$basedir/../@shipfox/typescript/bin/tsc-emit.js" "$@"
36
+ elif command -v node >/dev/null 2>&1; then
37
+ exec node "$basedir/../@shipfox/typescript/bin/tsc-emit.js" "$@"
38
+ elif [ -n "$exe" ] && command -v node.exe >/dev/null 2>&1; then
39
+ exec node.exe "$basedir_win/../@shipfox/typescript/bin/tsc-emit.js" "$@"
40
+ else
41
+ exec node "$basedir/../@shipfox/typescript/bin/tsc-emit.js" "$@"
42
+ fi
43
+ # cmd-shim-target=/Users/noe.charmet/conductor/workspaces/platform-v1/singapore/libs/client/shell/test/external/toy-feature/node_modules/@shipfox/typescript/bin/tsc-emit.js
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@shipfox/client-shell-fixture-feature",
3
+ "version": "0.0.3",
4
+ "private": true,
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "development": {
11
+ "types": "./src/index.ts",
12
+ "default": "./src/index.ts"
13
+ },
14
+ "default": {
15
+ "types": "./dist/index.d.ts",
16
+ "default": "./dist/index.js"
17
+ }
18
+ },
19
+ "./routes/*": {
20
+ "development": {
21
+ "types": "./src/routes/*.tsx",
22
+ "default": "./src/routes/*.tsx"
23
+ },
24
+ "default": {
25
+ "types": "./dist/routes/*.d.ts",
26
+ "default": "./dist/routes/*.js"
27
+ }
28
+ }
29
+ },
30
+ "scripts": {
31
+ "build": "shipfox-swc",
32
+ "check": "shipfox-biome-check",
33
+ "check:fix": "shipfox-biome-check --write",
34
+ "type": "shipfox-tsc-check",
35
+ "type:emit": "shipfox-tsc-emit"
36
+ },
37
+ "dependencies": {
38
+ "@shipfox/client-shell": "workspace:*",
39
+ "@swc/helpers": "catalog:",
40
+ "zod": "catalog:"
41
+ },
42
+ "peerDependencies": {
43
+ "@tanstack/react-query": "^5.101.0",
44
+ "@tanstack/react-router": "^1.170.16",
45
+ "react": "^19.0.0"
46
+ },
47
+ "devDependencies": {
48
+ "@shipfox/biome": "workspace:*",
49
+ "@shipfox/swc": "workspace:*",
50
+ "@shipfox/ts-config": "workspace:*",
51
+ "@shipfox/typescript": "workspace:*",
52
+ "@tanstack/react-query": "catalog:",
53
+ "@tanstack/react-router": "catalog:",
54
+ "@types/react": "catalog:",
55
+ "react": "catalog:"
56
+ }
57
+ }
@@ -0,0 +1,5 @@
1
+ import {z} from 'zod';
2
+
3
+ export const toyConfigShape = {
4
+ fixtureGreeting: z.string().min(1).describe('Greeting required by the external fixture feature.'),
5
+ };
@@ -0,0 +1,64 @@
1
+ import {defineClientFeature} from '@shipfox/client-shell';
2
+ import {toyConfigShape} from './config.js';
3
+ import {ToyFeatureProvider} from './provider.js';
4
+
5
+ export {
6
+ ProviderProbe,
7
+ type ProviderProbeEntry,
8
+ ProviderProbeObserver,
9
+ } from './provider.js';
10
+
11
+ export const toyFeature = defineClientFeature({
12
+ id: 'fixture.toy-feature',
13
+ routes: [
14
+ {
15
+ path: '/workspaces/$wid/insights',
16
+ parent: 'workspaceLayout',
17
+ impl: '@shipfox/client-shell-fixture-feature/routes/insights',
18
+ },
19
+ {
20
+ path: '/workspaces/$wid/settings/primary',
21
+ parent: 'workspaceSettings',
22
+ impl: '@shipfox/client-shell-fixture-feature/routes/settings',
23
+ },
24
+ {
25
+ path: '/workspaces/$wid/settings/secondary',
26
+ parent: 'workspaceSettings',
27
+ impl: '@shipfox/client-shell-fixture-feature/routes/settings',
28
+ },
29
+ ],
30
+ providers: [{id: 'fixture-toy-provider', Component: ToyFeatureProvider}],
31
+ navigation: [
32
+ {
33
+ id: 'fixture-insights-first',
34
+ scope: 'workspace',
35
+ label: 'Insights first',
36
+ to: '/workspaces/$wid/insights',
37
+ order: 100,
38
+ },
39
+ {
40
+ id: 'fixture-insights-second',
41
+ scope: 'workspace',
42
+ label: 'Insights second',
43
+ to: '/workspaces/$wid/insights',
44
+ order: 200,
45
+ },
46
+ ],
47
+ settingsSections: [
48
+ {
49
+ id: 'fixture-primary-settings',
50
+ pathSegment: 'primary',
51
+ label: 'Primary settings',
52
+ icon: 'userLine',
53
+ order: 100,
54
+ },
55
+ {
56
+ id: 'fixture-secondary-settings',
57
+ pathSegment: 'secondary',
58
+ label: 'Secondary settings',
59
+ icon: 'settings3Line',
60
+ order: 200,
61
+ },
62
+ ],
63
+ configShape: toyConfigShape,
64
+ });
@@ -0,0 +1,33 @@
1
+ import {type QueryClient, useQueryClient} from '@tanstack/react-query';
2
+ import {createContext, type PropsWithChildren, useContext, useEffect, useMemo} from 'react';
3
+
4
+ export interface ProviderProbeEntry {
5
+ id: string;
6
+ queryClient: QueryClient;
7
+ }
8
+
9
+ const ProviderProbeContext = createContext<readonly ProviderProbeEntry[]>([]);
10
+
11
+ export function ProviderProbe({id, children}: PropsWithChildren<{id: string}>) {
12
+ const parentEntries = useContext(ProviderProbeContext);
13
+ const queryClient = useQueryClient();
14
+ const entries = useMemo(
15
+ () => [...parentEntries, {id, queryClient}],
16
+ [id, parentEntries, queryClient],
17
+ );
18
+ return <ProviderProbeContext value={entries}>{children}</ProviderProbeContext>;
19
+ }
20
+
21
+ export function ProviderProbeObserver({
22
+ onChange,
23
+ }: {
24
+ onChange: (entries: readonly ProviderProbeEntry[]) => void;
25
+ }) {
26
+ const entries = useContext(ProviderProbeContext);
27
+ useEffect(() => onChange(entries), [entries, onChange]);
28
+ return null;
29
+ }
30
+
31
+ export function ToyFeatureProvider({children}: PropsWithChildren) {
32
+ return <ProviderProbe id="toy-feature">{children}</ProviderProbe>;
33
+ }
@@ -0,0 +1,7 @@
1
+ import {defineRoute} from '@shipfox/client-shell/runtime';
2
+
3
+ function ToyInsights() {
4
+ return <h1>Toy insights</h1>;
5
+ }
6
+
7
+ export default defineRoute({component: ToyInsights});
@@ -0,0 +1,7 @@
1
+ import {defineRoute} from '@shipfox/client-shell/runtime';
2
+
3
+ function ToySettings() {
4
+ return <h1>Toy settings</h1>;
5
+ }
6
+
7
+ export default defineRoute({component: ToySettings});
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "@shipfox/ts-config/react",
3
+ "compilerOptions": { "rootDir": "src", "outDir": "dist" },
4
+ "include": ["src"]
5
+ }
@@ -0,0 +1 @@
1
+ { "files": [], "references": [{ "path": "tsconfig.build.json" }] }
@@ -0,0 +1,318 @@
1
+ import {spawn} from 'node:child_process';
2
+ import {globSync} from 'node:fs';
3
+ import {cp, mkdir, mkdtemp, readdir, readFile, realpath, rm, writeFile} from 'node:fs/promises';
4
+ import {tmpdir} from 'node:os';
5
+ import {basename, join, relative, resolve, sep} from 'node:path';
6
+ import {fileURLToPath, pathToFileURL} from 'node:url';
7
+
8
+ const repositoryRoot = resolve(fileURLToPath(new URL('../../../../..', import.meta.url)));
9
+ const externalRoot = fileURLToPath(new URL('.', import.meta.url));
10
+ const fixtureTemplate = join(externalRoot, 'fixture');
11
+ const REGISTRY_SHIPFOX_PACKAGE_PATTERN = /^@shipfox\+[^@]+@\d/u;
12
+ const linkMode = process.argv.includes('--link');
13
+
14
+ if (process.argv.some((argument) => argument.startsWith('--') && argument !== '--link')) {
15
+ throw new Error('Usage: node verify.mjs [--link]');
16
+ }
17
+
18
+ const {
19
+ computePublicationClosure,
20
+ entryPointSupportsRuntimeImport,
21
+ entryPointSupportsTypeResolution,
22
+ listPublicPackageEntryPoints,
23
+ readPublicationClosureConfig,
24
+ readWorkspacePackages,
25
+ validatePublicationState,
26
+ } = await import(
27
+ pathToFileURL(join(repositoryRoot, 'tools/application-release/dist/package-closure.js')).href
28
+ );
29
+ const config = readPublicationClosureConfig(join(repositoryRoot, 'publication-closure.json'));
30
+ const workspacePackages = readWorkspacePackages(repositoryRoot);
31
+ validatePublicationState(workspacePackages, config, repositoryRoot);
32
+ const clientRoots = config.roots.filter((root) => root.startsWith('@shipfox/client-'));
33
+ const closure = computePublicationClosure(workspacePackages, clientRoots);
34
+ const entryPoints = closure.flatMap((name) =>
35
+ listConcreteEntryPoints(name, requiredPackage(workspacePackages, name)),
36
+ );
37
+ const runtimeEntryPoints = entryPoints
38
+ .filter(({target}) => entryPointSupportsRuntimeImport(target))
39
+ .map(({specifier}) => specifier);
40
+ const typeEntryPoints = entryPoints
41
+ .filter(({target}) => entryPointSupportsTypeResolution(target))
42
+ .map(({specifier}) => specifier);
43
+ const temporaryRoot = await mkdtemp(join(tmpdir(), 'shipfox-client-composition-'));
44
+ const fixtureRoot = join(temporaryRoot, 'fixture');
45
+
46
+ try {
47
+ await cp(fixtureTemplate, fixtureRoot, {recursive: true});
48
+ const packageSpecs = linkMode
49
+ ? linkedPackageSpecs(closure, workspacePackages)
50
+ : await packedPackageSpecs(closure, workspacePackages, temporaryRoot);
51
+ await configureFixture(fixtureRoot, packageSpecs);
52
+ await writeTypeFixture(fixtureRoot, typeEntryPoints);
53
+ await run('pnpm', ['install', '--prefer-offline', '--ignore-scripts'], fixtureRoot);
54
+
55
+ if (linkMode) await validateLinkedPackages(fixtureRoot, closure, workspacePackages);
56
+ else {
57
+ await validateInstalledPackages(fixtureRoot, closure, workspacePackages);
58
+ await validateNoRegistryShipfoxPackages(fixtureRoot);
59
+ await validateDefaultPackageResolution(fixtureRoot, runtimeEntryPoints);
60
+ }
61
+
62
+ await run('pnpm', ['exec', 'vite', 'build'], fixtureRoot);
63
+ await run('pnpm', ['exec', 'tsc', '--noEmit'], fixtureRoot);
64
+
65
+ process.stdout.write(
66
+ `Verified ${closure.length} external client runtime packages and ${entryPoints.length} public entry points in ${linkMode ? 'linked' : 'packed-tarball'} mode.\n`,
67
+ );
68
+ } finally {
69
+ await rm(temporaryRoot, {recursive: true, force: true});
70
+ }
71
+
72
+ function linkedPackageSpecs(names, packages) {
73
+ return Object.fromEntries(
74
+ names.map((name) => {
75
+ const workspacePackage = requiredPackage(packages, name);
76
+ return [name, `link:${workspacePackage.directory}`];
77
+ }),
78
+ );
79
+ }
80
+
81
+ async function packedPackageSpecs(names, packages, root) {
82
+ const tarballRoot = join(root, 'tarballs');
83
+ await mkdir(tarballRoot);
84
+ const tarballs = await mapWithConcurrency(names, 4, async (name) => {
85
+ const workspacePackage = requiredPackage(packages, name);
86
+ const tarball = join(tarballRoot, `${safePackageName(name)}.tgz`);
87
+ await run('pnpm', ['pack', '--out', tarball], workspacePackage.directory, {capture: true});
88
+ return [name, `file:${tarball}`];
89
+ });
90
+ return Object.fromEntries(tarballs);
91
+ }
92
+
93
+ async function configureFixture(root, packageSpecs) {
94
+ const manifestPath = join(root, 'package.json');
95
+ const manifest = JSON.parse(await readFile(manifestPath, 'utf8'));
96
+ manifest.dependencies = {...manifest.dependencies, ...packageSpecs};
97
+ await Promise.all([
98
+ writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`),
99
+ writeFile(
100
+ join(root, 'pnpm-workspace.yaml'),
101
+ `packages:\n - .\noverrides:\n${Object.entries(packageSpecs)
102
+ .map(([name, specifier]) => ` ${JSON.stringify(name)}: ${JSON.stringify(specifier)}`)
103
+ .join('\n')}\n`,
104
+ ),
105
+ ]);
106
+ }
107
+
108
+ async function writeTypeFixture(root, entryPoints) {
109
+ await writeFile(
110
+ join(root, 'types.ts'),
111
+ `${entryPoints
112
+ .map(
113
+ (specifier, index) => `import type * as Entry${index} from ${JSON.stringify(specifier)};`,
114
+ )
115
+ .join('\n')}\n\n${entryPoints
116
+ .map((_, index) => `void (0 as unknown as typeof Entry${index});`)
117
+ .join('\n')}\n`,
118
+ );
119
+ }
120
+
121
+ async function validateLinkedPackages(root, names, packages) {
122
+ for (const name of names) {
123
+ const installedRoot = await realpath(join(root, 'node_modules', name));
124
+ const expectedRoot = await realpath(requiredPackage(packages, name).directory);
125
+ if (installedRoot !== expectedRoot) {
126
+ throw new Error(`Linked ${name} resolved to ${installedRoot}; expected ${expectedRoot}`);
127
+ }
128
+ }
129
+ }
130
+
131
+ async function validateInstalledPackages(root, names, packages) {
132
+ const realFixtureRoot = await realpath(root);
133
+ for (const name of names) {
134
+ const installedManifestPath = join(root, 'node_modules', name, 'package.json');
135
+ const installedManifest = JSON.parse(await readFile(installedManifestPath, 'utf8'));
136
+ const expectedManifest = requiredPackage(packages, name).manifest;
137
+ if (installedManifest.version !== expectedManifest.version) {
138
+ throw new Error(
139
+ `Packed ${name} has version ${installedManifest.version}; expected ${expectedManifest.version}`,
140
+ );
141
+ }
142
+ const workspaceRange = findWorkspaceRange(installedManifest);
143
+ if (workspaceRange)
144
+ throw new Error(`Packed ${name} contains a workspace range at ${workspaceRange}`);
145
+ validateDefaultInternalImports(name, installedManifest);
146
+ const installedRoot = await realpath(join(root, 'node_modules', name));
147
+ if (!installedRoot.startsWith(realFixtureRoot)) {
148
+ throw new Error(`Packed ${name} resolved outside the external consumer`);
149
+ }
150
+ }
151
+ }
152
+
153
+ function validateDefaultInternalImports(name, manifest) {
154
+ const internalImports = manifest.imports?.['#*'];
155
+ if (internalImports === undefined) return;
156
+ if (
157
+ !internalImports ||
158
+ typeof internalImports !== 'object' ||
159
+ Array.isArray(internalImports) ||
160
+ internalImports.default !== './dist/*'
161
+ ) {
162
+ throw new Error(`Packed ${name} does not map the default #* condition to ./dist/*`);
163
+ }
164
+ }
165
+
166
+ async function validateDefaultPackageResolution(root, specifiers) {
167
+ const resolution = await run(
168
+ process.execPath,
169
+ [
170
+ '--input-type=module',
171
+ '--eval',
172
+ `const specifiers = ${JSON.stringify(specifiers)};
173
+ const resolved = Object.fromEntries(specifiers.map((specifier) => [specifier, import.meta.resolve(specifier)]));
174
+ process.stdout.write(JSON.stringify(resolved));`,
175
+ ],
176
+ root,
177
+ {capture: true},
178
+ );
179
+ const resolved = JSON.parse(resolution.stdout);
180
+ for (const specifier of specifiers) {
181
+ const resolvedPath = fileURLToPath(resolved[specifier]);
182
+ if (!resolvedPath.split(sep).includes('dist')) {
183
+ throw new Error(`Packed ${specifier} resolved to source instead of dist: ${resolvedPath}`);
184
+ }
185
+ }
186
+ }
187
+
188
+ async function validateNoRegistryShipfoxPackages(root) {
189
+ const virtualStore = await readdir(join(root, 'node_modules/.pnpm'), {withFileTypes: true});
190
+ const registryPackages = virtualStore
191
+ .filter((entry) => entry.isDirectory())
192
+ .map((entry) => entry.name)
193
+ .filter((name) => REGISTRY_SHIPFOX_PACKAGE_PATTERN.test(name));
194
+ if (registryPackages.length) {
195
+ throw new Error(
196
+ `External consumer used registry Shipfox packages: ${registryPackages.join(', ')}`,
197
+ );
198
+ }
199
+ }
200
+
201
+ function requiredPackage(packages, name) {
202
+ const workspacePackage = packages.get(name);
203
+ if (!workspacePackage) throw new Error(`Missing workspace package: ${name}`);
204
+ return workspacePackage;
205
+ }
206
+
207
+ function listConcreteEntryPoints(name, workspacePackage) {
208
+ return listPublicPackageEntryPoints(name, workspacePackage.manifest.exports).flatMap(
209
+ (entryPoint) => {
210
+ if (!entryPoint.specifier.includes('*')) return entryPoint;
211
+ return concretePatternEntryPoints(entryPoint, workspacePackage.directory);
212
+ },
213
+ );
214
+ }
215
+
216
+ function concretePatternEntryPoints(entryPoint, packageDirectory) {
217
+ const runtimeTarget = defaultRuntimeTarget(entryPoint.target);
218
+ if (!runtimeTarget?.includes('*')) {
219
+ throw new Error(
220
+ `Wildcard export ${entryPoint.specifier} must define a default JavaScript target.`,
221
+ );
222
+ }
223
+
224
+ const targetPattern = runtimeTarget.slice(2);
225
+ const globPattern = targetPattern.replace('*', '**/*');
226
+ const capturePattern = new RegExp(
227
+ `^${targetPattern
228
+ .split('*')
229
+ .map((segment) => segment.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&'))
230
+ .join('(.+)')}$`,
231
+ );
232
+ const concreteEntryPoints = globSync(join(packageDirectory, globPattern)).map((path) => {
233
+ const relativePath = relative(packageDirectory, path).split(sep).join('/');
234
+ const match = capturePattern.exec(relativePath);
235
+ if (!match) {
236
+ throw new Error(
237
+ `Wildcard export ${entryPoint.specifier} resolved ${relativePath} outside ${runtimeTarget}.`,
238
+ );
239
+ }
240
+ return {...entryPoint, specifier: entryPoint.specifier.replace('*', match[1])};
241
+ });
242
+
243
+ if (!concreteEntryPoints.length) {
244
+ throw new Error(
245
+ `Wildcard export ${entryPoint.specifier} did not resolve any files matching ${runtimeTarget}.`,
246
+ );
247
+ }
248
+
249
+ return concreteEntryPoints;
250
+ }
251
+
252
+ function defaultRuntimeTarget(target) {
253
+ if (typeof target === 'string') return target.endsWith('.js') ? target : undefined;
254
+ if (!target || typeof target !== 'object' || Array.isArray(target)) return undefined;
255
+ const defaultTarget = target.default;
256
+ return defaultTarget === undefined ? undefined : defaultRuntimeTarget(defaultTarget);
257
+ }
258
+
259
+ function findWorkspaceRange(value, path = 'package.json') {
260
+ if (typeof value === 'string') return value.startsWith('workspace:') ? path : undefined;
261
+ if (!value || typeof value !== 'object') return undefined;
262
+ for (const [key, child] of Object.entries(value)) {
263
+ const found = findWorkspaceRange(child, `${path}.${key}`);
264
+ if (found) return found;
265
+ }
266
+ return undefined;
267
+ }
268
+
269
+ function safePackageName(name) {
270
+ return name.replace('@shipfox/', '').replaceAll('/', '-');
271
+ }
272
+
273
+ async function mapWithConcurrency(values, concurrency, mapper) {
274
+ const results = new Array(values.length);
275
+ let nextIndex = 0;
276
+
277
+ async function worker() {
278
+ while (nextIndex < values.length) {
279
+ const index = nextIndex;
280
+ nextIndex += 1;
281
+ results[index] = await mapper(values[index], index);
282
+ }
283
+ }
284
+
285
+ await Promise.all(Array.from({length: concurrency}, () => worker()));
286
+ return results;
287
+ }
288
+
289
+ function run(command, args, cwd, options = {}) {
290
+ const {allowFailure = false, capture = false, env = {}} = options;
291
+ return new Promise((resolvePromise, reject) => {
292
+ const child = spawn(command, args, {
293
+ cwd,
294
+ env: {...process.env, ...env},
295
+ stdio: capture ? ['ignore', 'pipe', 'pipe'] : 'inherit',
296
+ });
297
+ let stdout = '';
298
+ let stderr = '';
299
+ child.stdout?.on('data', (chunk) => {
300
+ stdout += chunk;
301
+ });
302
+ child.stderr?.on('data', (chunk) => {
303
+ stderr += chunk;
304
+ });
305
+ child.on('error', reject);
306
+ child.on('exit', (code) => {
307
+ const result = {code: code ?? 1, stdout, stderr};
308
+ if (code === 0 || allowFailure) resolvePromise(result);
309
+ else {
310
+ reject(
311
+ new Error(
312
+ `${basename(command)} ${args.join(' ')} exited with code ${code}${capture ? `\n${stdout}\n${stderr}` : ''}`,
313
+ ),
314
+ );
315
+ }
316
+ });
317
+ });
318
+ }