@shipfox/client-shell 11.0.0 → 12.0.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/dist/compose/compose-client-features.d.ts +2 -1
- package/dist/compose/compose-client-features.d.ts.map +1 -1
- package/dist/compose/compose-client-features.js +5 -3
- package/dist/compose/compose-client-features.js.map +1 -1
- package/dist/compose/compose-routes.d.ts +6 -2
- package/dist/compose/compose-routes.d.ts.map +1 -1
- package/dist/compose/compose-routes.js +137 -2
- package/dist/compose/compose-routes.js.map +1 -1
- package/dist/compose/errors.d.ts +4 -0
- package/dist/compose/errors.d.ts.map +1 -1
- package/dist/compose/errors.js +6 -0
- package/dist/compose/errors.js.map +1 -1
- package/dist/compose/validate-registries.d.ts +9 -2
- package/dist/compose/validate-registries.d.ts.map +1 -1
- package/dist/compose/validate-registries.js +83 -2
- package/dist/compose/validate-registries.js.map +1 -1
- package/dist/contract.d.ts +23 -3
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js.map +1 -1
- package/dist/runtime/anchor-paths.d.ts +2 -0
- package/dist/runtime/anchor-paths.d.ts.map +1 -1
- package/dist/runtime/anchor-paths.js +15 -0
- package/dist/runtime/anchor-paths.js.map +1 -1
- package/dist/runtime/assemble-route-tree.d.ts +3 -2
- package/dist/runtime/assemble-route-tree.d.ts.map +1 -1
- package/dist/runtime/assemble-route-tree.js +76 -5
- package/dist/runtime/assemble-route-tree.js.map +1 -1
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/layout-navigation.d.ts +7 -0
- package/dist/runtime/layout-navigation.d.ts.map +1 -0
- package/dist/runtime/layout-navigation.js +18 -0
- package/dist/runtime/layout-navigation.js.map +1 -0
- package/dist/runtime/provider-stack.d.ts.map +1 -1
- package/dist/runtime/provider-stack.js +7 -3
- package/dist/runtime/provider-stack.js.map +1 -1
- package/dist/runtime/registries.d.ts +2 -1
- package/dist/runtime/registries.d.ts.map +1 -1
- package/dist/runtime/registries.js +10 -0
- package/dist/runtime/registries.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/dist/vite/generate.d.ts +3 -2
- package/dist/vite/generate.d.ts.map +1 -1
- package/dist/vite/generate.js +108 -20
- package/dist/vite/generate.js.map +1 -1
- package/dist/vite/plugin.d.ts.map +1 -1
- package/dist/vite/plugin.js +7 -3
- package/dist/vite/plugin.js.map +1 -1
- package/package.json +2 -2
package/dist/vite/generate.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type { ComposedRoute } from '#compose/compose-routes.js';
|
|
1
|
+
import type { ComposedLayout, ComposedRoute } from '#compose/compose-routes.js';
|
|
2
2
|
import type { NavTabEntry, SettingsSectionEntry } from '#contract.js';
|
|
3
3
|
export interface GenerateAppModuleOptions {
|
|
4
|
+
layouts?: readonly ComposedLayout[];
|
|
4
5
|
routes: readonly ComposedRoute[];
|
|
5
6
|
navigation: readonly NavTabEntry[];
|
|
6
7
|
settingsSections: readonly SettingsSectionEntry[];
|
|
7
8
|
}
|
|
8
|
-
export declare function generateAppModule({ routes, navigation, settingsSections, }: GenerateAppModuleOptions): string;
|
|
9
|
+
export declare function generateAppModule({ layouts, routes, navigation, settingsSections, }: GenerateAppModuleOptions): string;
|
|
9
10
|
//# sourceMappingURL=generate.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/vite/generate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/vite/generate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,cAAc,EAAE,aAAa,EAAC,MAAM,4BAA4B,CAAC;AAC9E,OAAO,KAAK,EAAC,WAAW,EAAiB,oBAAoB,EAAC,MAAM,cAAc,CAAC;AAGnF,MAAM,WAAW,wBAAwB;IACvC,OAAO,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IACpC,MAAM,EAAE,SAAS,aAAa,EAAE,CAAC;IACjC,UAAU,EAAE,SAAS,WAAW,EAAE,CAAC;IACnC,gBAAgB,EAAE,SAAS,oBAAoB,EAAE,CAAC;CACnD;AAoED,wBAAgB,iBAAiB,CAAC,EAChC,OAAY,EACZ,MAAM,EACN,UAAU,EACV,gBAAgB,GACjB,EAAE,wBAAwB,GAAG,MAAM,CA8GnC"}
|
package/dist/vite/generate.js
CHANGED
|
@@ -1,35 +1,125 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { routePathForParent } from '#runtime/anchor-paths.js';
|
|
2
2
|
function literal(value) {
|
|
3
3
|
return JSON.stringify(value, undefined, 2);
|
|
4
4
|
}
|
|
5
5
|
function indentedLiteral(value, indentation) {
|
|
6
6
|
return literal(value).replaceAll('\n', `\n${' '.repeat(indentation)}`);
|
|
7
7
|
}
|
|
8
|
+
function isShellAnchor(parent) {
|
|
9
|
+
return [
|
|
10
|
+
'root',
|
|
11
|
+
'workspaceLayout',
|
|
12
|
+
'projectLayout',
|
|
13
|
+
'workspaceSettings'
|
|
14
|
+
].includes(parent);
|
|
15
|
+
}
|
|
16
|
+
function orderedLayouts(layouts) {
|
|
17
|
+
const byId = new Map(layouts.map((layout)=>[
|
|
18
|
+
layout.id,
|
|
19
|
+
layout
|
|
20
|
+
]));
|
|
21
|
+
const ordered = [];
|
|
22
|
+
const visiting = new Set();
|
|
23
|
+
const visited = new Set();
|
|
24
|
+
function visit(layout) {
|
|
25
|
+
if (visited.has(layout.id)) return;
|
|
26
|
+
if (!isShellAnchor(layout.parent)) {
|
|
27
|
+
if (visiting.has(layout.id)) {
|
|
28
|
+
throw new Error(`Layout "${layout.id}" has a cyclic parent reference.`);
|
|
29
|
+
}
|
|
30
|
+
const parent = byId.get(layout.parent);
|
|
31
|
+
if (!parent) {
|
|
32
|
+
throw new Error(`Layout "${layout.id}" targets missing layout parent "${layout.parent}".`);
|
|
33
|
+
}
|
|
34
|
+
visiting.add(layout.id);
|
|
35
|
+
visit(parent);
|
|
36
|
+
visiting.delete(layout.id);
|
|
37
|
+
}
|
|
38
|
+
visited.add(layout.id);
|
|
39
|
+
ordered.push(layout);
|
|
40
|
+
}
|
|
41
|
+
for (const layout of layouts)visit(layout);
|
|
42
|
+
return ordered;
|
|
43
|
+
}
|
|
8
44
|
function routeNames(routes, parent) {
|
|
9
45
|
return routes.map((route, index)=>({
|
|
10
46
|
route,
|
|
11
47
|
index
|
|
12
48
|
})).filter(({ route })=>route.parent === parent).map(({ index })=>`route${index}`).join(', ');
|
|
13
49
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}));
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
50
|
+
function layoutTreeNames(layouts, parent) {
|
|
51
|
+
return layouts.map((layout, index)=>({
|
|
52
|
+
layout,
|
|
53
|
+
index
|
|
54
|
+
})).filter(({ layout })=>layout.parent === parent).map(({ index })=>`layout${index}Tree`);
|
|
55
|
+
}
|
|
56
|
+
function parentExpression(parent, layoutIndexes) {
|
|
57
|
+
if (parent === 'root') return 'skeleton.rootRoute';
|
|
58
|
+
if (isShellAnchor(parent)) return `skeleton.${parent}`;
|
|
59
|
+
const index = layoutIndexes.get(parent);
|
|
60
|
+
if (index === undefined) throw new Error(`Missing generated layout parent "${parent}".`);
|
|
61
|
+
return `layout${index}`;
|
|
62
|
+
}
|
|
63
|
+
export function generateAppModule({ layouts = [], routes, navigation, settingsSections }) {
|
|
64
|
+
const generatedLayouts = orderedLayouts(layouts);
|
|
65
|
+
const layoutIndexes = new Map(generatedLayouts.map((layout, index)=>[
|
|
66
|
+
layout.id,
|
|
67
|
+
index
|
|
68
|
+
]));
|
|
69
|
+
const layoutPaths = new Map(generatedLayouts.map((layout)=>[
|
|
70
|
+
layout.id,
|
|
71
|
+
layout.path
|
|
72
|
+
]));
|
|
73
|
+
const imports = [
|
|
74
|
+
...generatedLayouts.map((layout, index)=>`import * as layout${index}Module from ${literal(layout.impl)};`),
|
|
75
|
+
...routes.map((route, index)=>`import * as route${index}Module from ${literal(route.impl)};`)
|
|
76
|
+
].join('\n');
|
|
77
|
+
const layoutDeclarations = generatedLayouts.map((layout, index)=>`const layout${index} = createRoute({
|
|
78
|
+
getParentRoute: () => ${parentExpression(layout.parent, layoutIndexes)},
|
|
79
|
+
path: ${literal(routePathForParent(layout.parent, layout.path, layoutPaths))},
|
|
80
|
+
...routeOptions(layout${index}Module.default, ${literal(layout.impl)}, ${literal(layout.path)}),
|
|
81
|
+
});`).join('\n\n');
|
|
82
|
+
const routeDeclarations = routes.map((route, index)=>`const route${index} = createRoute({
|
|
83
|
+
getParentRoute: () => ${parentExpression(route.parent, layoutIndexes)},
|
|
84
|
+
path: ${literal(routePathForParent(route.parent, route.path, layoutPaths))},
|
|
23
85
|
...routeOptions(route${index}Module.default, ${literal(route.impl)}, ${literal(route.path)}),
|
|
24
86
|
});`).join('\n\n');
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
87
|
+
const layoutTreeDeclarations = generatedLayouts.map((layout, index)=>({
|
|
88
|
+
layout,
|
|
89
|
+
index
|
|
90
|
+
})).reverse().map(({ layout, index })=>{
|
|
91
|
+
const routeChildren = routeNames(routes, layout.id);
|
|
92
|
+
const children = [
|
|
93
|
+
routeChildren,
|
|
94
|
+
...layoutTreeNames(generatedLayouts, layout.id)
|
|
95
|
+
].filter(Boolean).join(',\n ');
|
|
96
|
+
return `const layout${index}Tree = layout${index}.addChildren([${children}]);`;
|
|
97
|
+
}).join('\n');
|
|
98
|
+
const declarations = [
|
|
99
|
+
layoutDeclarations,
|
|
100
|
+
routeDeclarations,
|
|
101
|
+
layoutTreeDeclarations
|
|
102
|
+
].filter(Boolean).join('\n\n');
|
|
103
|
+
const rootRoutes = routeNames(routes, 'root');
|
|
29
104
|
const rootChildren = [
|
|
30
105
|
rootRoutes,
|
|
106
|
+
...layoutTreeNames(generatedLayouts, 'root'),
|
|
31
107
|
'workspaceLayout'
|
|
32
108
|
].filter(Boolean).join(',\n ');
|
|
109
|
+
const projectChildren = [
|
|
110
|
+
routeNames(routes, 'projectLayout'),
|
|
111
|
+
...layoutTreeNames(generatedLayouts, 'projectLayout')
|
|
112
|
+
].filter(Boolean).join(',\n ');
|
|
113
|
+
const workspaceSettingsChildren = [
|
|
114
|
+
routeNames(routes, 'workspaceSettings'),
|
|
115
|
+
...layoutTreeNames(generatedLayouts, 'workspaceSettings')
|
|
116
|
+
].filter(Boolean).join(',\n ');
|
|
117
|
+
const workspaceChildren = [
|
|
118
|
+
routeNames(routes, 'workspaceLayout'),
|
|
119
|
+
...layoutTreeNames(generatedLayouts, 'workspaceLayout'),
|
|
120
|
+
'projectLayout',
|
|
121
|
+
'workspaceSettings'
|
|
122
|
+
].filter(Boolean).join(',\n ');
|
|
33
123
|
return `// GENERATED by @shipfox/client-shell/vite. Do not edit.
|
|
34
124
|
// biome-ignore-all format: generated code has stable, reviewable output.
|
|
35
125
|
// biome-ignore-all assist/source/organizeImports: generated imports follow route order.
|
|
@@ -49,14 +139,12 @@ const skeleton = buildAnchorSkeleton({
|
|
|
49
139
|
settingsSections: ${indentedLiteral(settingsSections, 2)},
|
|
50
140
|
});
|
|
51
141
|
|
|
52
|
-
${
|
|
142
|
+
${declarations}
|
|
53
143
|
|
|
54
|
-
const projectLayout = skeleton.projectLayout.addChildren([${
|
|
55
|
-
const workspaceSettings = skeleton.workspaceSettings.addChildren([${
|
|
144
|
+
const projectLayout = skeleton.projectLayout.addChildren([${projectChildren}]);
|
|
145
|
+
const workspaceSettings = skeleton.workspaceSettings.addChildren([${workspaceSettingsChildren}]);
|
|
56
146
|
const workspaceLayout = skeleton.workspaceLayout.addChildren([
|
|
57
|
-
${
|
|
58
|
-
projectLayout,
|
|
59
|
-
workspaceSettings,
|
|
147
|
+
${workspaceChildren},
|
|
60
148
|
]);
|
|
61
149
|
|
|
62
150
|
export const routeTree = skeleton.rootRoute.addChildren([
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/vite/generate.ts"],"sourcesContent":["import type {ComposedRoute} from '#compose/compose-routes.js';\nimport type {NavTabEntry, SettingsSectionEntry} from '#contract.js';\nimport {routePathForAnchor} from '#runtime/anchor-paths.js';\n\nexport interface GenerateAppModuleOptions {\n routes: readonly ComposedRoute[];\n navigation: readonly NavTabEntry[];\n settingsSections: readonly SettingsSectionEntry[];\n}\n\nfunction literal(value: unknown): string {\n return JSON.stringify(value, undefined, 2);\n}\n\nfunction indentedLiteral(value: unknown, indentation: number): string {\n return literal(value).replaceAll('\\n', `\\n${' '.repeat(indentation)}`);\n}\n\nfunction routeNames(routes: readonly ComposedRoute[], parent: ComposedRoute['parent']): string {\n return routes\n .map((route, index) => ({route, index}))\n .filter(({route}) => route.parent === parent)\n .map(({index}) => `route${index}`)\n .join(', ');\n}\n\nexport function generateAppModule({\n routes,\n navigation,\n settingsSections,\n}: GenerateAppModuleOptions): string {\n const generatedRoutes = routes.map((route) => ({\n ...route,\n routePath: routePathForAnchor(route.parent, route.path),\n }));\n const imports = routes\n .map((route, index) => `import * as route${index}Module from ${literal(route.impl)};`)\n .join('\\n');\n const routeDeclarations = generatedRoutes\n .map(\n (route, index) => `const route${index} = createRoute({\n getParentRoute: () => ${route.parent === 'root' ? 'skeleton.rootRoute' : `skeleton.${route.parent}`},\n path: ${literal(route.routePath)},\n ...routeOptions(route${index}Module.default, ${literal(route.impl)}, ${literal(route.path)}),\n});`,\n )\n .join('\\n\\n');\n const rootRoutes = routeNames(generatedRoutes, 'root');\n const workspaceLayoutRoutes = routeNames(generatedRoutes, 'workspaceLayout');\n const projectLayoutRoutes = routeNames(generatedRoutes, 'projectLayout');\n const workspaceSettingsRoutes = routeNames(generatedRoutes, 'workspaceSettings');\n const rootChildren = [rootRoutes, 'workspaceLayout'].filter(Boolean).join(',\\n ');\n\n return `// GENERATED by @shipfox/client-shell/vite. Do not edit.\n// biome-ignore-all format: generated code has stable, reviewable output.\n// biome-ignore-all assist/source/organizeImports: generated imports follow route order.\nimport {createRoute, createRouter} from '@tanstack/react-router';\nimport {buildAnchorSkeleton, isRouteImpl, type RouteImpl, type RouterContext} from '@shipfox/client-shell/runtime';\n${imports}\n\nfunction routeOptions<T extends RouteImpl>(routeImpl: T, impl: string, path: string): T['options'] {\n if (!isRouteImpl(routeImpl)) {\n throw new TypeError(\\`Route implementation \"\\${impl}\" for \"\\${path}\" must export default defineRoute(...).\\`);\n }\n return routeImpl.options;\n}\n\nconst skeleton = buildAnchorSkeleton({\n navigation: ${indentedLiteral(navigation, 2)},\n settingsSections: ${indentedLiteral(settingsSections, 2)},\n});\n\n${routeDeclarations}\n\nconst projectLayout = skeleton.projectLayout.addChildren([${projectLayoutRoutes}]);\nconst workspaceSettings = skeleton.workspaceSettings.addChildren([${workspaceSettingsRoutes}]);\nconst workspaceLayout = skeleton.workspaceLayout.addChildren([\n ${workspaceLayoutRoutes}${workspaceLayoutRoutes ? ',' : ''}\n projectLayout,\n workspaceSettings,\n]);\n\nexport const routeTree = skeleton.rootRoute.addChildren([\n ${rootChildren},\n]);\n\nexport const router = createRouter({\n routeTree,\n context: {auth: undefined, queryClient: undefined} satisfies RouterContext,\n scrollRestoration: true,\n});\n\ndeclare module '@tanstack/react-router' {\n interface Register {\n router: typeof router;\n }\n}\n`;\n}\n"],"names":["routePathForAnchor","literal","value","JSON","stringify","undefined","indentedLiteral","indentation","replaceAll","repeat","routeNames","routes","parent","map","route","index","filter","join","generateAppModule","navigation","settingsSections","generatedRoutes","routePath","path","imports","impl","routeDeclarations","rootRoutes","workspaceLayoutRoutes","projectLayoutRoutes","workspaceSettingsRoutes","rootChildren","Boolean"],"mappings":"AAEA,SAAQA,kBAAkB,QAAO,2BAA2B;AAQ5D,SAASC,QAAQC,KAAc;IAC7B,OAAOC,KAAKC,SAAS,CAACF,OAAOG,WAAW;AAC1C;AAEA,SAASC,gBAAgBJ,KAAc,EAAEK,WAAmB;IAC1D,OAAON,QAAQC,OAAOM,UAAU,CAAC,MAAM,CAAC,EAAE,EAAE,IAAIC,MAAM,CAACF,cAAc;AACvE;AAEA,SAASG,WAAWC,MAAgC,EAAEC,MAA+B;IACnF,OAAOD,OACJE,GAAG,CAAC,CAACC,OAAOC,QAAW,CAAA;YAACD;YAAOC;QAAK,CAAA,GACpCC,MAAM,CAAC,CAAC,EAACF,KAAK,EAAC,GAAKA,MAAMF,MAAM,KAAKA,QACrCC,GAAG,CAAC,CAAC,EAACE,KAAK,EAAC,GAAK,CAAC,KAAK,EAAEA,OAAO,EAChCE,IAAI,CAAC;AACV;AAEA,OAAO,SAASC,kBAAkB,EAChCP,MAAM,EACNQ,UAAU,EACVC,gBAAgB,EACS;IACzB,MAAMC,kBAAkBV,OAAOE,GAAG,CAAC,CAACC,QAAW,CAAA;YAC7C,GAAGA,KAAK;YACRQ,WAAWtB,mBAAmBc,MAAMF,MAAM,EAAEE,MAAMS,IAAI;QACxD,CAAA;IACA,MAAMC,UAAUb,OACbE,GAAG,CAAC,CAACC,OAAOC,QAAU,CAAC,iBAAiB,EAAEA,MAAM,YAAY,EAAEd,QAAQa,MAAMW,IAAI,EAAE,CAAC,CAAC,EACpFR,IAAI,CAAC;IACR,MAAMS,oBAAoBL,gBACvBR,GAAG,CACF,CAACC,OAAOC,QAAU,CAAC,WAAW,EAAEA,MAAM;wBACpB,EAAED,MAAMF,MAAM,KAAK,SAAS,uBAAuB,CAAC,SAAS,EAAEE,MAAMF,MAAM,EAAE,CAAC;QAC9F,EAAEX,QAAQa,MAAMQ,SAAS,EAAE;uBACZ,EAAEP,MAAM,gBAAgB,EAAEd,QAAQa,MAAMW,IAAI,EAAE,EAAE,EAAExB,QAAQa,MAAMS,IAAI,EAAE;GAC1F,CAAC,EAECN,IAAI,CAAC;IACR,MAAMU,aAAajB,WAAWW,iBAAiB;IAC/C,MAAMO,wBAAwBlB,WAAWW,iBAAiB;IAC1D,MAAMQ,sBAAsBnB,WAAWW,iBAAiB;IACxD,MAAMS,0BAA0BpB,WAAWW,iBAAiB;IAC5D,MAAMU,eAAe;QAACJ;QAAY;KAAkB,CAACX,MAAM,CAACgB,SAASf,IAAI,CAAC;IAE1E,OAAO,CAAC;;;;;AAKV,EAAEO,QAAQ;;;;;;;;;;cAUI,EAAElB,gBAAgBa,YAAY,GAAG;oBAC3B,EAAEb,gBAAgBc,kBAAkB,GAAG;;;AAG3D,EAAEM,kBAAkB;;0DAEsC,EAAEG,oBAAoB;kEACd,EAAEC,wBAAwB;;EAE1F,EAAEF,wBAAwBA,wBAAwB,MAAM,GAAG;;;;;;EAM3D,EAAEG,aAAa;;;;;;;;;;;;;;AAcjB,CAAC;AACD"}
|
|
1
|
+
{"version":3,"sources":["../../src/vite/generate.ts"],"sourcesContent":["import type {ComposedLayout, ComposedRoute} from '#compose/compose-routes.js';\nimport type {NavTabEntry, RouteParentId, SettingsSectionEntry} from '#contract.js';\nimport {routePathForParent} from '#runtime/anchor-paths.js';\n\nexport interface GenerateAppModuleOptions {\n layouts?: readonly ComposedLayout[];\n routes: readonly ComposedRoute[];\n navigation: readonly NavTabEntry[];\n settingsSections: readonly SettingsSectionEntry[];\n}\n\nfunction literal(value: unknown): string {\n return JSON.stringify(value, undefined, 2);\n}\n\nfunction indentedLiteral(value: unknown, indentation: number): string {\n return literal(value).replaceAll('\\n', `\\n${' '.repeat(indentation)}`);\n}\n\nfunction isShellAnchor(parent: RouteParentId): boolean {\n return ['root', 'workspaceLayout', 'projectLayout', 'workspaceSettings'].includes(parent);\n}\n\nfunction orderedLayouts(layouts: readonly ComposedLayout[]): ComposedLayout[] {\n const byId = new Map(layouts.map((layout) => [layout.id, layout]));\n const ordered: ComposedLayout[] = [];\n const visiting = new Set<string>();\n const visited = new Set<string>();\n\n function visit(layout: ComposedLayout): void {\n if (visited.has(layout.id)) return;\n if (!isShellAnchor(layout.parent)) {\n if (visiting.has(layout.id)) {\n throw new Error(`Layout \"${layout.id}\" has a cyclic parent reference.`);\n }\n const parent = byId.get(layout.parent);\n if (!parent) {\n throw new Error(`Layout \"${layout.id}\" targets missing layout parent \"${layout.parent}\".`);\n }\n visiting.add(layout.id);\n visit(parent);\n visiting.delete(layout.id);\n }\n visited.add(layout.id);\n ordered.push(layout);\n }\n\n for (const layout of layouts) visit(layout);\n return ordered;\n}\n\nfunction routeNames(routes: readonly ComposedRoute[], parent: RouteParentId): string {\n return routes\n .map((route, index) => ({route, index}))\n .filter(({route}) => route.parent === parent)\n .map(({index}) => `route${index}`)\n .join(', ');\n}\n\nfunction layoutTreeNames(layouts: readonly ComposedLayout[], parent: RouteParentId): string[] {\n return layouts\n .map((layout, index) => ({layout, index}))\n .filter(({layout}) => layout.parent === parent)\n .map(({index}) => `layout${index}Tree`);\n}\n\nfunction parentExpression(\n parent: RouteParentId,\n layoutIndexes: ReadonlyMap<string, number>,\n): string {\n if (parent === 'root') return 'skeleton.rootRoute';\n if (isShellAnchor(parent)) return `skeleton.${parent}`;\n const index = layoutIndexes.get(parent);\n if (index === undefined) throw new Error(`Missing generated layout parent \"${parent}\".`);\n return `layout${index}`;\n}\n\nexport function generateAppModule({\n layouts = [],\n routes,\n navigation,\n settingsSections,\n}: GenerateAppModuleOptions): string {\n const generatedLayouts = orderedLayouts(layouts);\n const layoutIndexes = new Map(generatedLayouts.map((layout, index) => [layout.id, index]));\n const layoutPaths = new Map(generatedLayouts.map((layout) => [layout.id, layout.path]));\n const imports = [\n ...generatedLayouts.map(\n (layout, index) => `import * as layout${index}Module from ${literal(layout.impl)};`,\n ),\n ...routes.map((route, index) => `import * as route${index}Module from ${literal(route.impl)};`),\n ].join('\\n');\n const layoutDeclarations = generatedLayouts\n .map(\n (layout, index) => `const layout${index} = createRoute({\n getParentRoute: () => ${parentExpression(layout.parent, layoutIndexes)},\n path: ${literal(routePathForParent(layout.parent, layout.path, layoutPaths))},\n ...routeOptions(layout${index}Module.default, ${literal(layout.impl)}, ${literal(layout.path)}),\n});`,\n )\n .join('\\n\\n');\n const routeDeclarations = routes\n .map(\n (route, index) => `const route${index} = createRoute({\n getParentRoute: () => ${parentExpression(route.parent, layoutIndexes)},\n path: ${literal(routePathForParent(route.parent, route.path, layoutPaths))},\n ...routeOptions(route${index}Module.default, ${literal(route.impl)}, ${literal(route.path)}),\n});`,\n )\n .join('\\n\\n');\n const layoutTreeDeclarations = generatedLayouts\n .map((layout, index) => ({layout, index}))\n .reverse()\n .map(({layout, index}) => {\n const routeChildren = routeNames(routes, layout.id);\n const children = [routeChildren, ...layoutTreeNames(generatedLayouts, layout.id)]\n .filter(Boolean)\n .join(',\\n ');\n return `const layout${index}Tree = layout${index}.addChildren([${children}]);`;\n })\n .join('\\n');\n const declarations = [layoutDeclarations, routeDeclarations, layoutTreeDeclarations]\n .filter(Boolean)\n .join('\\n\\n');\n const rootRoutes = routeNames(routes, 'root');\n const rootChildren = [rootRoutes, ...layoutTreeNames(generatedLayouts, 'root'), 'workspaceLayout']\n .filter(Boolean)\n .join(',\\n ');\n const projectChildren = [\n routeNames(routes, 'projectLayout'),\n ...layoutTreeNames(generatedLayouts, 'projectLayout'),\n ]\n .filter(Boolean)\n .join(',\\n ');\n const workspaceSettingsChildren = [\n routeNames(routes, 'workspaceSettings'),\n ...layoutTreeNames(generatedLayouts, 'workspaceSettings'),\n ]\n .filter(Boolean)\n .join(',\\n ');\n const workspaceChildren = [\n routeNames(routes, 'workspaceLayout'),\n ...layoutTreeNames(generatedLayouts, 'workspaceLayout'),\n 'projectLayout',\n 'workspaceSettings',\n ]\n .filter(Boolean)\n .join(',\\n ');\n\n return `// GENERATED by @shipfox/client-shell/vite. Do not edit.\n// biome-ignore-all format: generated code has stable, reviewable output.\n// biome-ignore-all assist/source/organizeImports: generated imports follow route order.\nimport {createRoute, createRouter} from '@tanstack/react-router';\nimport {buildAnchorSkeleton, isRouteImpl, type RouteImpl, type RouterContext} from '@shipfox/client-shell/runtime';\n${imports}\n\nfunction routeOptions<T extends RouteImpl>(routeImpl: T, impl: string, path: string): T['options'] {\n if (!isRouteImpl(routeImpl)) {\n throw new TypeError(\\`Route implementation \"\\${impl}\" for \"\\${path}\" must export default defineRoute(...).\\`);\n }\n return routeImpl.options;\n}\n\nconst skeleton = buildAnchorSkeleton({\n navigation: ${indentedLiteral(navigation, 2)},\n settingsSections: ${indentedLiteral(settingsSections, 2)},\n});\n\n${declarations}\n\nconst projectLayout = skeleton.projectLayout.addChildren([${projectChildren}]);\nconst workspaceSettings = skeleton.workspaceSettings.addChildren([${workspaceSettingsChildren}]);\nconst workspaceLayout = skeleton.workspaceLayout.addChildren([\n ${workspaceChildren},\n]);\n\nexport const routeTree = skeleton.rootRoute.addChildren([\n ${rootChildren},\n]);\n\nexport const router = createRouter({\n routeTree,\n context: {auth: undefined, queryClient: undefined} satisfies RouterContext,\n scrollRestoration: true,\n});\n\ndeclare module '@tanstack/react-router' {\n interface Register {\n router: typeof router;\n }\n}\n`;\n}\n"],"names":["routePathForParent","literal","value","JSON","stringify","undefined","indentedLiteral","indentation","replaceAll","repeat","isShellAnchor","parent","includes","orderedLayouts","layouts","byId","Map","map","layout","id","ordered","visiting","Set","visited","visit","has","Error","get","add","delete","push","routeNames","routes","route","index","filter","join","layoutTreeNames","parentExpression","layoutIndexes","generateAppModule","navigation","settingsSections","generatedLayouts","layoutPaths","path","imports","impl","layoutDeclarations","routeDeclarations","layoutTreeDeclarations","reverse","routeChildren","children","Boolean","declarations","rootRoutes","rootChildren","projectChildren","workspaceSettingsChildren","workspaceChildren"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/vite/plugin.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,MAAM,EAAiB,MAAM,MAAM,CAAC;AAOjD,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAID,wBAAgB,wBAAwB,CAAC,EACvC,QAAQ,EACR,GAAgC,GACjC,EAAE,+BAA+B,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/vite/plugin.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,MAAM,EAAiB,MAAM,MAAM,CAAC;AAOjD,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAID,wBAAgB,wBAAwB,CAAC,EACvC,QAAQ,EACR,GAAgC,GACjC,EAAE,+BAA+B,GAAG,MAAM,CA4E1C"}
|
package/dist/vite/plugin.js
CHANGED
|
@@ -9,8 +9,11 @@ export function shipfoxClientComposition({ features, out = './src/shipfox-app.ge
|
|
|
9
9
|
let watchedFiles = new Set();
|
|
10
10
|
const outputPath = ()=>resolve(config?.root ?? process.cwd(), out);
|
|
11
11
|
const featuresPath = ()=>resolve(config?.root ?? process.cwd(), features);
|
|
12
|
-
async function assertRoutesResolve(resolveRoute, routes) {
|
|
13
|
-
for (const route of
|
|
12
|
+
async function assertRoutesResolve(resolveRoute, routes, layouts) {
|
|
13
|
+
for (const route of [
|
|
14
|
+
...layouts,
|
|
15
|
+
...routes
|
|
16
|
+
]){
|
|
14
17
|
const resolvedRoute = await resolveRoute(route.impl, outputPath());
|
|
15
18
|
if (!resolvedRoute) {
|
|
16
19
|
throw new Error(`Could not resolve route implementation "${route.impl}" for "${route.path}".`);
|
|
@@ -20,10 +23,11 @@ export function shipfoxClientComposition({ features, out = './src/shipfox-app.ge
|
|
|
20
23
|
async function generate({ addWatchFile, resolveRoute }) {
|
|
21
24
|
const evaluated = await evaluateFeatures(featuresPath());
|
|
22
25
|
const composition = composeClientFeatures(evaluated.features);
|
|
23
|
-
await assertRoutesResolve(resolveRoute, composition.routes);
|
|
26
|
+
await assertRoutesResolve(resolveRoute, composition.routes, composition.layouts);
|
|
24
27
|
watchedFiles = new Set(evaluated.loadedFiles);
|
|
25
28
|
for (const file of watchedFiles)addWatchFile(file);
|
|
26
29
|
const output = generateAppModule({
|
|
30
|
+
layouts: composition.layouts,
|
|
27
31
|
routes: composition.routes,
|
|
28
32
|
navigation: composition.navigation,
|
|
29
33
|
settingsSections: composition.settingsSections
|
package/dist/vite/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/vite/plugin.ts"],"sourcesContent":["import {mkdir, readFile, realpath, writeFile} from 'node:fs/promises';\nimport {dirname, resolve} from 'node:path';\nimport type {Plugin, ResolvedConfig} from 'vite';\nimport {composeClientFeatures} from '#compose/compose-client-features.js';\nimport {evaluateFeatures} from './evaluate-features.js';\nimport {generateAppModule} from './generate.js';\n\nconst manifestPluginName = 'shipfox-client-manifest';\n\nexport interface ShipfoxClientCompositionOptions {\n features: string;\n out?: string;\n}\n\ntype RouteResolver = (source: string, importer?: string) => Promise<{id: string} | null>;\n\nexport function shipfoxClientComposition({\n features,\n out = './src/shipfox-app.gen.ts',\n}: ShipfoxClientCompositionOptions): Plugin {\n let config: ResolvedConfig | undefined;\n let watchedFiles = new Set<string>();\n\n const outputPath = () => resolve(config?.root ?? process.cwd(), out);\n const featuresPath = () => resolve(config?.root ?? process.cwd(), features);\n\n async function assertRoutesResolve(\n resolveRoute: RouteResolver,\n routes: ReturnType<typeof composeClientFeatures>['routes'],\n ): Promise<void> {\n for (const route of routes) {\n const resolvedRoute = await resolveRoute(route.impl, outputPath());\n if (!resolvedRoute) {\n throw new Error(\n `Could not resolve route implementation \"${route.impl}\" for \"${route.path}\".`,\n );\n }\n }\n }\n\n async function generate({\n addWatchFile,\n resolveRoute,\n }: {\n addWatchFile(file: string): void;\n resolveRoute: RouteResolver;\n }): Promise<void> {\n const evaluated = await evaluateFeatures(featuresPath());\n const composition = composeClientFeatures(evaluated.features);\n await assertRoutesResolve(resolveRoute, composition.routes);\n\n watchedFiles = new Set(evaluated.loadedFiles);\n for (const file of watchedFiles) addWatchFile(file);\n\n const output = generateAppModule({\n routes: composition.routes,\n navigation: composition.navigation,\n settingsSections: composition.settingsSections,\n });\n const path = outputPath();\n const existing = await readFile(path, 'utf8').catch(() => undefined);\n if (existing === output) return;\n await mkdir(dirname(path), {recursive: true});\n await writeFile(path, output);\n }\n\n return {\n name: 'shipfox-client-composition',\n configResolved(resolvedConfig) {\n config = resolvedConfig;\n if (!resolvedConfig.plugins.some(({name}) => name === manifestPluginName)) {\n resolvedConfig.logger.warn(\n `shipfoxClientComposition() is registered without the \"${manifestPluginName}\" plugin (shipfoxClientManifest()). Add shipfoxClientManifest() to the Vite plugins to serve Shipfox branding assets.`,\n );\n }\n },\n async buildStart() {\n await generate({\n addWatchFile: this.addWatchFile.bind(this),\n resolveRoute: this.resolve.bind(this),\n });\n },\n async hotUpdate({file, server}) {\n const resolvedFile = await realpath(file).catch(() => resolve(file));\n if (!watchedFiles.has(resolvedFile)) return;\n await generate({\n addWatchFile: server.watcher.add.bind(server.watcher),\n resolveRoute: this.environment.pluginContainer.resolveId.bind(\n this.environment.pluginContainer,\n ),\n });\n },\n };\n}\n"],"names":["mkdir","readFile","realpath","writeFile","dirname","resolve","composeClientFeatures","evaluateFeatures","generateAppModule","manifestPluginName","shipfoxClientComposition","features","out","config","watchedFiles","Set","outputPath","root","process","cwd","featuresPath","assertRoutesResolve","resolveRoute","routes","route","resolvedRoute","impl","Error","path","generate","addWatchFile","evaluated","composition","loadedFiles","file","output","navigation","settingsSections","existing","catch","undefined","recursive","name","configResolved","resolvedConfig","plugins","some","logger","warn","buildStart","bind","hotUpdate","server","resolvedFile","has","watcher","add","environment","pluginContainer","resolveId"],"mappings":"AAAA,SAAQA,KAAK,EAAEC,QAAQ,EAAEC,QAAQ,EAAEC,SAAS,QAAO,mBAAmB;AACtE,SAAQC,OAAO,EAAEC,OAAO,QAAO,YAAY;AAE3C,SAAQC,qBAAqB,QAAO,sCAAsC;AAC1E,SAAQC,gBAAgB,QAAO,yBAAyB;AACxD,SAAQC,iBAAiB,QAAO,gBAAgB;AAEhD,MAAMC,qBAAqB;AAS3B,OAAO,SAASC,yBAAyB,EACvCC,QAAQ,EACRC,MAAM,0BAA0B,EACA;IAChC,IAAIC;IACJ,IAAIC,eAAe,IAAIC;IAEvB,MAAMC,aAAa,IAAMX,QAAQQ,QAAQI,QAAQC,QAAQC,GAAG,IAAIP;IAChE,MAAMQ,eAAe,IAAMf,QAAQQ,QAAQI,QAAQC,QAAQC,GAAG,IAAIR;IAElE,eAAeU,oBACbC,YAA2B,EAC3BC,MAA0D;
|
|
1
|
+
{"version":3,"sources":["../../src/vite/plugin.ts"],"sourcesContent":["import {mkdir, readFile, realpath, writeFile} from 'node:fs/promises';\nimport {dirname, resolve} from 'node:path';\nimport type {Plugin, ResolvedConfig} from 'vite';\nimport {composeClientFeatures} from '#compose/compose-client-features.js';\nimport {evaluateFeatures} from './evaluate-features.js';\nimport {generateAppModule} from './generate.js';\n\nconst manifestPluginName = 'shipfox-client-manifest';\n\nexport interface ShipfoxClientCompositionOptions {\n features: string;\n out?: string;\n}\n\ntype RouteResolver = (source: string, importer?: string) => Promise<{id: string} | null>;\n\nexport function shipfoxClientComposition({\n features,\n out = './src/shipfox-app.gen.ts',\n}: ShipfoxClientCompositionOptions): Plugin {\n let config: ResolvedConfig | undefined;\n let watchedFiles = new Set<string>();\n\n const outputPath = () => resolve(config?.root ?? process.cwd(), out);\n const featuresPath = () => resolve(config?.root ?? process.cwd(), features);\n\n async function assertRoutesResolve(\n resolveRoute: RouteResolver,\n routes: ReturnType<typeof composeClientFeatures>['routes'],\n layouts: ReturnType<typeof composeClientFeatures>['layouts'],\n ): Promise<void> {\n for (const route of [...layouts, ...routes]) {\n const resolvedRoute = await resolveRoute(route.impl, outputPath());\n if (!resolvedRoute) {\n throw new Error(\n `Could not resolve route implementation \"${route.impl}\" for \"${route.path}\".`,\n );\n }\n }\n }\n\n async function generate({\n addWatchFile,\n resolveRoute,\n }: {\n addWatchFile(file: string): void;\n resolveRoute: RouteResolver;\n }): Promise<void> {\n const evaluated = await evaluateFeatures(featuresPath());\n const composition = composeClientFeatures(evaluated.features);\n await assertRoutesResolve(resolveRoute, composition.routes, composition.layouts);\n\n watchedFiles = new Set(evaluated.loadedFiles);\n for (const file of watchedFiles) addWatchFile(file);\n\n const output = generateAppModule({\n layouts: composition.layouts,\n routes: composition.routes,\n navigation: composition.navigation,\n settingsSections: composition.settingsSections,\n });\n const path = outputPath();\n const existing = await readFile(path, 'utf8').catch(() => undefined);\n if (existing === output) return;\n await mkdir(dirname(path), {recursive: true});\n await writeFile(path, output);\n }\n\n return {\n name: 'shipfox-client-composition',\n configResolved(resolvedConfig) {\n config = resolvedConfig;\n if (!resolvedConfig.plugins.some(({name}) => name === manifestPluginName)) {\n resolvedConfig.logger.warn(\n `shipfoxClientComposition() is registered without the \"${manifestPluginName}\" plugin (shipfoxClientManifest()). Add shipfoxClientManifest() to the Vite plugins to serve Shipfox branding assets.`,\n );\n }\n },\n async buildStart() {\n await generate({\n addWatchFile: this.addWatchFile.bind(this),\n resolveRoute: this.resolve.bind(this),\n });\n },\n async hotUpdate({file, server}) {\n const resolvedFile = await realpath(file).catch(() => resolve(file));\n if (!watchedFiles.has(resolvedFile)) return;\n await generate({\n addWatchFile: server.watcher.add.bind(server.watcher),\n resolveRoute: this.environment.pluginContainer.resolveId.bind(\n this.environment.pluginContainer,\n ),\n });\n },\n };\n}\n"],"names":["mkdir","readFile","realpath","writeFile","dirname","resolve","composeClientFeatures","evaluateFeatures","generateAppModule","manifestPluginName","shipfoxClientComposition","features","out","config","watchedFiles","Set","outputPath","root","process","cwd","featuresPath","assertRoutesResolve","resolveRoute","routes","layouts","route","resolvedRoute","impl","Error","path","generate","addWatchFile","evaluated","composition","loadedFiles","file","output","navigation","settingsSections","existing","catch","undefined","recursive","name","configResolved","resolvedConfig","plugins","some","logger","warn","buildStart","bind","hotUpdate","server","resolvedFile","has","watcher","add","environment","pluginContainer","resolveId"],"mappings":"AAAA,SAAQA,KAAK,EAAEC,QAAQ,EAAEC,QAAQ,EAAEC,SAAS,QAAO,mBAAmB;AACtE,SAAQC,OAAO,EAAEC,OAAO,QAAO,YAAY;AAE3C,SAAQC,qBAAqB,QAAO,sCAAsC;AAC1E,SAAQC,gBAAgB,QAAO,yBAAyB;AACxD,SAAQC,iBAAiB,QAAO,gBAAgB;AAEhD,MAAMC,qBAAqB;AAS3B,OAAO,SAASC,yBAAyB,EACvCC,QAAQ,EACRC,MAAM,0BAA0B,EACA;IAChC,IAAIC;IACJ,IAAIC,eAAe,IAAIC;IAEvB,MAAMC,aAAa,IAAMX,QAAQQ,QAAQI,QAAQC,QAAQC,GAAG,IAAIP;IAChE,MAAMQ,eAAe,IAAMf,QAAQQ,QAAQI,QAAQC,QAAQC,GAAG,IAAIR;IAElE,eAAeU,oBACbC,YAA2B,EAC3BC,MAA0D,EAC1DC,OAA4D;QAE5D,KAAK,MAAMC,SAAS;eAAID;eAAYD;SAAO,CAAE;YAC3C,MAAMG,gBAAgB,MAAMJ,aAAaG,MAAME,IAAI,EAAEX;YACrD,IAAI,CAACU,eAAe;gBAClB,MAAM,IAAIE,MACR,CAAC,wCAAwC,EAAEH,MAAME,IAAI,CAAC,OAAO,EAAEF,MAAMI,IAAI,CAAC,EAAE,CAAC;YAEjF;QACF;IACF;IAEA,eAAeC,SAAS,EACtBC,YAAY,EACZT,YAAY,EAIb;QACC,MAAMU,YAAY,MAAMzB,iBAAiBa;QACzC,MAAMa,cAAc3B,sBAAsB0B,UAAUrB,QAAQ;QAC5D,MAAMU,oBAAoBC,cAAcW,YAAYV,MAAM,EAAEU,YAAYT,OAAO;QAE/EV,eAAe,IAAIC,IAAIiB,UAAUE,WAAW;QAC5C,KAAK,MAAMC,QAAQrB,aAAciB,aAAaI;QAE9C,MAAMC,SAAS5B,kBAAkB;YAC/BgB,SAASS,YAAYT,OAAO;YAC5BD,QAAQU,YAAYV,MAAM;YAC1Bc,YAAYJ,YAAYI,UAAU;YAClCC,kBAAkBL,YAAYK,gBAAgB;QAChD;QACA,MAAMT,OAAOb;QACb,MAAMuB,WAAW,MAAMtC,SAAS4B,MAAM,QAAQW,KAAK,CAAC,IAAMC;QAC1D,IAAIF,aAAaH,QAAQ;QACzB,MAAMpC,MAAMI,QAAQyB,OAAO;YAACa,WAAW;QAAI;QAC3C,MAAMvC,UAAU0B,MAAMO;IACxB;IAEA,OAAO;QACLO,MAAM;QACNC,gBAAeC,cAAc;YAC3BhC,SAASgC;YACT,IAAI,CAACA,eAAeC,OAAO,CAACC,IAAI,CAAC,CAAC,EAACJ,IAAI,EAAC,GAAKA,SAASlC,qBAAqB;gBACzEoC,eAAeG,MAAM,CAACC,IAAI,CACxB,CAAC,sDAAsD,EAAExC,mBAAmB,qHAAqH,CAAC;YAEtM;QACF;QACA,MAAMyC;YACJ,MAAMpB,SAAS;gBACbC,cAAc,IAAI,CAACA,YAAY,CAACoB,IAAI,CAAC,IAAI;gBACzC7B,cAAc,IAAI,CAACjB,OAAO,CAAC8C,IAAI,CAAC,IAAI;YACtC;QACF;QACA,MAAMC,WAAU,EAACjB,IAAI,EAAEkB,MAAM,EAAC;YAC5B,MAAMC,eAAe,MAAMpD,SAASiC,MAAMK,KAAK,CAAC,IAAMnC,QAAQ8B;YAC9D,IAAI,CAACrB,aAAayC,GAAG,CAACD,eAAe;YACrC,MAAMxB,SAAS;gBACbC,cAAcsB,OAAOG,OAAO,CAACC,GAAG,CAACN,IAAI,CAACE,OAAOG,OAAO;gBACpDlC,cAAc,IAAI,CAACoC,WAAW,CAACC,eAAe,CAACC,SAAS,CAACT,IAAI,CAC3D,IAAI,CAACO,WAAW,CAACC,eAAe;YAEpC;QACF;IACF;AACF"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipfox/client-shell",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "12.0.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/ShipfoxHQ/shipfox.git",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"@tanstack/router-core": "^1.171.13",
|
|
39
39
|
"tsx": "^4.22.4",
|
|
40
40
|
"framer-motion": "^12.23.24",
|
|
41
|
-
"@shipfox/api-auth-dto": "10.
|
|
41
|
+
"@shipfox/api-auth-dto": "10.1.0",
|
|
42
42
|
"@shipfox/api-workspaces-dto": "10.0.0",
|
|
43
43
|
"@shipfox/client-api": "6.0.1",
|
|
44
44
|
"@shipfox/client-config": "6.0.2",
|