@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.
- package/.turbo/turbo-build.log +2 -0
- package/.turbo/turbo-type$colon$emit.log +1 -0
- package/.turbo/turbo-type.log +1 -0
- package/CHANGELOG.md +26 -0
- package/LICENSE +21 -0
- package/dist/components/main-layout.d.ts +11 -0
- package/dist/components/main-layout.d.ts.map +1 -0
- package/dist/components/main-layout.js +39 -0
- package/dist/components/main-layout.js.map +1 -0
- package/dist/components/nav-bar.d.ts +4 -0
- package/dist/components/nav-bar.d.ts.map +1 -0
- package/dist/components/nav-bar.js +62 -0
- package/dist/components/nav-bar.js.map +1 -0
- package/dist/components/nav-tabs.d.ts +6 -0
- package/dist/components/nav-tabs.d.ts.map +1 -0
- package/dist/components/nav-tabs.js +38 -0
- package/dist/components/nav-tabs.js.map +1 -0
- package/dist/components/not-found-page.d.ts +2 -0
- package/dist/components/not-found-page.d.ts.map +1 -0
- package/dist/components/not-found-page.js +25 -0
- package/dist/components/not-found-page.js.map +1 -0
- package/dist/components/settings-nav.d.ts +5 -0
- package/dist/components/settings-nav.d.ts.map +1 -0
- package/dist/components/settings-nav.js +45 -0
- package/dist/components/settings-nav.js.map +1 -0
- package/dist/components/user-menu.d.ts +2 -0
- package/dist/components/user-menu.d.ts.map +1 -0
- package/dist/components/user-menu.js +76 -0
- package/dist/components/user-menu.js.map +1 -0
- package/dist/components/workspace-crumb.d.ts +7 -0
- package/dist/components/workspace-crumb.d.ts.map +1 -0
- package/dist/components/workspace-crumb.js +54 -0
- package/dist/components/workspace-crumb.js.map +1 -0
- package/dist/components/workspace-switcher.d.ts +5 -0
- package/dist/components/workspace-switcher.d.ts.map +1 -0
- package/dist/components/workspace-switcher.js +90 -0
- package/dist/components/workspace-switcher.js.map +1 -0
- package/dist/compose/compose-routes.d.ts +6 -0
- package/dist/compose/compose-routes.d.ts.map +1 -0
- package/dist/compose/compose-routes.js +53 -0
- package/dist/compose/compose-routes.js.map +1 -0
- package/dist/compose/errors.d.ts +26 -0
- package/dist/compose/errors.d.ts.map +1 -0
- package/dist/compose/errors.js +39 -0
- package/dist/compose/errors.js.map +1 -0
- package/dist/compose/merge-config.d.ts +4 -0
- package/dist/compose/merge-config.d.ts.map +1 -0
- package/dist/compose/merge-config.js +28 -0
- package/dist/compose/merge-config.js.map +1 -0
- package/dist/compose/normalize-route-path.d.ts +2 -0
- package/dist/compose/normalize-route-path.d.ts.map +1 -0
- package/dist/compose/normalize-route-path.js +7 -0
- package/dist/compose/normalize-route-path.js.map +1 -0
- package/dist/compose/validate-providers.d.ts +3 -0
- package/dist/compose/validate-providers.d.ts.map +1 -0
- package/dist/compose/validate-providers.js +32 -0
- package/dist/compose/validate-providers.js.map +1 -0
- package/dist/compose/validate-registries.d.ts +4 -0
- package/dist/compose/validate-registries.d.ts.map +1 -0
- package/dist/compose/validate-registries.js +50 -0
- package/dist/compose/validate-registries.js.map +1 -0
- package/dist/config/shell-config.d.ts +10 -0
- package/dist/config/shell-config.d.ts.map +1 -0
- package/dist/config/shell-config.js +12 -0
- package/dist/config/shell-config.js.map +1 -0
- package/dist/contract.d.ts +39 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +5 -0
- package/dist/contract.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime/active-workspace.d.ts +4 -0
- package/dist/runtime/active-workspace.d.ts.map +1 -0
- package/dist/runtime/active-workspace.js +15 -0
- package/dist/runtime/active-workspace.js.map +1 -0
- package/dist/runtime/anchor-paths.d.ts +9 -0
- package/dist/runtime/anchor-paths.d.ts.map +1 -0
- package/dist/runtime/anchor-paths.js +18 -0
- package/dist/runtime/anchor-paths.js.map +1 -0
- package/dist/runtime/anchors.d.ts +19 -0
- package/dist/runtime/anchors.d.ts.map +1 -0
- package/dist/runtime/anchors.js +125 -0
- package/dist/runtime/anchors.js.map +1 -0
- package/dist/runtime/assemble-route-tree.d.ts +10 -0
- package/dist/runtime/assemble-route-tree.d.ts.map +1 -0
- package/dist/runtime/assemble-route-tree.js +30 -0
- package/dist/runtime/assemble-route-tree.js.map +1 -0
- package/dist/runtime/auth.d.ts +50 -0
- package/dist/runtime/auth.d.ts.map +1 -0
- package/dist/runtime/auth.js +194 -0
- package/dist/runtime/auth.js.map +1 -0
- package/dist/runtime/chrome-context.d.ts +10 -0
- package/dist/runtime/chrome-context.d.ts.map +1 -0
- package/dist/runtime/chrome-context.js +16 -0
- package/dist/runtime/chrome-context.js.map +1 -0
- package/dist/runtime/compose-client-app.d.ts +13 -0
- package/dist/runtime/compose-client-app.d.ts.map +1 -0
- package/dist/runtime/compose-client-app.js +93 -0
- package/dist/runtime/compose-client-app.js.map +1 -0
- package/dist/runtime/define-route.d.ts +19 -0
- package/dist/runtime/define-route.d.ts.map +1 -0
- package/dist/runtime/define-route.js +12 -0
- package/dist/runtime/define-route.js.map +1 -0
- package/dist/runtime/index.d.ts +20 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +21 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/last-workspace.d.ts +4 -0
- package/dist/runtime/last-workspace.d.ts.map +1 -0
- package/dist/runtime/last-workspace.js +20 -0
- package/dist/runtime/last-workspace.js.map +1 -0
- package/dist/runtime/nav-order.d.ts +7 -0
- package/dist/runtime/nav-order.d.ts.map +1 -0
- package/dist/runtime/nav-order.js +8 -0
- package/dist/runtime/nav-order.js.map +1 -0
- package/dist/runtime/provider-stack.d.ts +14 -0
- package/dist/runtime/provider-stack.d.ts.map +1 -0
- package/dist/runtime/provider-stack.js +28 -0
- package/dist/runtime/provider-stack.js.map +1 -0
- package/dist/runtime/registries.d.ts +4 -0
- package/dist/runtime/registries.d.ts.map +1 -0
- package/dist/runtime/registries.js +23 -0
- package/dist/runtime/registries.js.map +1 -0
- package/dist/runtime/router-context.d.ts +9 -0
- package/dist/runtime/router-context.d.ts.map +1 -0
- package/dist/runtime/router-context.js +3 -0
- package/dist/runtime/router-context.js.map +1 -0
- package/dist/runtime/workspace-setup.d.ts +18 -0
- package/dist/runtime/workspace-setup.d.ts.map +1 -0
- package/dist/runtime/workspace-setup.js +64 -0
- package/dist/runtime/workspace-setup.js.map +1 -0
- package/dist/testing/index.d.ts +17 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +35 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -0
- package/dist/vite/evaluate-features.d.ts +8 -0
- package/dist/vite/evaluate-features.d.ts.map +1 -0
- package/dist/vite/evaluate-features.js +80 -0
- package/dist/vite/evaluate-features.js.map +1 -0
- package/dist/vite/generate.d.ts +9 -0
- package/dist/vite/generate.d.ts.map +1 -0
- package/dist/vite/generate.js +80 -0
- package/dist/vite/generate.js.map +1 -0
- package/dist/vite/index.d.ts +2 -0
- package/dist/vite/index.d.ts.map +1 -0
- package/dist/vite/index.js +3 -0
- package/dist/vite/index.js.map +1 -0
- package/dist/vite/plugin.d.ts +7 -0
- package/dist/vite/plugin.d.ts.map +1 -0
- package/dist/vite/plugin.js +77 -0
- package/dist/vite/plugin.js.map +1 -0
- package/package.json +113 -0
- package/src/components/main-layout.tsx +45 -0
- package/src/components/nav-bar.tsx +41 -0
- package/src/components/nav-tabs.test.tsx +60 -0
- package/src/components/nav-tabs.tsx +47 -0
- package/src/components/not-found-page.tsx +14 -0
- package/src/components/settings-nav.tsx +35 -0
- package/src/components/user-menu.tsx +59 -0
- package/src/components/workspace-crumb.tsx +43 -0
- package/src/components/workspace-switcher.tsx +75 -0
- package/src/compose/compose-routes.test.ts +112 -0
- package/src/compose/compose-routes.ts +60 -0
- package/src/compose/errors.ts +54 -0
- package/src/compose/merge-config.test.ts +26 -0
- package/src/compose/merge-config.ts +27 -0
- package/src/compose/normalize-route-path.ts +6 -0
- package/src/compose/validate-providers.test.ts +31 -0
- package/src/compose/validate-providers.ts +36 -0
- package/src/compose/validate-registries.test.ts +92 -0
- package/src/compose/validate-registries.ts +61 -0
- package/src/config/config-screen.test.tsx +37 -0
- package/src/config/shell-config.ts +10 -0
- package/src/contract.ts +47 -0
- package/src/index.ts +1 -0
- package/src/runtime/active-workspace.ts +13 -0
- package/src/runtime/anchor-paths.ts +18 -0
- package/src/runtime/anchors.tsx +106 -0
- package/src/runtime/assemble-route-tree.ts +42 -0
- package/src/runtime/auth.tsx +213 -0
- package/src/runtime/chrome-context.tsx +22 -0
- package/src/runtime/compose-client-app.tsx +122 -0
- package/src/runtime/define-route.test.ts +15 -0
- package/src/runtime/define-route.ts +33 -0
- package/src/runtime/index.ts +19 -0
- package/src/runtime/last-workspace.ts +22 -0
- package/src/runtime/nav-order.ts +1 -0
- package/src/runtime/provider-stack.tsx +39 -0
- package/src/runtime/providers.test.tsx +51 -0
- package/src/runtime/registries.test.tsx +100 -0
- package/src/runtime/registries.ts +42 -0
- package/src/runtime/router-context.ts +9 -0
- package/src/runtime/routes.test.tsx +48 -0
- package/src/runtime/workspace-setup.tsx +61 -0
- package/src/testing/index.test.tsx +57 -0
- package/src/testing/index.tsx +51 -0
- package/src/vite/evaluate-features.test.ts +34 -0
- package/src/vite/evaluate-features.ts +93 -0
- package/src/vite/generate.test.ts +22 -0
- package/src/vite/generate.ts +99 -0
- package/src/vite/generated-router.test.tsx +58 -0
- package/src/vite/index.ts +1 -0
- package/src/vite/plugin.test.ts +181 -0
- package/src/vite/plugin.ts +114 -0
- package/src/vite/validate.test.ts +166 -0
- package/test/default-route-impl.tsx +3 -0
- package/test/external/FINDINGS.md +53 -0
- package/test/external/README.md +39 -0
- package/test/external/fixture/index.html +12 -0
- package/test/external/fixture/package.json +32 -0
- package/test/external/fixture/src/app.fixture.tsx +72 -0
- package/test/external/fixture/src/features/override-impl.tsx +12 -0
- package/test/external/fixture/src/features/override.tsx +20 -0
- package/test/external/fixture/src/features.collision.ts +15 -0
- package/test/external/fixture/src/features.ts +4 -0
- package/test/external/fixture/src/link-typecheck.tsx +10 -0
- package/test/external/fixture/src/main.tsx +4 -0
- package/test/external/fixture/tsconfig.json +15 -0
- package/test/external/fixture/vite.config.ts +10 -0
- package/test/external/fixture/vitest.config.ts +15 -0
- package/test/external/toy-feature/CHANGELOG.md +15 -0
- package/test/external/toy-feature/node_modules/.bin/shipfox-biome-check +43 -0
- package/test/external/toy-feature/node_modules/.bin/shipfox-biome-format +43 -0
- package/test/external/toy-feature/node_modules/.bin/shipfox-biome-lint +43 -0
- package/test/external/toy-feature/node_modules/.bin/shipfox-swc +43 -0
- package/test/external/toy-feature/node_modules/.bin/shipfox-tsc-check +43 -0
- package/test/external/toy-feature/node_modules/.bin/shipfox-tsc-emit +43 -0
- package/test/external/toy-feature/package.json +57 -0
- package/test/external/toy-feature/src/config.ts +5 -0
- package/test/external/toy-feature/src/index.ts +64 -0
- package/test/external/toy-feature/src/provider.tsx +33 -0
- package/test/external/toy-feature/src/routes/insights.tsx +7 -0
- package/test/external/toy-feature/src/routes/settings.tsx +7 -0
- package/test/external/toy-feature/tsconfig.build.json +5 -0
- package/test/external/toy-feature/tsconfig.json +1 -0
- package/test/external/verify.mjs +318 -0
- package/test/fixtures/features.ts +56 -0
- package/test/named-route-impl.tsx +3 -0
- package/test/not-route-impl.ts +1 -0
- package/test/render.tsx +67 -0
- package/test/search-route-impl.tsx +7 -0
- package/test/setup.ts +3 -0
- package/test/typecheck/shipfox-app.gen.ts +85 -0
- package/test/typecheck/types.tsx +18 -0
- package/tsconfig.build.json +10 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +1 -0
- package/tsconfig.test.json +12 -0
- package/vitest.config.ts +22 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator } from '@shipfox/react-ui/command';
|
|
3
|
+
import { Icon } from '@shipfox/react-ui/icon';
|
|
4
|
+
import { useNavigate } from '@tanstack/react-router';
|
|
5
|
+
import { useSetAtom } from 'jotai';
|
|
6
|
+
import { useAuthState } from '#runtime/auth.js';
|
|
7
|
+
import { lastWorkspaceIdAtom } from '#runtime/last-workspace.js';
|
|
8
|
+
export function WorkspaceSwitcher({ activeWorkspaceId, onSelect }) {
|
|
9
|
+
const { workspaces } = useAuthState();
|
|
10
|
+
const navigate = useNavigate();
|
|
11
|
+
const setLastWorkspaceId = useSetAtom(lastWorkspaceIdAtom);
|
|
12
|
+
const selectWorkspace = (wid)=>{
|
|
13
|
+
try {
|
|
14
|
+
setLastWorkspaceId(wid);
|
|
15
|
+
} catch {
|
|
16
|
+
// Local storage is best effort.
|
|
17
|
+
}
|
|
18
|
+
navigate({
|
|
19
|
+
to: '/workspaces/$wid',
|
|
20
|
+
params: {
|
|
21
|
+
wid
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
onSelect?.();
|
|
25
|
+
};
|
|
26
|
+
return /*#__PURE__*/ _jsxs(Command, {
|
|
27
|
+
children: [
|
|
28
|
+
/*#__PURE__*/ _jsx(CommandInput, {
|
|
29
|
+
placeholder: "Search workspaces..."
|
|
30
|
+
}),
|
|
31
|
+
/*#__PURE__*/ _jsxs(CommandList, {
|
|
32
|
+
className: "max-h-none overflow-visible overflow-y-visible p-0",
|
|
33
|
+
children: [
|
|
34
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
35
|
+
className: "max-h-300 overflow-y-auto overflow-x-hidden p-4 scrollbar",
|
|
36
|
+
children: [
|
|
37
|
+
/*#__PURE__*/ _jsx(CommandEmpty, {
|
|
38
|
+
children: "No workspaces found."
|
|
39
|
+
}),
|
|
40
|
+
/*#__PURE__*/ _jsx(CommandGroup, {
|
|
41
|
+
heading: "Workspaces",
|
|
42
|
+
children: workspaces.map((workspace)=>/*#__PURE__*/ _jsxs(CommandItem, {
|
|
43
|
+
value: workspace.id,
|
|
44
|
+
keywords: [
|
|
45
|
+
workspace.name
|
|
46
|
+
],
|
|
47
|
+
onSelect: ()=>selectWorkspace(workspace.id),
|
|
48
|
+
children: [
|
|
49
|
+
/*#__PURE__*/ _jsx(Icon, {
|
|
50
|
+
name: "check",
|
|
51
|
+
className: `size-16 mr-8 ${activeWorkspaceId === workspace.id ? 'opacity-100' : 'opacity-0'}`
|
|
52
|
+
}),
|
|
53
|
+
workspace.name
|
|
54
|
+
]
|
|
55
|
+
}, workspace.id))
|
|
56
|
+
})
|
|
57
|
+
]
|
|
58
|
+
}),
|
|
59
|
+
/*#__PURE__*/ _jsx(CommandSeparator, {
|
|
60
|
+
alwaysRender: true,
|
|
61
|
+
className: "mx-0"
|
|
62
|
+
}),
|
|
63
|
+
/*#__PURE__*/ _jsx(CommandGroup, {
|
|
64
|
+
forceMount: true,
|
|
65
|
+
className: "p-4",
|
|
66
|
+
children: /*#__PURE__*/ _jsxs(CommandItem, {
|
|
67
|
+
value: "__create",
|
|
68
|
+
onSelect: ()=>{
|
|
69
|
+
navigate({
|
|
70
|
+
to: '/setup/workspaces/new'
|
|
71
|
+
});
|
|
72
|
+
onSelect?.();
|
|
73
|
+
},
|
|
74
|
+
forceMount: true,
|
|
75
|
+
children: [
|
|
76
|
+
/*#__PURE__*/ _jsx(Icon, {
|
|
77
|
+
name: "addLine",
|
|
78
|
+
className: "size-16"
|
|
79
|
+
}),
|
|
80
|
+
"Create workspace"
|
|
81
|
+
]
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
]
|
|
85
|
+
})
|
|
86
|
+
]
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
//# sourceMappingURL=workspace-switcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/workspace-switcher.tsx"],"sourcesContent":["import {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n CommandSeparator,\n} from '@shipfox/react-ui/command';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {useNavigate} from '@tanstack/react-router';\nimport {useSetAtom} from 'jotai';\nimport {useAuthState} from '#runtime/auth.js';\nimport {lastWorkspaceIdAtom} from '#runtime/last-workspace.js';\n\nexport function WorkspaceSwitcher({\n activeWorkspaceId,\n onSelect,\n}: {\n activeWorkspaceId?: string;\n onSelect?: () => void;\n}) {\n const {workspaces} = useAuthState();\n const navigate = useNavigate();\n const setLastWorkspaceId = useSetAtom(lastWorkspaceIdAtom);\n const selectWorkspace = (wid: string) => {\n try {\n setLastWorkspaceId(wid);\n } catch {\n // Local storage is best effort.\n }\n navigate({to: '/workspaces/$wid', params: {wid}});\n onSelect?.();\n };\n return (\n <Command>\n <CommandInput placeholder=\"Search workspaces...\" />\n <CommandList className=\"max-h-none overflow-visible overflow-y-visible p-0\">\n <div className=\"max-h-300 overflow-y-auto overflow-x-hidden p-4 scrollbar\">\n <CommandEmpty>No workspaces found.</CommandEmpty>\n <CommandGroup heading=\"Workspaces\">\n {workspaces.map((workspace) => (\n <CommandItem\n key={workspace.id}\n value={workspace.id}\n keywords={[workspace.name]}\n onSelect={() => selectWorkspace(workspace.id)}\n >\n <Icon\n name=\"check\"\n className={`size-16 mr-8 ${activeWorkspaceId === workspace.id ? 'opacity-100' : 'opacity-0'}`}\n />\n {workspace.name}\n </CommandItem>\n ))}\n </CommandGroup>\n </div>\n <CommandSeparator alwaysRender className=\"mx-0\" />\n <CommandGroup forceMount className=\"p-4\">\n <CommandItem\n value=\"__create\"\n onSelect={() => {\n navigate({to: '/setup/workspaces/new' as never});\n onSelect?.();\n }}\n forceMount\n >\n <Icon name=\"addLine\" className=\"size-16\" />\n Create workspace\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n );\n}\n"],"names":["Command","CommandEmpty","CommandGroup","CommandInput","CommandItem","CommandList","CommandSeparator","Icon","useNavigate","useSetAtom","useAuthState","lastWorkspaceIdAtom","WorkspaceSwitcher","activeWorkspaceId","onSelect","workspaces","navigate","setLastWorkspaceId","selectWorkspace","wid","to","params","placeholder","className","div","heading","map","workspace","value","id","keywords","name","alwaysRender","forceMount"],"mappings":";AAAA,SACEA,OAAO,EACPC,YAAY,EACZC,YAAY,EACZC,YAAY,EACZC,WAAW,EACXC,WAAW,EACXC,gBAAgB,QACX,4BAA4B;AACnC,SAAQC,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,WAAW,QAAO,yBAAyB;AACnD,SAAQC,UAAU,QAAO,QAAQ;AACjC,SAAQC,YAAY,QAAO,mBAAmB;AAC9C,SAAQC,mBAAmB,QAAO,6BAA6B;AAE/D,OAAO,SAASC,kBAAkB,EAChCC,iBAAiB,EACjBC,QAAQ,EAIT;IACC,MAAM,EAACC,UAAU,EAAC,GAAGL;IACrB,MAAMM,WAAWR;IACjB,MAAMS,qBAAqBR,WAAWE;IACtC,MAAMO,kBAAkB,CAACC;QACvB,IAAI;YACFF,mBAAmBE;QACrB,EAAE,OAAM;QACN,gCAAgC;QAClC;QACAH,SAAS;YAACI,IAAI;YAAoBC,QAAQ;gBAACF;YAAG;QAAC;QAC/CL;IACF;IACA,qBACE,MAACd;;0BACC,KAACG;gBAAamB,aAAY;;0BAC1B,MAACjB;gBAAYkB,WAAU;;kCACrB,MAACC;wBAAID,WAAU;;0CACb,KAACtB;0CAAa;;0CACd,KAACC;gCAAauB,SAAQ;0CACnBV,WAAWW,GAAG,CAAC,CAACC,0BACf,MAACvB;wCAECwB,OAAOD,UAAUE,EAAE;wCACnBC,UAAU;4CAACH,UAAUI,IAAI;yCAAC;wCAC1BjB,UAAU,IAAMI,gBAAgBS,UAAUE,EAAE;;0DAE5C,KAACtB;gDACCwB,MAAK;gDACLR,WAAW,CAAC,aAAa,EAAEV,sBAAsBc,UAAUE,EAAE,GAAG,gBAAgB,aAAa;;4CAE9FF,UAAUI,IAAI;;uCATVJ,UAAUE,EAAE;;;;kCAczB,KAACvB;wBAAiB0B,YAAY;wBAACT,WAAU;;kCACzC,KAACrB;wBAAa+B,UAAU;wBAACV,WAAU;kCACjC,cAAA,MAACnB;4BACCwB,OAAM;4BACNd,UAAU;gCACRE,SAAS;oCAACI,IAAI;gCAAgC;gCAC9CN;4BACF;4BACAmB,UAAU;;8CAEV,KAAC1B;oCAAKwB,MAAK;oCAAUR,WAAU;;gCAAY;;;;;;;;AAOvD"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ClientFeature, RouteContribution } from '#contract.js';
|
|
2
|
+
export interface ComposedRoute extends RouteContribution {
|
|
3
|
+
featureId: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function composeRoutes(features: readonly ClientFeature[]): ComposedRoute[];
|
|
6
|
+
//# sourceMappingURL=compose-routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compose-routes.d.ts","sourceRoot":"","sources":["../../src/compose/compose-routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,aAAa,EAAE,iBAAiB,EAAC,MAAM,cAAc,CAAC;AAInE,MAAM,WAAW,aAAc,SAAQ,iBAAiB;IACtD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,GAAG,aAAa,EAAE,CAmDjF"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { RouteCompositionError } from './errors.js';
|
|
2
|
+
import { normalizeRoutePath } from './normalize-route-path.js';
|
|
3
|
+
export function composeRoutes(features) {
|
|
4
|
+
const routes = new Map();
|
|
5
|
+
for (const feature of features){
|
|
6
|
+
for (const contribution of feature.routes ?? []){
|
|
7
|
+
const normalizedContribution = {
|
|
8
|
+
...contribution,
|
|
9
|
+
path: normalizeRoutePath(contribution.path)
|
|
10
|
+
};
|
|
11
|
+
const existing = routes.get(normalizedContribution.path);
|
|
12
|
+
if (!existing) {
|
|
13
|
+
if (normalizedContribution.override) {
|
|
14
|
+
throw new RouteCompositionError(normalizedContribution.path, `Route override for "${normalizedContribution.path}" from feature "${feature.id}" has no route to replace.`, [
|
|
15
|
+
feature.id
|
|
16
|
+
]);
|
|
17
|
+
}
|
|
18
|
+
routes.set(normalizedContribution.path, {
|
|
19
|
+
...normalizedContribution,
|
|
20
|
+
featureId: feature.id
|
|
21
|
+
});
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (!normalizedContribution.override) {
|
|
25
|
+
throw new RouteCompositionError(normalizedContribution.path, `Route "${normalizedContribution.path}" is contributed by both features "${existing.featureId}" and "${feature.id}". Set override: true to replace it explicitly.`, [
|
|
26
|
+
existing.featureId,
|
|
27
|
+
feature.id
|
|
28
|
+
]);
|
|
29
|
+
}
|
|
30
|
+
if (existing.override) {
|
|
31
|
+
throw new RouteCompositionError(normalizedContribution.path, `Route "${normalizedContribution.path}" has competing overrides from features "${existing.featureId}" and "${feature.id}".`, [
|
|
32
|
+
existing.featureId,
|
|
33
|
+
feature.id
|
|
34
|
+
]);
|
|
35
|
+
}
|
|
36
|
+
if (existing.parent !== normalizedContribution.parent) {
|
|
37
|
+
throw new RouteCompositionError(normalizedContribution.path, `Route override for "${normalizedContribution.path}" from feature "${feature.id}" cannot change anchor from "${existing.parent}" in feature "${existing.featureId}" to "${normalizedContribution.parent}".`, [
|
|
38
|
+
existing.featureId,
|
|
39
|
+
feature.id
|
|
40
|
+
]);
|
|
41
|
+
}
|
|
42
|
+
routes.set(normalizedContribution.path, {
|
|
43
|
+
...normalizedContribution,
|
|
44
|
+
featureId: feature.id
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return [
|
|
49
|
+
...routes.values()
|
|
50
|
+
];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
//# sourceMappingURL=compose-routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/compose/compose-routes.ts"],"sourcesContent":["import type {ClientFeature, RouteContribution} from '#contract.js';\nimport {RouteCompositionError} from './errors.js';\nimport {normalizeRoutePath} from './normalize-route-path.js';\n\nexport interface ComposedRoute extends RouteContribution {\n featureId: string;\n}\n\nexport function composeRoutes(features: readonly ClientFeature[]): ComposedRoute[] {\n const routes = new Map<string, ComposedRoute>();\n for (const feature of features) {\n for (const contribution of feature.routes ?? []) {\n const normalizedContribution = {\n ...contribution,\n path: normalizeRoutePath(contribution.path),\n };\n const existing = routes.get(normalizedContribution.path);\n if (!existing) {\n if (normalizedContribution.override) {\n throw new RouteCompositionError(\n normalizedContribution.path,\n `Route override for \"${normalizedContribution.path}\" from feature \"${feature.id}\" has no route to replace.`,\n [feature.id],\n );\n }\n routes.set(normalizedContribution.path, {\n ...normalizedContribution,\n featureId: feature.id,\n });\n continue;\n }\n if (!normalizedContribution.override) {\n throw new RouteCompositionError(\n normalizedContribution.path,\n `Route \"${normalizedContribution.path}\" is contributed by both features \"${existing.featureId}\" and \"${feature.id}\". Set override: true to replace it explicitly.`,\n [existing.featureId, feature.id],\n );\n }\n if (existing.override) {\n throw new RouteCompositionError(\n normalizedContribution.path,\n `Route \"${normalizedContribution.path}\" has competing overrides from features \"${existing.featureId}\" and \"${feature.id}\".`,\n [existing.featureId, feature.id],\n );\n }\n if (existing.parent !== normalizedContribution.parent) {\n throw new RouteCompositionError(\n normalizedContribution.path,\n `Route override for \"${normalizedContribution.path}\" from feature \"${feature.id}\" cannot change anchor from \"${existing.parent}\" in feature \"${existing.featureId}\" to \"${normalizedContribution.parent}\".`,\n [existing.featureId, feature.id],\n );\n }\n routes.set(normalizedContribution.path, {\n ...normalizedContribution,\n featureId: feature.id,\n });\n }\n }\n return [...routes.values()];\n}\n"],"names":["RouteCompositionError","normalizeRoutePath","composeRoutes","features","routes","Map","feature","contribution","normalizedContribution","path","existing","get","override","id","set","featureId","parent","values"],"mappings":"AACA,SAAQA,qBAAqB,QAAO,cAAc;AAClD,SAAQC,kBAAkB,QAAO,4BAA4B;AAM7D,OAAO,SAASC,cAAcC,QAAkC;IAC9D,MAAMC,SAAS,IAAIC;IACnB,KAAK,MAAMC,WAAWH,SAAU;QAC9B,KAAK,MAAMI,gBAAgBD,QAAQF,MAAM,IAAI,EAAE,CAAE;YAC/C,MAAMI,yBAAyB;gBAC7B,GAAGD,YAAY;gBACfE,MAAMR,mBAAmBM,aAAaE,IAAI;YAC5C;YACA,MAAMC,WAAWN,OAAOO,GAAG,CAACH,uBAAuBC,IAAI;YACvD,IAAI,CAACC,UAAU;gBACb,IAAIF,uBAAuBI,QAAQ,EAAE;oBACnC,MAAM,IAAIZ,sBACRQ,uBAAuBC,IAAI,EAC3B,CAAC,oBAAoB,EAAED,uBAAuBC,IAAI,CAAC,gBAAgB,EAAEH,QAAQO,EAAE,CAAC,0BAA0B,CAAC,EAC3G;wBAACP,QAAQO,EAAE;qBAAC;gBAEhB;gBACAT,OAAOU,GAAG,CAACN,uBAAuBC,IAAI,EAAE;oBACtC,GAAGD,sBAAsB;oBACzBO,WAAWT,QAAQO,EAAE;gBACvB;gBACA;YACF;YACA,IAAI,CAACL,uBAAuBI,QAAQ,EAAE;gBACpC,MAAM,IAAIZ,sBACRQ,uBAAuBC,IAAI,EAC3B,CAAC,OAAO,EAAED,uBAAuBC,IAAI,CAAC,mCAAmC,EAAEC,SAASK,SAAS,CAAC,OAAO,EAAET,QAAQO,EAAE,CAAC,+CAA+C,CAAC,EAClK;oBAACH,SAASK,SAAS;oBAAET,QAAQO,EAAE;iBAAC;YAEpC;YACA,IAAIH,SAASE,QAAQ,EAAE;gBACrB,MAAM,IAAIZ,sBACRQ,uBAAuBC,IAAI,EAC3B,CAAC,OAAO,EAAED,uBAAuBC,IAAI,CAAC,yCAAyC,EAAEC,SAASK,SAAS,CAAC,OAAO,EAAET,QAAQO,EAAE,CAAC,EAAE,CAAC,EAC3H;oBAACH,SAASK,SAAS;oBAAET,QAAQO,EAAE;iBAAC;YAEpC;YACA,IAAIH,SAASM,MAAM,KAAKR,uBAAuBQ,MAAM,EAAE;gBACrD,MAAM,IAAIhB,sBACRQ,uBAAuBC,IAAI,EAC3B,CAAC,oBAAoB,EAAED,uBAAuBC,IAAI,CAAC,gBAAgB,EAAEH,QAAQO,EAAE,CAAC,6BAA6B,EAAEH,SAASM,MAAM,CAAC,cAAc,EAAEN,SAASK,SAAS,CAAC,MAAM,EAAEP,uBAAuBQ,MAAM,CAAC,EAAE,CAAC,EAC3M;oBAACN,SAASK,SAAS;oBAAET,QAAQO,EAAE;iBAAC;YAEpC;YACAT,OAAOU,GAAG,CAACN,uBAAuBC,IAAI,EAAE;gBACtC,GAAGD,sBAAsB;gBACzBO,WAAWT,QAAQO,EAAE;YACvB;QACF;IACF;IACA,OAAO;WAAIT,OAAOa,MAAM;KAAG;AAC7B"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
declare abstract class CompositionError extends Error {
|
|
2
|
+
readonly featureIds: readonly string[];
|
|
3
|
+
protected constructor(name: string, message: string, featureIds: readonly string[]);
|
|
4
|
+
}
|
|
5
|
+
export declare class RouteCompositionError extends CompositionError {
|
|
6
|
+
readonly path: string;
|
|
7
|
+
constructor(path: string, message: string, featureIds: readonly string[]);
|
|
8
|
+
}
|
|
9
|
+
export declare class ProviderCompositionError extends CompositionError {
|
|
10
|
+
readonly id: string;
|
|
11
|
+
constructor(id: string, message: string, featureIds: readonly string[]);
|
|
12
|
+
}
|
|
13
|
+
export declare class NavCompositionError extends CompositionError {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
constructor(id: string, message: string, featureIds: readonly string[]);
|
|
16
|
+
}
|
|
17
|
+
export declare class SettingsCompositionError extends CompositionError {
|
|
18
|
+
readonly id: string;
|
|
19
|
+
constructor(id: string, message: string, featureIds: readonly string[]);
|
|
20
|
+
}
|
|
21
|
+
export declare class ConfigCompositionError extends CompositionError {
|
|
22
|
+
readonly key: string;
|
|
23
|
+
constructor(key: string, message: string, featureIds: readonly string[]);
|
|
24
|
+
}
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/compose/errors.ts"],"names":[],"mappings":"AAAA,uBAAe,gBAAiB,SAAQ,KAAK;IAC3C,SAAgB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAE9C,SAAS,aAAa,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE;CAKnF;AAED,qBAAa,qBAAsB,SAAQ,gBAAgB;IACzD,SAAgB,IAAI,EAAE,MAAM,CAAC;gBAEjB,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE;CAIzE;AAED,qBAAa,wBAAyB,SAAQ,gBAAgB;IAC5D,SAAgB,EAAE,EAAE,MAAM,CAAC;gBAEf,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE;CAIvE;AAED,qBAAa,mBAAoB,SAAQ,gBAAgB;IACvD,SAAgB,EAAE,EAAE,MAAM,CAAC;gBAEf,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE;CAIvE;AAED,qBAAa,wBAAyB,SAAQ,gBAAgB;IAC5D,SAAgB,EAAE,EAAE,MAAM,CAAC;gBAEf,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE;CAIvE;AAED,qBAAa,sBAAuB,SAAQ,gBAAgB;IAC1D,SAAgB,GAAG,EAAE,MAAM,CAAC;gBAEhB,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE;CAIxE"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
let CompositionError = class CompositionError extends Error {
|
|
2
|
+
constructor(name, message, featureIds){
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = name;
|
|
5
|
+
this.featureIds = featureIds;
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
export class RouteCompositionError extends CompositionError {
|
|
9
|
+
constructor(path, message, featureIds){
|
|
10
|
+
super('RouteCompositionError', message, featureIds);
|
|
11
|
+
this.path = path;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export class ProviderCompositionError extends CompositionError {
|
|
15
|
+
constructor(id, message, featureIds){
|
|
16
|
+
super('ProviderCompositionError', message, featureIds);
|
|
17
|
+
this.id = id;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export class NavCompositionError extends CompositionError {
|
|
21
|
+
constructor(id, message, featureIds){
|
|
22
|
+
super('NavCompositionError', message, featureIds);
|
|
23
|
+
this.id = id;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export class SettingsCompositionError extends CompositionError {
|
|
27
|
+
constructor(id, message, featureIds){
|
|
28
|
+
super('SettingsCompositionError', message, featureIds);
|
|
29
|
+
this.id = id;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export class ConfigCompositionError extends CompositionError {
|
|
33
|
+
constructor(key, message, featureIds){
|
|
34
|
+
super('ConfigCompositionError', message, featureIds);
|
|
35
|
+
this.key = key;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/compose/errors.ts"],"sourcesContent":["abstract class CompositionError extends Error {\n public readonly featureIds: readonly string[];\n\n protected constructor(name: string, message: string, featureIds: readonly string[]) {\n super(message);\n this.name = name;\n this.featureIds = featureIds;\n }\n}\n\nexport class RouteCompositionError extends CompositionError {\n public readonly path: string;\n\n constructor(path: string, message: string, featureIds: readonly string[]) {\n super('RouteCompositionError', message, featureIds);\n this.path = path;\n }\n}\n\nexport class ProviderCompositionError extends CompositionError {\n public readonly id: string;\n\n constructor(id: string, message: string, featureIds: readonly string[]) {\n super('ProviderCompositionError', message, featureIds);\n this.id = id;\n }\n}\n\nexport class NavCompositionError extends CompositionError {\n public readonly id: string;\n\n constructor(id: string, message: string, featureIds: readonly string[]) {\n super('NavCompositionError', message, featureIds);\n this.id = id;\n }\n}\n\nexport class SettingsCompositionError extends CompositionError {\n public readonly id: string;\n\n constructor(id: string, message: string, featureIds: readonly string[]) {\n super('SettingsCompositionError', message, featureIds);\n this.id = id;\n }\n}\n\nexport class ConfigCompositionError extends CompositionError {\n public readonly key: string;\n\n constructor(key: string, message: string, featureIds: readonly string[]) {\n super('ConfigCompositionError', message, featureIds);\n this.key = key;\n }\n}\n"],"names":["CompositionError","Error","name","message","featureIds","RouteCompositionError","path","ProviderCompositionError","id","NavCompositionError","SettingsCompositionError","ConfigCompositionError","key"],"mappings":"AAAA,IAAA,AAAeA,mBAAf,MAAeA,yBAAyBC;IAGtC,YAAsBC,IAAY,EAAEC,OAAe,EAAEC,UAA6B,CAAE;QAClF,KAAK,CAACD;QACN,IAAI,CAACD,IAAI,GAAGA;QACZ,IAAI,CAACE,UAAU,GAAGA;IACpB;AACF;AAEA,OAAO,MAAMC,8BAA8BL;IAGzC,YAAYM,IAAY,EAAEH,OAAe,EAAEC,UAA6B,CAAE;QACxE,KAAK,CAAC,yBAAyBD,SAASC;QACxC,IAAI,CAACE,IAAI,GAAGA;IACd;AACF;AAEA,OAAO,MAAMC,iCAAiCP;IAG5C,YAAYQ,EAAU,EAAEL,OAAe,EAAEC,UAA6B,CAAE;QACtE,KAAK,CAAC,4BAA4BD,SAASC;QAC3C,IAAI,CAACI,EAAE,GAAGA;IACZ;AACF;AAEA,OAAO,MAAMC,4BAA4BT;IAGvC,YAAYQ,EAAU,EAAEL,OAAe,EAAEC,UAA6B,CAAE;QACtE,KAAK,CAAC,uBAAuBD,SAASC;QACtC,IAAI,CAACI,EAAE,GAAGA;IACZ;AACF;AAEA,OAAO,MAAME,iCAAiCV;IAG5C,YAAYQ,EAAU,EAAEL,OAAe,EAAEC,UAA6B,CAAE;QACtE,KAAK,CAAC,4BAA4BD,SAASC;QAC3C,IAAI,CAACI,EAAE,GAAGA;IACZ;AACF;AAEA,OAAO,MAAMG,+BAA+BX;IAG1C,YAAYY,GAAW,EAAET,OAAe,EAAEC,UAA6B,CAAE;QACvE,KAAK,CAAC,0BAA0BD,SAASC;QACzC,IAAI,CAACQ,GAAG,GAAGA;IACb;AACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-config.d.ts","sourceRoot":"","sources":["../../src/compose/merge-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAE3B,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,cAAc,CAAC;AAGhD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,GAAG,CAAC,CAAC,WAAW,CAqBnF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { shellConfigShape } from '#config/shell-config.js';
|
|
2
|
+
import { ConfigCompositionError } from './errors.js';
|
|
3
|
+
export function mergeConfigShapes(features) {
|
|
4
|
+
const merged = {
|
|
5
|
+
...shellConfigShape
|
|
6
|
+
};
|
|
7
|
+
const owners = new Map(Object.keys(shellConfigShape).map((key)=>[
|
|
8
|
+
key,
|
|
9
|
+
'shell'
|
|
10
|
+
]));
|
|
11
|
+
for (const feature of features){
|
|
12
|
+
for (const [key, schema] of Object.entries(feature.configShape ?? {})){
|
|
13
|
+
const existing = merged[key];
|
|
14
|
+
if (existing && existing !== schema) {
|
|
15
|
+
const existingFeatureId = owners.get(key) ?? 'shell';
|
|
16
|
+
throw new ConfigCompositionError(key, `Config key "${key}" is contributed by both features "${existingFeatureId}" and "${feature.id}". Reuse the same schema instance to intentionally share it.`, [
|
|
17
|
+
existingFeatureId,
|
|
18
|
+
feature.id
|
|
19
|
+
]);
|
|
20
|
+
}
|
|
21
|
+
merged[key] = schema;
|
|
22
|
+
owners.set(key, feature.id);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return merged;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//# sourceMappingURL=merge-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/compose/merge-config.ts"],"sourcesContent":["import type {z} from 'zod';\nimport {shellConfigShape} from '#config/shell-config.js';\nimport type {ClientFeature} from '#contract.js';\nimport {ConfigCompositionError} from './errors.js';\n\nexport function mergeConfigShapes(features: readonly ClientFeature[]): z.ZodRawShape {\n const merged = {...shellConfigShape} as Record<string, z.ZodRawShape[string]>;\n const owners = new Map<string, string>(\n Object.keys(shellConfigShape).map((key) => [key, 'shell']),\n );\n for (const feature of features) {\n for (const [key, schema] of Object.entries(feature.configShape ?? {})) {\n const existing = merged[key];\n if (existing && existing !== schema) {\n const existingFeatureId = owners.get(key) ?? 'shell';\n throw new ConfigCompositionError(\n key,\n `Config key \"${key}\" is contributed by both features \"${existingFeatureId}\" and \"${feature.id}\". Reuse the same schema instance to intentionally share it.`,\n [existingFeatureId, feature.id],\n );\n }\n merged[key] = schema;\n owners.set(key, feature.id);\n }\n }\n return merged as z.ZodRawShape;\n}\n"],"names":["shellConfigShape","ConfigCompositionError","mergeConfigShapes","features","merged","owners","Map","Object","keys","map","key","feature","schema","entries","configShape","existing","existingFeatureId","get","id","set"],"mappings":"AACA,SAAQA,gBAAgB,QAAO,0BAA0B;AAEzD,SAAQC,sBAAsB,QAAO,cAAc;AAEnD,OAAO,SAASC,kBAAkBC,QAAkC;IAClE,MAAMC,SAAS;QAAC,GAAGJ,gBAAgB;IAAA;IACnC,MAAMK,SAAS,IAAIC,IACjBC,OAAOC,IAAI,CAACR,kBAAkBS,GAAG,CAAC,CAACC,MAAQ;YAACA;YAAK;SAAQ;IAE3D,KAAK,MAAMC,WAAWR,SAAU;QAC9B,KAAK,MAAM,CAACO,KAAKE,OAAO,IAAIL,OAAOM,OAAO,CAACF,QAAQG,WAAW,IAAI,CAAC,GAAI;YACrE,MAAMC,WAAWX,MAAM,CAACM,IAAI;YAC5B,IAAIK,YAAYA,aAAaH,QAAQ;gBACnC,MAAMI,oBAAoBX,OAAOY,GAAG,CAACP,QAAQ;gBAC7C,MAAM,IAAIT,uBACRS,KACA,CAAC,YAAY,EAAEA,IAAI,mCAAmC,EAAEM,kBAAkB,OAAO,EAAEL,QAAQO,EAAE,CAAC,4DAA4D,CAAC,EAC3J;oBAACF;oBAAmBL,QAAQO,EAAE;iBAAC;YAEnC;YACAd,MAAM,CAACM,IAAI,GAAGE;YACdP,OAAOc,GAAG,CAACT,KAAKC,QAAQO,EAAE;QAC5B;IACF;IACA,OAAOd;AACT"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize-route-path.d.ts","sourceRoot":"","sources":["../../src/compose/normalize-route-path.ts"],"names":[],"mappings":"AAEA,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGvD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/compose/normalize-route-path.ts"],"sourcesContent":["const trailingSlashes = /\\/+$/u;\n\nexport function normalizeRoutePath(path: string): string {\n if (path === '/') return path;\n return path.replace(trailingSlashes, '') || '/';\n}\n"],"names":["trailingSlashes","normalizeRoutePath","path","replace"],"mappings":"AAAA,MAAMA,kBAAkB;AAExB,OAAO,SAASC,mBAAmBC,IAAY;IAC7C,IAAIA,SAAS,KAAK,OAAOA;IACzB,OAAOA,KAAKC,OAAO,CAACH,iBAAiB,OAAO;AAC9C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-providers.d.ts","sourceRoot":"","sources":["../../src/compose/validate-providers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,cAAc,CAAC;AAahD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,GAAG,IAAI,CAsB5E"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ProviderCompositionError } from './errors.js';
|
|
2
|
+
const reservedProviderIds = new Set([
|
|
3
|
+
'theme',
|
|
4
|
+
'tooltip',
|
|
5
|
+
'query-client',
|
|
6
|
+
'jotai-store',
|
|
7
|
+
'auth',
|
|
8
|
+
'router',
|
|
9
|
+
'toaster'
|
|
10
|
+
]);
|
|
11
|
+
export function validateProviderIds(features) {
|
|
12
|
+
const providers = new Map();
|
|
13
|
+
for (const feature of features){
|
|
14
|
+
for (const provider of feature.providers ?? []){
|
|
15
|
+
if (reservedProviderIds.has(provider.id)) {
|
|
16
|
+
throw new ProviderCompositionError(provider.id, `Provider id "${provider.id}" in feature "${feature.id}" is reserved by the shell.`, [
|
|
17
|
+
feature.id
|
|
18
|
+
]);
|
|
19
|
+
}
|
|
20
|
+
const existingFeatureId = providers.get(provider.id);
|
|
21
|
+
if (existingFeatureId) {
|
|
22
|
+
throw new ProviderCompositionError(provider.id, `Provider id "${provider.id}" is contributed by both features "${existingFeatureId}" and "${feature.id}".`, [
|
|
23
|
+
existingFeatureId,
|
|
24
|
+
feature.id
|
|
25
|
+
]);
|
|
26
|
+
}
|
|
27
|
+
providers.set(provider.id, feature.id);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=validate-providers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/compose/validate-providers.ts"],"sourcesContent":["import type {ClientFeature} from '#contract.js';\nimport {ProviderCompositionError} from './errors.js';\n\nconst reservedProviderIds = new Set([\n 'theme',\n 'tooltip',\n 'query-client',\n 'jotai-store',\n 'auth',\n 'router',\n 'toaster',\n]);\n\nexport function validateProviderIds(features: readonly ClientFeature[]): void {\n const providers = new Map<string, string>();\n for (const feature of features) {\n for (const provider of feature.providers ?? []) {\n if (reservedProviderIds.has(provider.id)) {\n throw new ProviderCompositionError(\n provider.id,\n `Provider id \"${provider.id}\" in feature \"${feature.id}\" is reserved by the shell.`,\n [feature.id],\n );\n }\n const existingFeatureId = providers.get(provider.id);\n if (existingFeatureId) {\n throw new ProviderCompositionError(\n provider.id,\n `Provider id \"${provider.id}\" is contributed by both features \"${existingFeatureId}\" and \"${feature.id}\".`,\n [existingFeatureId, feature.id],\n );\n }\n providers.set(provider.id, feature.id);\n }\n }\n}\n"],"names":["ProviderCompositionError","reservedProviderIds","Set","validateProviderIds","features","providers","Map","feature","provider","has","id","existingFeatureId","get","set"],"mappings":"AACA,SAAQA,wBAAwB,QAAO,cAAc;AAErD,MAAMC,sBAAsB,IAAIC,IAAI;IAClC;IACA;IACA;IACA;IACA;IACA;IACA;CACD;AAED,OAAO,SAASC,oBAAoBC,QAAkC;IACpE,MAAMC,YAAY,IAAIC;IACtB,KAAK,MAAMC,WAAWH,SAAU;QAC9B,KAAK,MAAMI,YAAYD,QAAQF,SAAS,IAAI,EAAE,CAAE;YAC9C,IAAIJ,oBAAoBQ,GAAG,CAACD,SAASE,EAAE,GAAG;gBACxC,MAAM,IAAIV,yBACRQ,SAASE,EAAE,EACX,CAAC,aAAa,EAAEF,SAASE,EAAE,CAAC,cAAc,EAAEH,QAAQG,EAAE,CAAC,2BAA2B,CAAC,EACnF;oBAACH,QAAQG,EAAE;iBAAC;YAEhB;YACA,MAAMC,oBAAoBN,UAAUO,GAAG,CAACJ,SAASE,EAAE;YACnD,IAAIC,mBAAmB;gBACrB,MAAM,IAAIX,yBACRQ,SAASE,EAAE,EACX,CAAC,aAAa,EAAEF,SAASE,EAAE,CAAC,mCAAmC,EAAEC,kBAAkB,OAAO,EAAEJ,QAAQG,EAAE,CAAC,EAAE,CAAC,EAC1G;oBAACC;oBAAmBJ,QAAQG,EAAE;iBAAC;YAEnC;YACAL,UAAUQ,GAAG,CAACL,SAASE,EAAE,EAAEH,QAAQG,EAAE;QACvC;IACF;AACF"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ClientFeature } from '#contract.js';
|
|
2
|
+
export declare function validateNavigation(features: readonly ClientFeature[], routePaths: Iterable<string>): void;
|
|
3
|
+
export declare function validateSettingsSections(features: readonly ClientFeature[], routePaths: Iterable<string>): void;
|
|
4
|
+
//# sourceMappingURL=validate-registries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-registries.d.ts","sourceRoot":"","sources":["../../src/compose/validate-registries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,cAAc,CAAC;AAIhD,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,SAAS,aAAa,EAAE,EAClC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,GAC3B,IAAI,CAwBN;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,SAAS,aAAa,EAAE,EAClC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,GAC3B,IAAI,CAwBN"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { NavCompositionError, SettingsCompositionError } from './errors.js';
|
|
2
|
+
import { normalizeRoutePath } from './normalize-route-path.js';
|
|
3
|
+
export function validateNavigation(features, routePaths) {
|
|
4
|
+
const routes = new Set([
|
|
5
|
+
...routePaths
|
|
6
|
+
].map(normalizeRoutePath));
|
|
7
|
+
const entries = new Map();
|
|
8
|
+
for (const feature of features){
|
|
9
|
+
for (const entry of feature.navigation ?? []){
|
|
10
|
+
const existingFeatureId = entries.get(entry.id);
|
|
11
|
+
if (existingFeatureId) {
|
|
12
|
+
throw new NavCompositionError(entry.id, `Navigation entry "${entry.id}" is contributed by both features "${existingFeatureId}" and "${feature.id}".`, [
|
|
13
|
+
existingFeatureId,
|
|
14
|
+
feature.id
|
|
15
|
+
]);
|
|
16
|
+
}
|
|
17
|
+
const target = normalizeRoutePath(entry.to);
|
|
18
|
+
if (!routes.has(target)) {
|
|
19
|
+
throw new NavCompositionError(entry.id, `Navigation entry "${entry.id}" in feature "${feature.id}" targets missing route "${target}".`, [
|
|
20
|
+
feature.id
|
|
21
|
+
]);
|
|
22
|
+
}
|
|
23
|
+
entries.set(entry.id, feature.id);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function validateSettingsSections(features, routePaths) {
|
|
28
|
+
const routes = new Set(routePaths);
|
|
29
|
+
const sections = new Map();
|
|
30
|
+
for (const feature of features){
|
|
31
|
+
for (const section of feature.settingsSections ?? []){
|
|
32
|
+
const existingFeatureId = sections.get(section.id);
|
|
33
|
+
if (existingFeatureId) {
|
|
34
|
+
throw new SettingsCompositionError(section.id, `Settings section "${section.id}" is contributed by both features "${existingFeatureId}" and "${feature.id}".`, [
|
|
35
|
+
existingFeatureId,
|
|
36
|
+
feature.id
|
|
37
|
+
]);
|
|
38
|
+
}
|
|
39
|
+
const path = `/workspaces/$wid/settings/${section.pathSegment}`;
|
|
40
|
+
if (!routes.has(path)) {
|
|
41
|
+
throw new SettingsCompositionError(section.id, `Settings section "${section.id}" in feature "${feature.id}" requires route "${path}".`, [
|
|
42
|
+
feature.id
|
|
43
|
+
]);
|
|
44
|
+
}
|
|
45
|
+
sections.set(section.id, feature.id);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
//# sourceMappingURL=validate-registries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/compose/validate-registries.ts"],"sourcesContent":["import type {ClientFeature} from '#contract.js';\nimport {NavCompositionError, SettingsCompositionError} from './errors.js';\nimport {normalizeRoutePath} from './normalize-route-path.js';\n\nexport function validateNavigation(\n features: readonly ClientFeature[],\n routePaths: Iterable<string>,\n): void {\n const routes = new Set([...routePaths].map(normalizeRoutePath));\n const entries = new Map<string, string>();\n for (const feature of features) {\n for (const entry of feature.navigation ?? []) {\n const existingFeatureId = entries.get(entry.id);\n if (existingFeatureId) {\n throw new NavCompositionError(\n entry.id,\n `Navigation entry \"${entry.id}\" is contributed by both features \"${existingFeatureId}\" and \"${feature.id}\".`,\n [existingFeatureId, feature.id],\n );\n }\n const target = normalizeRoutePath(entry.to);\n if (!routes.has(target)) {\n throw new NavCompositionError(\n entry.id,\n `Navigation entry \"${entry.id}\" in feature \"${feature.id}\" targets missing route \"${target}\".`,\n [feature.id],\n );\n }\n entries.set(entry.id, feature.id);\n }\n }\n}\n\nexport function validateSettingsSections(\n features: readonly ClientFeature[],\n routePaths: Iterable<string>,\n): void {\n const routes = new Set(routePaths);\n const sections = new Map<string, string>();\n for (const feature of features) {\n for (const section of feature.settingsSections ?? []) {\n const existingFeatureId = sections.get(section.id);\n if (existingFeatureId) {\n throw new SettingsCompositionError(\n section.id,\n `Settings section \"${section.id}\" is contributed by both features \"${existingFeatureId}\" and \"${feature.id}\".`,\n [existingFeatureId, feature.id],\n );\n }\n const path = `/workspaces/$wid/settings/${section.pathSegment}`;\n if (!routes.has(path)) {\n throw new SettingsCompositionError(\n section.id,\n `Settings section \"${section.id}\" in feature \"${feature.id}\" requires route \"${path}\".`,\n [feature.id],\n );\n }\n sections.set(section.id, feature.id);\n }\n }\n}\n"],"names":["NavCompositionError","SettingsCompositionError","normalizeRoutePath","validateNavigation","features","routePaths","routes","Set","map","entries","Map","feature","entry","navigation","existingFeatureId","get","id","target","to","has","set","validateSettingsSections","sections","section","settingsSections","path","pathSegment"],"mappings":"AACA,SAAQA,mBAAmB,EAAEC,wBAAwB,QAAO,cAAc;AAC1E,SAAQC,kBAAkB,QAAO,4BAA4B;AAE7D,OAAO,SAASC,mBACdC,QAAkC,EAClCC,UAA4B;IAE5B,MAAMC,SAAS,IAAIC,IAAI;WAAIF;KAAW,CAACG,GAAG,CAACN;IAC3C,MAAMO,UAAU,IAAIC;IACpB,KAAK,MAAMC,WAAWP,SAAU;QAC9B,KAAK,MAAMQ,SAASD,QAAQE,UAAU,IAAI,EAAE,CAAE;YAC5C,MAAMC,oBAAoBL,QAAQM,GAAG,CAACH,MAAMI,EAAE;YAC9C,IAAIF,mBAAmB;gBACrB,MAAM,IAAId,oBACRY,MAAMI,EAAE,EACR,CAAC,kBAAkB,EAAEJ,MAAMI,EAAE,CAAC,mCAAmC,EAAEF,kBAAkB,OAAO,EAAEH,QAAQK,EAAE,CAAC,EAAE,CAAC,EAC5G;oBAACF;oBAAmBH,QAAQK,EAAE;iBAAC;YAEnC;YACA,MAAMC,SAASf,mBAAmBU,MAAMM,EAAE;YAC1C,IAAI,CAACZ,OAAOa,GAAG,CAACF,SAAS;gBACvB,MAAM,IAAIjB,oBACRY,MAAMI,EAAE,EACR,CAAC,kBAAkB,EAAEJ,MAAMI,EAAE,CAAC,cAAc,EAAEL,QAAQK,EAAE,CAAC,yBAAyB,EAAEC,OAAO,EAAE,CAAC,EAC9F;oBAACN,QAAQK,EAAE;iBAAC;YAEhB;YACAP,QAAQW,GAAG,CAACR,MAAMI,EAAE,EAAEL,QAAQK,EAAE;QAClC;IACF;AACF;AAEA,OAAO,SAASK,yBACdjB,QAAkC,EAClCC,UAA4B;IAE5B,MAAMC,SAAS,IAAIC,IAAIF;IACvB,MAAMiB,WAAW,IAAIZ;IACrB,KAAK,MAAMC,WAAWP,SAAU;QAC9B,KAAK,MAAMmB,WAAWZ,QAAQa,gBAAgB,IAAI,EAAE,CAAE;YACpD,MAAMV,oBAAoBQ,SAASP,GAAG,CAACQ,QAAQP,EAAE;YACjD,IAAIF,mBAAmB;gBACrB,MAAM,IAAIb,yBACRsB,QAAQP,EAAE,EACV,CAAC,kBAAkB,EAAEO,QAAQP,EAAE,CAAC,mCAAmC,EAAEF,kBAAkB,OAAO,EAAEH,QAAQK,EAAE,CAAC,EAAE,CAAC,EAC9G;oBAACF;oBAAmBH,QAAQK,EAAE;iBAAC;YAEnC;YACA,MAAMS,OAAO,CAAC,0BAA0B,EAAEF,QAAQG,WAAW,EAAE;YAC/D,IAAI,CAACpB,OAAOa,GAAG,CAACM,OAAO;gBACrB,MAAM,IAAIxB,yBACRsB,QAAQP,EAAE,EACV,CAAC,kBAAkB,EAAEO,QAAQP,EAAE,CAAC,cAAc,EAAEL,QAAQK,EAAE,CAAC,kBAAkB,EAAES,KAAK,EAAE,CAAC,EACvF;oBAACd,QAAQK,EAAE;iBAAC;YAEhB;YACAM,SAASF,GAAG,CAACG,QAAQP,EAAE,EAAEL,QAAQK,EAAE;QACrC;IACF;AACF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const shellConfigShape: {
|
|
3
|
+
environment: z.ZodDefault<z.ZodEnum<{
|
|
4
|
+
development: "development";
|
|
5
|
+
staging: "staging";
|
|
6
|
+
production: "production";
|
|
7
|
+
}>>;
|
|
8
|
+
apiUrl: z.ZodDefault<z.ZodUnion<[z.ZodString, z.ZodLiteral<"">]>>;
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=shell-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell-config.d.ts","sourceRoot":"","sources":["../../src/config/shell-config.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,eAAO,MAAM,gBAAgB;;;;;;;CAM5B,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { apiConfigShape } from '@shipfox/client-api';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
export const shellConfigShape = {
|
|
4
|
+
...apiConfigShape,
|
|
5
|
+
environment: z.enum([
|
|
6
|
+
'development',
|
|
7
|
+
'staging',
|
|
8
|
+
'production'
|
|
9
|
+
]).default('production').describe('Deployment environment name shown in diagnostics and reported to monitoring.')
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=shell-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/config/shell-config.ts"],"sourcesContent":["import {apiConfigShape} from '@shipfox/client-api';\nimport {z} from 'zod';\n\nexport const shellConfigShape = {\n ...apiConfigShape,\n environment: z\n .enum(['development', 'staging', 'production'])\n .default('production')\n .describe('Deployment environment name shown in diagnostics and reported to monitoring.'),\n};\n"],"names":["apiConfigShape","z","shellConfigShape","environment","enum","default","describe"],"mappings":"AAAA,SAAQA,cAAc,QAAO,sBAAsB;AACnD,SAAQC,CAAC,QAAO,MAAM;AAEtB,OAAO,MAAMC,mBAAmB;IAC9B,GAAGF,cAAc;IACjBG,aAAaF,EACVG,IAAI,CAAC;QAAC;QAAe;QAAW;KAAa,EAC7CC,OAAO,CAAC,cACRC,QAAQ,CAAC;AACd,EAAE"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { IconName } from '@shipfox/react-ui/icon';
|
|
2
|
+
import type { ComponentType, PropsWithChildren } from 'react';
|
|
3
|
+
import type { z } from 'zod';
|
|
4
|
+
export type AnchorId = 'root' | 'workspaceLayout' | 'projectLayout' | 'workspaceSettings';
|
|
5
|
+
export interface RouteContribution {
|
|
6
|
+
path: string;
|
|
7
|
+
parent: AnchorId;
|
|
8
|
+
override?: boolean;
|
|
9
|
+
impl: string;
|
|
10
|
+
}
|
|
11
|
+
export interface FeatureProvider {
|
|
12
|
+
id: string;
|
|
13
|
+
Component: ComponentType<PropsWithChildren>;
|
|
14
|
+
}
|
|
15
|
+
export interface NavTabEntry {
|
|
16
|
+
id: string;
|
|
17
|
+
scope: 'workspace' | 'project';
|
|
18
|
+
label: string;
|
|
19
|
+
to: string;
|
|
20
|
+
exact?: boolean;
|
|
21
|
+
order?: number;
|
|
22
|
+
}
|
|
23
|
+
export interface SettingsSectionEntry {
|
|
24
|
+
id: string;
|
|
25
|
+
pathSegment: string;
|
|
26
|
+
label: string;
|
|
27
|
+
icon: IconName;
|
|
28
|
+
order?: number;
|
|
29
|
+
}
|
|
30
|
+
export interface ClientFeature<S extends z.ZodRawShape = z.ZodRawShape> {
|
|
31
|
+
id: string;
|
|
32
|
+
routes?: readonly RouteContribution[];
|
|
33
|
+
providers?: readonly FeatureProvider[];
|
|
34
|
+
navigation?: readonly NavTabEntry[];
|
|
35
|
+
settingsSections?: readonly SettingsSectionEntry[];
|
|
36
|
+
configShape?: S;
|
|
37
|
+
}
|
|
38
|
+
export declare function defineClientFeature<const T extends ClientFeature>(feature: T): T;
|
|
39
|
+
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,wBAAwB,CAAC;AACrD,OAAO,KAAK,EAAC,aAAa,EAAE,iBAAiB,EAAC,MAAM,OAAO,CAAC;AAC5D,OAAO,KAAK,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAE3B,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,iBAAiB,GAAG,eAAe,GAAG,mBAAmB,CAAC;AAE1F,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,QAAQ,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,aAAa,CAAC,iBAAiB,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,WAAW,GAAG,SAAS,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,WAAW;IACpE,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACtC,SAAS,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACvC,UAAU,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;IACpC,gBAAgB,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAC;IACnD,WAAW,CAAC,EAAE,CAAC,CAAC;CACjB;AAED,wBAAgB,mBAAmB,CAAC,KAAK,CAAC,CAAC,SAAS,aAAa,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,CAEhF"}
|
package/dist/contract.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/contract.ts"],"sourcesContent":["import type {IconName} from '@shipfox/react-ui/icon';\nimport type {ComponentType, PropsWithChildren} from 'react';\nimport type {z} from 'zod';\n\nexport type AnchorId = 'root' | 'workspaceLayout' | 'projectLayout' | 'workspaceSettings';\n\nexport interface RouteContribution {\n path: string;\n parent: AnchorId;\n override?: boolean;\n impl: string;\n}\n\nexport interface FeatureProvider {\n id: string;\n Component: ComponentType<PropsWithChildren>;\n}\n\nexport interface NavTabEntry {\n id: string;\n scope: 'workspace' | 'project';\n label: string;\n to: string;\n exact?: boolean;\n order?: number;\n}\n\nexport interface SettingsSectionEntry {\n id: string;\n pathSegment: string;\n label: string;\n icon: IconName;\n order?: number;\n}\n\nexport interface ClientFeature<S extends z.ZodRawShape = z.ZodRawShape> {\n id: string;\n routes?: readonly RouteContribution[];\n providers?: readonly FeatureProvider[];\n navigation?: readonly NavTabEntry[];\n settingsSections?: readonly SettingsSectionEntry[];\n configShape?: S;\n}\n\nexport function defineClientFeature<const T extends ClientFeature>(feature: T): T {\n return feature;\n}\n"],"names":["defineClientFeature","feature"],"mappings":"AA4CA,OAAO,SAASA,oBAAmDC,OAAU;IAC3E,OAAOA;AACT"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './contract.js';\n"],"names":[],"mappings":"AAAA,cAAc,gBAAgB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"active-workspace.d.ts","sourceRoot":"","sources":["../../src/runtime/active-workspace.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,KAAK,SAAS,EAAC,MAAM,WAAW,CAAC;AAEvD,wBAAgB,kBAAkB,IAAI,SAAS,CAI9C;AAED,wBAAgB,uBAAuB,IAAI,SAAS,GAAG,SAAS,CAG/D"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useParams } from '@tanstack/react-router';
|
|
2
|
+
import { useAuthState } from './auth.js';
|
|
3
|
+
export function useActiveWorkspace() {
|
|
4
|
+
const workspace = useMaybeActiveWorkspace();
|
|
5
|
+
if (!workspace) throw new Error('useActiveWorkspace called outside a /workspaces/$wid route');
|
|
6
|
+
return workspace;
|
|
7
|
+
}
|
|
8
|
+
export function useMaybeActiveWorkspace() {
|
|
9
|
+
const { wid } = useParams({
|
|
10
|
+
strict: false
|
|
11
|
+
});
|
|
12
|
+
return useAuthState().workspaces.find((workspace)=>workspace.id === wid);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=active-workspace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/runtime/active-workspace.ts"],"sourcesContent":["import {useParams} from '@tanstack/react-router';\nimport {useAuthState, type Workspace} from './auth.js';\n\nexport function useActiveWorkspace(): Workspace {\n const workspace = useMaybeActiveWorkspace();\n if (!workspace) throw new Error('useActiveWorkspace called outside a /workspaces/$wid route');\n return workspace;\n}\n\nexport function useMaybeActiveWorkspace(): Workspace | undefined {\n const {wid} = useParams({strict: false}) as {wid?: string};\n return useAuthState().workspaces.find((workspace) => workspace.id === wid);\n}\n"],"names":["useParams","useAuthState","useActiveWorkspace","workspace","useMaybeActiveWorkspace","Error","wid","strict","workspaces","find","id"],"mappings":"AAAA,SAAQA,SAAS,QAAO,yBAAyB;AACjD,SAAQC,YAAY,QAAuB,YAAY;AAEvD,OAAO,SAASC;IACd,MAAMC,YAAYC;IAClB,IAAI,CAACD,WAAW,MAAM,IAAIE,MAAM;IAChC,OAAOF;AACT;AAEA,OAAO,SAASC;IACd,MAAM,EAACE,GAAG,EAAC,GAAGN,UAAU;QAACO,QAAQ;IAAK;IACtC,OAAON,eAAeO,UAAU,CAACC,IAAI,CAAC,CAACN,YAAcA,UAAUO,EAAE,KAAKJ;AACxE"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
declare const anchorPaths: {
|
|
2
|
+
readonly root: "/";
|
|
3
|
+
readonly workspaceLayout: "/workspaces/$wid";
|
|
4
|
+
readonly projectLayout: "/workspaces/$wid/projects/$pid";
|
|
5
|
+
readonly workspaceSettings: "/workspaces/$wid/settings";
|
|
6
|
+
};
|
|
7
|
+
export { anchorPaths };
|
|
8
|
+
export declare function routePathForAnchor(anchor: keyof typeof anchorPaths, fullPath: string): string;
|
|
9
|
+
//# sourceMappingURL=anchor-paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anchor-paths.d.ts","sourceRoot":"","sources":["../../src/runtime/anchor-paths.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,WAAW;;;;;CAKP,CAAC;AAEX,OAAO,EAAC,WAAW,EAAC,CAAC;AAErB,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,OAAO,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQ7F"}
|