@ankhorage/navigator 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE +21 -0
  3. package/README.md +19 -0
  4. package/dist/definitions/NavigatorPlan.d.ts +37 -0
  5. package/dist/definitions/NavigatorPlan.d.ts.map +1 -0
  6. package/dist/definitions/NavigatorPlan.js +2 -0
  7. package/dist/definitions/NavigatorPlan.js.map +1 -0
  8. package/dist/expo-router/createNavigatorPlan.d.ts +9 -0
  9. package/dist/expo-router/createNavigatorPlan.d.ts.map +1 -0
  10. package/dist/expo-router/createNavigatorPlan.js +63 -0
  11. package/dist/expo-router/createNavigatorPlan.js.map +1 -0
  12. package/dist/expo-router/resolveTabsNavigatorPlan.d.ts +5 -0
  13. package/dist/expo-router/resolveTabsNavigatorPlan.d.ts.map +1 -0
  14. package/dist/expo-router/resolveTabsNavigatorPlan.js +68 -0
  15. package/dist/expo-router/resolveTabsNavigatorPlan.js.map +1 -0
  16. package/dist/index.d.ts +8 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +5 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/index.test.d.ts +2 -0
  21. package/dist/index.test.d.ts.map +1 -0
  22. package/dist/index.test.js +107 -0
  23. package/dist/index.test.js.map +1 -0
  24. package/dist/metadata/index.d.ts +30 -0
  25. package/dist/metadata/index.d.ts.map +1 -0
  26. package/dist/metadata/index.js +31 -0
  27. package/dist/metadata/index.js.map +1 -0
  28. package/dist/presentation/resolveCustomTabsPresentation.d.ts +9 -0
  29. package/dist/presentation/resolveCustomTabsPresentation.d.ts.map +1 -0
  30. package/dist/presentation/resolveCustomTabsPresentation.js +32 -0
  31. package/dist/presentation/resolveCustomTabsPresentation.js.map +1 -0
  32. package/dist/topology/resolveNavigatorPreset.d.ts +4 -0
  33. package/dist/topology/resolveNavigatorPreset.d.ts.map +1 -0
  34. package/dist/topology/resolveNavigatorPreset.js +34 -0
  35. package/dist/topology/resolveNavigatorPreset.js.map +1 -0
  36. package/package.json +76 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ # @ankhorage/navigator
2
+
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 4439933: Establish the standalone Navigator capability around `AppNavigatorManifest`, add canonical topology preset planning, adaptive native/Web tab planning, responsive custom Web presentations, and serializable authoring metadata.
8
+
9
+ All notable changes to this package will be documented in this file by Changesets.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ankhorage
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,19 @@
1
+ <!-- markdownlint-disable MD013 MD033 -->
2
+ <!-- This file is generated by Paradox. Do not edit manually. -->
3
+
4
+ # NAVIGATOR
5
+
6
+ ![license: MIT](././paradox/badges/license.svg) ![npm: v0.1.0](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: checked](././paradox/badges/tests.svg) ![docs: paradox](././paradox/badges/docs.svg)
7
+
8
+ Standalone manifest-driven navigator capability for Expo Router across React Native and React Native Web.
9
+
10
+ ## Generated documentation
11
+
12
+ - [Interactive documentation app](././paradox/index.html)
13
+ - [Public API reference](././paradox/exports.md)
14
+ - [Component registry](././paradox/components.md)
15
+ - [Architecture overview](././paradox/diagrams/architecture-overview.mmd)
16
+ - [Module relationships](././paradox/diagrams/module-relationships.mmd)
17
+ - [Export graph](././paradox/diagrams/export-graph.mmd)
18
+ - [resolveCustomTabsPresentation sequence](././paradox/diagrams/sequences/resolve-custom-tabs-presentation.mmd)
19
+ - [resolveTabsNavigatorPlan sequence](././paradox/diagrams/sequences/resolve-tabs-navigator-plan.mmd)
@@ -0,0 +1,37 @@
1
+ import type { NavigatorType } from '@ankhorage/contracts/navigator';
2
+ export type NavigatorRuntimePlatform = 'android' | 'ios' | 'web';
3
+ export type NavigatorResponsiveSize = 'compact' | 'medium' | 'expanded';
4
+ export type NavigatorApiStability = 'stable' | 'unstable';
5
+ export type ExpoRouterNavigatorModule = 'expo-router' | 'expo-router/drawer' | 'expo-router/js-tabs' | 'expo-router/js-top-tabs' | 'expo-router/ui' | 'expo-router/unstable-native-tabs';
6
+ export type ResolvedTabsImplementation = 'custom' | 'javascript' | 'native';
7
+ export type ResolvedTabsPresentation = 'bottom' | 'top' | 'rail' | 'sidebar' | 'custom';
8
+ export interface TabsNavigatorPlan {
9
+ implementation: ResolvedTabsImplementation;
10
+ module: ExpoRouterNavigatorModule;
11
+ exportName: string;
12
+ stability: NavigatorApiStability;
13
+ presentation?: ResolvedTabsPresentation;
14
+ customPresentationId?: string;
15
+ }
16
+ export interface NavigatorRoutePlan {
17
+ name: string;
18
+ path?: string;
19
+ screenId?: string;
20
+ navigator?: NavigatorNodePlan;
21
+ }
22
+ export interface NavigatorNodePlan {
23
+ type: NavigatorType;
24
+ module: ExpoRouterNavigatorModule;
25
+ exportName: string;
26
+ routes: NavigatorRoutePlan[];
27
+ tabs?: TabsNavigatorPlan;
28
+ }
29
+ export interface NavigatorPlan {
30
+ presetLayers: readonly NavigatorType[];
31
+ root: NavigatorNodePlan;
32
+ flows: {
33
+ onboarding: boolean;
34
+ authentication: boolean;
35
+ };
36
+ }
37
+ //# sourceMappingURL=NavigatorPlan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavigatorPlan.d.ts","sourceRoot":"","sources":["../../src/definitions/NavigatorPlan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAEpE,MAAM,MAAM,wBAAwB,GAAG,SAAS,GAAG,KAAK,GAAG,KAAK,CAAC;AACjE,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,QAAQ,GAAG,UAAU,CAAC;AACxE,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE1D,MAAM,MAAM,yBAAyB,GACjC,aAAa,GACb,oBAAoB,GACpB,qBAAqB,GACrB,yBAAyB,GACzB,gBAAgB,GAChB,kCAAkC,CAAC;AAEvC,MAAM,MAAM,0BAA0B,GAAG,QAAQ,GAAG,YAAY,GAAG,QAAQ,CAAC;AAC5E,MAAM,MAAM,wBAAwB,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;AAExF,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,0BAA0B,CAAC;IAC3C,MAAM,EAAE,yBAAyB,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,qBAAqB,CAAC;IACjC,YAAY,CAAC,EAAE,wBAAwB,CAAC;IACxC,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,iBAAiB,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,yBAAyB,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC7B,IAAI,CAAC,EAAE,iBAAiB,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,SAAS,aAAa,EAAE,CAAC;IACvC,IAAI,EAAE,iBAAiB,CAAC;IACxB,KAAK,EAAE;QACL,UAAU,EAAE,OAAO,CAAC;QACpB,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC;CACH"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=NavigatorPlan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NavigatorPlan.js","sourceRoot":"","sources":["../../src/definitions/NavigatorPlan.ts"],"names":[],"mappings":""}
@@ -0,0 +1,9 @@
1
+ import type { AppNavigatorManifest } from '@ankhorage/contracts/navigator';
2
+ import type { NavigatorPlan, NavigatorResponsiveSize, NavigatorRuntimePlatform } from '../definitions/NavigatorPlan';
3
+ export interface CreateNavigatorPlanOptions {
4
+ platform: NavigatorRuntimePlatform;
5
+ responsiveSize?: NavigatorResponsiveSize;
6
+ }
7
+ /*** Create the complete standalone Navigator plan from only the navigator desired-state slice. */
8
+ export declare function createNavigatorPlan(manifest: AppNavigatorManifest, options: CreateNavigatorPlanOptions): NavigatorPlan;
9
+ //# sourceMappingURL=createNavigatorPlan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createNavigatorPlan.d.ts","sourceRoot":"","sources":["../../src/expo-router/createNavigatorPlan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EAKrB,MAAM,gCAAgC,CAAC;AAExC,OAAO,KAAK,EAEV,aAAa,EACb,uBAAuB,EACvB,wBAAwB,EACzB,MAAM,8BAA8B,CAAC;AAItC,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,wBAAwB,CAAC;IACnC,cAAc,CAAC,EAAE,uBAAuB,CAAC;CAC1C;AA0ED,kGAAkG;AAClG,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,oBAAoB,EAC9B,OAAO,EAAE,0BAA0B,GAClC,aAAa,CAUf"}
@@ -0,0 +1,63 @@
1
+ import { resolveNavigatorPreset } from '../topology/resolveNavigatorPreset';
2
+ import { resolveTabsNavigatorPlan } from './resolveTabsNavigatorPlan';
3
+ /*** Return node-local tabs configuration only when the node explicitly authors one. */
4
+ function resolveNodeTabsConfig(node) {
5
+ if (node.implementation !== undefined)
6
+ return node;
7
+ return node.native !== undefined || node.web !== undefined ? node : undefined;
8
+ }
9
+ /*** Resolve a platform-specific tabs override without dynamic object access. */
10
+ function resolvePlatformTabsConfig(platforms, platform) {
11
+ switch (platform) {
12
+ case 'android':
13
+ return platforms?.android?.tabs;
14
+ case 'ios':
15
+ return platforms?.ios?.tabs;
16
+ case 'web':
17
+ return platforms?.web?.tabs;
18
+ }
19
+ }
20
+ /*** Resolve effective tabs configuration using platform, node, default, then adaptive fallback. */
21
+ function resolveEffectiveTabsConfig(manifest, node, platform) {
22
+ return (resolvePlatformTabsConfig(manifest.platforms, platform) ??
23
+ resolveNodeTabsConfig(node) ??
24
+ manifest.defaults?.tabs);
25
+ }
26
+ /*** Convert one navigator node into a provider-aware Expo Router generation plan. */
27
+ function createNodePlan(manifest, node, platform, responsiveSize) {
28
+ const routes = node.routes.map((route) => ({
29
+ name: route.name,
30
+ ...(route.path === undefined ? {} : { path: route.path }),
31
+ ...(route.screenId === undefined ? {} : { screenId: route.screenId }),
32
+ ...(route.navigator === undefined
33
+ ? {}
34
+ : { navigator: createNodePlan(manifest, route.navigator, platform, responsiveSize) }),
35
+ }));
36
+ if (node.type === 'stack') {
37
+ return { type: 'stack', module: 'expo-router', exportName: 'Stack', routes };
38
+ }
39
+ if (node.type === 'drawer') {
40
+ return { type: 'drawer', module: 'expo-router/drawer', exportName: 'Drawer', routes };
41
+ }
42
+ const tabs = resolveTabsNavigatorPlan(resolveEffectiveTabsConfig(manifest, node, platform), platform, responsiveSize);
43
+ return {
44
+ type: 'tabs',
45
+ module: tabs.module,
46
+ exportName: tabs.exportName,
47
+ routes,
48
+ tabs,
49
+ };
50
+ }
51
+ /*** Create the complete standalone Navigator plan from only the navigator desired-state slice. */
52
+ export function createNavigatorPlan(manifest, options) {
53
+ const responsiveSize = options.responsiveSize ?? 'compact';
54
+ return {
55
+ presetLayers: resolveNavigatorPreset(manifest.preset, manifest.type),
56
+ root: createNodePlan(manifest, manifest, options.platform, responsiveSize),
57
+ flows: {
58
+ onboarding: manifest.flows?.onboarding ?? false,
59
+ authentication: manifest.flows?.authentication ?? false,
60
+ },
61
+ };
62
+ }
63
+ //# sourceMappingURL=createNavigatorPlan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createNavigatorPlan.js","sourceRoot":"","sources":["../../src/expo-router/createNavigatorPlan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { TabsImplementationConfig } from '@ankhorage/contracts/navigator';
2
+ import type { NavigatorResponsiveSize, NavigatorRuntimePlatform, TabsNavigatorPlan } from '../definitions/NavigatorPlan';
3
+ /*** Resolve the Expo Router module/export and presentation for one tabs implementation. */
4
+ export declare function resolveTabsNavigatorPlan(config: TabsImplementationConfig | undefined, platform: NavigatorRuntimePlatform, size: NavigatorResponsiveSize): TabsNavigatorPlan;
5
+ //# sourceMappingURL=resolveTabsNavigatorPlan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveTabsNavigatorPlan.d.ts","sourceRoot":"","sources":["../../src/expo-router/resolveTabsNavigatorPlan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAIV,wBAAwB,EACzB,MAAM,gCAAgC,CAAC;AAExC,OAAO,KAAK,EACV,uBAAuB,EACvB,wBAAwB,EACxB,iBAAiB,EAClB,MAAM,8BAA8B,CAAC;AAkEtC,2FAA2F;AAC3F,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,wBAAwB,GAAG,SAAS,EAC5C,QAAQ,EAAE,wBAAwB,EAClC,IAAI,EAAE,uBAAuB,GAC5B,iBAAiB,CAcnB"}
@@ -0,0 +1,68 @@
1
+ import { resolveCustomTabsPresentation } from '../presentation/resolveCustomTabsPresentation';
2
+ const DEFAULT_WEB_CUSTOM_TABS = {
3
+ presentation: 'responsive',
4
+ };
5
+ /*** Return the adaptive config when omission or `adaptive` selects that branch. */
6
+ function resolveAdaptiveConfig(config) {
7
+ if (config === undefined)
8
+ return undefined;
9
+ return config.implementation === undefined || config.implementation === 'adaptive'
10
+ ? config
11
+ : undefined;
12
+ }
13
+ /*** Create the SDK 57 native-tabs plan and reject unsupported Web usage. */
14
+ function createNativeTabsPlan(platform) {
15
+ if (platform === 'web') {
16
+ throw new Error('Native tabs are not available for the Web navigator plan.');
17
+ }
18
+ return {
19
+ implementation: 'native',
20
+ module: 'expo-router/unstable-native-tabs',
21
+ exportName: 'NativeTabs',
22
+ stability: 'unstable',
23
+ };
24
+ }
25
+ /*** Create the stable JavaScript tabs plan for bottom or top presentation. */
26
+ function createJavaScriptTabsPlan(config) {
27
+ const top = config.presentation === 'top';
28
+ return {
29
+ implementation: 'javascript',
30
+ module: top ? 'expo-router/js-top-tabs' : 'expo-router/js-tabs',
31
+ exportName: top ? 'TopTabs' : 'Tabs',
32
+ stability: 'stable',
33
+ presentation: top ? 'top' : 'bottom',
34
+ };
35
+ }
36
+ /*** Create the stable headless custom-tabs plan for Web or explicit custom use. */
37
+ function createCustomTabsPlan(config, size) {
38
+ return {
39
+ implementation: 'custom',
40
+ module: 'expo-router/ui',
41
+ exportName: 'Tabs',
42
+ stability: 'stable',
43
+ ...resolveCustomTabsPresentation(config, size),
44
+ };
45
+ }
46
+ /*** Resolve the adaptive tabs plan for the selected runtime platform. */
47
+ function createAdaptiveTabsPlan(config, platform, size) {
48
+ if (platform !== 'web')
49
+ return createNativeTabsPlan(platform);
50
+ return createCustomTabsPlan(resolveAdaptiveConfig(config)?.web ?? DEFAULT_WEB_CUSTOM_TABS, size);
51
+ }
52
+ /*** Resolve the Expo Router module/export and presentation for one tabs implementation. */
53
+ export function resolveTabsNavigatorPlan(config, platform, size) {
54
+ if (config === undefined)
55
+ return createAdaptiveTabsPlan(config, platform, size);
56
+ switch (config.implementation) {
57
+ case undefined:
58
+ case 'adaptive':
59
+ return createAdaptiveTabsPlan(config, platform, size);
60
+ case 'native':
61
+ return createNativeTabsPlan(platform);
62
+ case 'javascript':
63
+ return createJavaScriptTabsPlan(config);
64
+ case 'custom':
65
+ return createCustomTabsPlan(config, size);
66
+ }
67
+ }
68
+ //# sourceMappingURL=resolveTabsNavigatorPlan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveTabsNavigatorPlan.js","sourceRoot":"","sources":["../../src/expo-router/resolveTabsNavigatorPlan.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,6BAA6B,EAAE,MAAM,+CAA+C,CAAC;AAE9F,MAAM,uBAAuB,GAAG;IAC9B,YAAY,EAAE,YAAY;CAClB,CAAC;AAEX,mFAAmF;AACnF,SAAS,qBAAqB,CAC5B,MAA4C;IAE5C,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC3C,OAAO,MAAM,CAAC,cAAc,KAAK,SAAS,IAAI,MAAM,CAAC,cAAc,KAAK,UAAU;QAChF,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC;AAED,4EAA4E;AAC5E,SAAS,oBAAoB,CAAC,QAAkC;IAC9D,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;IAC/E,CAAC;IACD,OAAO;QACL,cAAc,EAAE,QAAQ;QACxB,MAAM,EAAE,kCAAkC;QAC1C,UAAU,EAAE,YAAY;QACxB,SAAS,EAAE,UAAU;KACtB,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,SAAS,wBAAwB,CAAC,MAA4B;IAC5D,MAAM,GAAG,GAAG,MAAM,CAAC,YAAY,KAAK,KAAK,CAAC;IAC1C,OAAO;QACL,cAAc,EAAE,YAAY;QAC5B,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,qBAAqB;QAC/D,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM;QACpC,SAAS,EAAE,QAAQ;QACnB,YAAY,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ;KACrC,CAAC;AACJ,CAAC;AAED,mFAAmF;AACnF,SAAS,oBAAoB,CAC3B,MAAgD,EAChD,IAA6B;IAE7B,OAAO;QACL,cAAc,EAAE,QAAQ;QACxB,MAAM,EAAE,gBAAgB;QACxB,UAAU,EAAE,MAAM;QAClB,SAAS,EAAE,QAAQ;QACnB,GAAG,6BAA6B,CAAC,MAAM,EAAE,IAAI,CAAC;KAC/C,CAAC;AACJ,CAAC;AAED,yEAAyE;AACzE,SAAS,sBAAsB,CAC7B,MAA4C,EAC5C,QAAkC,EAClC,IAA6B;IAE7B,IAAI,QAAQ,KAAK,KAAK;QAAE,OAAO,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IAC9D,OAAO,oBAAoB,CAAC,qBAAqB,CAAC,MAAM,CAAC,EAAE,GAAG,IAAI,uBAAuB,EAAE,IAAI,CAAC,CAAC;AACnG,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,wBAAwB,CACtC,MAA4C,EAC5C,QAAkC,EAClC,IAA6B;IAE7B,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,sBAAsB,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;IAEhF,QAAQ,MAAM,CAAC,cAAc,EAAE,CAAC;QAC9B,KAAK,SAAS,CAAC;QACf,KAAK,UAAU;YACb,OAAO,sBAAsB,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;QACxD,KAAK,QAAQ;YACX,OAAO,oBAAoB,CAAC,QAAQ,CAAC,CAAC;QACxC,KAAK,YAAY;YACf,OAAO,wBAAwB,CAAC,MAAM,CAAC,CAAC;QAC1C,KAAK,QAAQ;YACX,OAAO,oBAAoB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC9C,CAAC;AACH,CAAC"}
@@ -0,0 +1,8 @@
1
+ export type { ExpoRouterNavigatorModule, NavigatorApiStability, NavigatorNodePlan, NavigatorPlan, NavigatorResponsiveSize, NavigatorRoutePlan, NavigatorRuntimePlatform, ResolvedTabsImplementation, ResolvedTabsPresentation, TabsNavigatorPlan, } from './definitions/NavigatorPlan';
2
+ export type { CreateNavigatorPlanOptions } from './expo-router/createNavigatorPlan';
3
+ export { createNavigatorPlan } from './expo-router/createNavigatorPlan';
4
+ export { resolveTabsNavigatorPlan } from './expo-router/resolveTabsNavigatorPlan';
5
+ export type { ResolvedCustomTabsPresentation } from './presentation/resolveCustomTabsPresentation';
6
+ export { resolveCustomTabsPresentation } from './presentation/resolveCustomTabsPresentation';
7
+ export { resolveNavigatorPreset } from './topology/resolveNavigatorPreset';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,yBAAyB,EACzB,qBAAqB,EACrB,iBAAiB,EACjB,aAAa,EACb,uBAAuB,EACvB,kBAAkB,EAClB,wBAAwB,EACxB,0BAA0B,EAC1B,wBAAwB,EACxB,iBAAiB,GAClB,MAAM,6BAA6B,CAAC;AACrC,YAAY,EAAE,0BAA0B,EAAE,MAAM,mCAAmC,CAAC;AACpF,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAC;AAClF,YAAY,EAAE,8BAA8B,EAAE,MAAM,8CAA8C,CAAC;AACnG,OAAO,EAAE,6BAA6B,EAAE,MAAM,8CAA8C,CAAC;AAC7F,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { createNavigatorPlan } from './expo-router/createNavigatorPlan';
2
+ export { resolveTabsNavigatorPlan } from './expo-router/resolveTabsNavigatorPlan';
3
+ export { resolveCustomTabsPresentation } from './presentation/resolveCustomTabsPresentation';
4
+ export { resolveNavigatorPreset } from './topology/resolveNavigatorPreset';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAC;AAElF,OAAO,EAAE,6BAA6B,EAAE,MAAM,8CAA8C,CAAC;AAC7F,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,107 @@
1
+ import { readdir, readFile } from 'node:fs/promises';
2
+ import { extname, join } from 'node:path';
3
+ import { describe, expect, test } from 'bun:test';
4
+ import { createNavigatorPlan, resolveCustomTabsPresentation, resolveNavigatorPreset, resolveTabsNavigatorPlan, } from './index';
5
+ import { NAVIGATOR_PACKAGE_METADATA } from './metadata';
6
+ const ADAPTIVE_TABS = {
7
+ type: 'tabs',
8
+ preset: 'root-stack-tabs-stack',
9
+ routes: [{ name: 'home', path: '/', screenId: 'home' }],
10
+ };
11
+ async function collectProductionTypeScriptFiles(directory) {
12
+ const entries = await readdir(directory, { withFileTypes: true });
13
+ const nested = await Promise.all(entries.map(async (entry) => {
14
+ const full = join(directory, entry.name);
15
+ if (entry.isDirectory())
16
+ return collectProductionTypeScriptFiles(full);
17
+ return ['.ts', '.tsx'].includes(extname(entry.name)) && !entry.name.endsWith('.test.ts')
18
+ ? [full]
19
+ : [];
20
+ }));
21
+ return nested.flat();
22
+ }
23
+ describe('@ankhorage/navigator topology planning', () => {
24
+ test('resolves canonical topology presets without changing the manifest tree', () => {
25
+ expect(resolveNavigatorPreset('root-stack-tabs-stack', 'tabs')).toEqual([
26
+ 'stack',
27
+ 'tabs',
28
+ 'stack',
29
+ ]);
30
+ });
31
+ test('creates a plan from only the navigator manifest slice', () => {
32
+ const plan = createNavigatorPlan(ADAPTIVE_TABS, { platform: 'web', responsiveSize: 'medium' });
33
+ expect(plan.presetLayers).toEqual(['stack', 'tabs', 'stack']);
34
+ expect(plan.root.type).toBe('tabs');
35
+ expect(plan.root.tabs?.implementation).toBe('custom');
36
+ expect(plan.root.tabs?.presentation).toBe('rail');
37
+ });
38
+ });
39
+ describe('@ankhorage/navigator tabs planning', () => {
40
+ test('uses native unstable tabs for adaptive Android and iOS', () => {
41
+ for (const platform of ['android', 'ios']) {
42
+ expect(resolveTabsNavigatorPlan(undefined, platform, 'compact')).toEqual({
43
+ implementation: 'native',
44
+ module: 'expo-router/unstable-native-tabs',
45
+ exportName: 'NativeTabs',
46
+ stability: 'unstable',
47
+ });
48
+ }
49
+ });
50
+ test('uses headless custom tabs on Web with responsive defaults', () => {
51
+ expect(resolveTabsNavigatorPlan(undefined, 'web', 'compact').presentation).toBe('bottom');
52
+ expect(resolveTabsNavigatorPlan(undefined, 'web', 'medium').presentation).toBe('rail');
53
+ expect(resolveTabsNavigatorPlan(undefined, 'web', 'expanded').presentation).toBe('sidebar');
54
+ });
55
+ test('preserves Web config when adaptive implementation is omitted', () => {
56
+ const plan = resolveTabsNavigatorPlan({
57
+ web: {
58
+ presentation: 'responsive',
59
+ responsive: { compact: 'top', expanded: 'sidebar' },
60
+ },
61
+ }, 'web', 'compact');
62
+ expect(plan.presentation).toBe('top');
63
+ });
64
+ test('maps JavaScript bottom and top tabs to their stable Router entry points', () => {
65
+ expect(resolveTabsNavigatorPlan({ implementation: 'javascript' }, 'web', 'compact').module).toBe('expo-router/js-tabs');
66
+ expect(resolveTabsNavigatorPlan({ implementation: 'javascript', presentation: 'top' }, 'web', 'compact').module).toBe('expo-router/js-top-tabs');
67
+ });
68
+ });
69
+ describe('@ankhorage/navigator explicit tabs planning', () => {
70
+ test('maps custom tabs to the stable headless Router entry point', () => {
71
+ expect(resolveTabsNavigatorPlan({ implementation: 'custom', presentation: 'sidebar' }, 'web', 'expanded')).toEqual({
72
+ implementation: 'custom',
73
+ module: 'expo-router/ui',
74
+ exportName: 'Tabs',
75
+ stability: 'stable',
76
+ presentation: 'sidebar',
77
+ });
78
+ });
79
+ test('rejects explicit native tabs for Web', () => {
80
+ expect(() => resolveTabsNavigatorPlan({ implementation: 'native' }, 'web', 'compact')).toThrow('Native tabs are not available');
81
+ });
82
+ });
83
+ describe('@ankhorage/navigator custom presentation', () => {
84
+ test('resolves registered custom presentations without embedding a renderer', () => {
85
+ expect(resolveCustomTabsPresentation({ presentation: 'custom', customPresentationId: 'workspace-tabs' }, 'expanded')).toEqual({ presentation: 'custom', customPresentationId: 'workspace-tabs' });
86
+ });
87
+ test('uses expanded presentation when a responsive medium override is omitted', () => {
88
+ expect(resolveCustomTabsPresentation({
89
+ presentation: 'responsive',
90
+ responsive: { compact: 'bottom', expanded: 'sidebar' },
91
+ }, 'medium').presentation).toBe('sidebar');
92
+ });
93
+ });
94
+ describe('@ankhorage/navigator package boundary', () => {
95
+ test('publishes manifest-authoring metadata for the standalone capability', () => {
96
+ expect(NAVIGATOR_PACKAGE_METADATA.packageName).toBe('@ankhorage/navigator');
97
+ expect(NAVIGATOR_PACKAGE_METADATA.manifestProperty).toBe('navigator');
98
+ expect(NAVIGATOR_PACKAGE_METADATA.tabs.adaptiveDefault.webResponsive.medium).toBe('rail');
99
+ });
100
+ test('never imports the full app manifest into production source', async () => {
101
+ const forbidden = ['App', 'Manifest'].join('');
102
+ const files = await collectProductionTypeScriptFiles(join(process.cwd(), 'src'));
103
+ const contents = await Promise.all(files.map((file) => readFile(file, 'utf8')));
104
+ expect(contents.some((content) => content.includes(forbidden))).toBe(false);
105
+ });
106
+ });
107
+ //# sourceMappingURL=index.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ export declare const NAVIGATOR_PACKAGE_METADATA: {
2
+ readonly packageName: "@ankhorage/navigator";
3
+ readonly manifestProperty: "navigator";
4
+ readonly contractSubpath: "@ankhorage/contracts/navigator";
5
+ readonly navigatorTypes: readonly ["stack", "tabs", "drawer"];
6
+ readonly presets: readonly ["stack", "tabs", "tabs-stack", "drawer", "drawer-stack", "drawer-tabs", "drawer-tabs-stack", "root-stack-tabs", "root-stack-tabs-stack", "root-stack-drawer", "root-stack-drawer-stack", "root-stack-drawer-tabs", "root-stack-drawer-tabs-stack"];
7
+ readonly tabs: {
8
+ readonly implementations: readonly ["adaptive", "javascript", "native", "custom"];
9
+ readonly customPresentations: readonly ["bottom", "top", "rail", "sidebar", "responsive", "custom"];
10
+ readonly fixedCustomPresentations: readonly ["bottom", "top", "rail", "sidebar"];
11
+ readonly adaptiveDefault: {
12
+ readonly android: "native";
13
+ readonly ios: "native";
14
+ readonly web: "custom";
15
+ readonly webResponsive: {
16
+ readonly compact: "bottom";
17
+ readonly medium: "rail";
18
+ readonly expanded: "sidebar";
19
+ };
20
+ };
21
+ readonly expoRouter: {
22
+ readonly javascriptBottom: "expo-router/js-tabs";
23
+ readonly javascriptTop: "expo-router/js-top-tabs";
24
+ readonly custom: "expo-router/ui";
25
+ readonly native: "expo-router/unstable-native-tabs";
26
+ readonly nativeStability: "unstable";
27
+ };
28
+ };
29
+ };
30
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/metadata/index.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4B7B,CAAC"}
@@ -0,0 +1,31 @@
1
+ import { CUSTOM_TABS_PRESENTATIONS, FIXED_CUSTOM_TABS_PRESENTATIONS, NAVIGATOR_PRESETS, NAVIGATOR_TYPES, } from '@ankhorage/contracts/navigator';
2
+ export const NAVIGATOR_PACKAGE_METADATA = {
3
+ packageName: '@ankhorage/navigator',
4
+ manifestProperty: 'navigator',
5
+ contractSubpath: '@ankhorage/contracts/navigator',
6
+ navigatorTypes: NAVIGATOR_TYPES,
7
+ presets: NAVIGATOR_PRESETS,
8
+ tabs: {
9
+ implementations: ['adaptive', 'javascript', 'native', 'custom'],
10
+ customPresentations: CUSTOM_TABS_PRESENTATIONS,
11
+ fixedCustomPresentations: FIXED_CUSTOM_TABS_PRESENTATIONS,
12
+ adaptiveDefault: {
13
+ android: 'native',
14
+ ios: 'native',
15
+ web: 'custom',
16
+ webResponsive: {
17
+ compact: 'bottom',
18
+ medium: 'rail',
19
+ expanded: 'sidebar',
20
+ },
21
+ },
22
+ expoRouter: {
23
+ javascriptBottom: 'expo-router/js-tabs',
24
+ javascriptTop: 'expo-router/js-top-tabs',
25
+ custom: 'expo-router/ui',
26
+ native: 'expo-router/unstable-native-tabs',
27
+ nativeStability: 'unstable',
28
+ },
29
+ },
30
+ };
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/metadata/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,yBAAyB,EACzB,+BAA+B,EAC/B,iBAAiB,EACjB,eAAe,GAChB,MAAM,gCAAgC,CAAC;AAExC,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,WAAW,EAAE,sBAAsB;IACnC,gBAAgB,EAAE,WAAW;IAC7B,eAAe,EAAE,gCAAgC;IACjD,cAAc,EAAE,eAAe;IAC/B,OAAO,EAAE,iBAAiB;IAC1B,IAAI,EAAE;QACJ,eAAe,EAAE,CAAC,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,QAAQ,CAAC;QAC/D,mBAAmB,EAAE,yBAAyB;QAC9C,wBAAwB,EAAE,+BAA+B;QACzD,eAAe,EAAE;YACf,OAAO,EAAE,QAAQ;YACjB,GAAG,EAAE,QAAQ;YACb,GAAG,EAAE,QAAQ;YACb,aAAa,EAAE;gBACb,OAAO,EAAE,QAAQ;gBACjB,MAAM,EAAE,MAAM;gBACd,QAAQ,EAAE,SAAS;aACpB;SACF;QACD,UAAU,EAAE;YACV,gBAAgB,EAAE,qBAAqB;YACvC,aAAa,EAAE,yBAAyB;YACxC,MAAM,EAAE,gBAAgB;YACxB,MAAM,EAAE,kCAAkC;YAC1C,eAAe,EAAE,UAAU;SAC5B;KACF;CACO,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { CustomTabsConfig } from '@ankhorage/contracts/navigator';
2
+ import type { NavigatorResponsiveSize, ResolvedTabsPresentation } from '../definitions/NavigatorPlan';
3
+ export interface ResolvedCustomTabsPresentation {
4
+ presentation: ResolvedTabsPresentation;
5
+ customPresentationId?: string;
6
+ }
7
+ /*** Resolve one custom-tabs presentation for the current semantic responsive size. */
8
+ export declare function resolveCustomTabsPresentation(config: Omit<CustomTabsConfig, 'implementation'>, size: NavigatorResponsiveSize): ResolvedCustomTabsPresentation;
9
+ //# sourceMappingURL=resolveCustomTabsPresentation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveCustomTabsPresentation.d.ts","sourceRoot":"","sources":["../../src/presentation/resolveCustomTabsPresentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAGjB,MAAM,gCAAgC,CAAC;AAExC,OAAO,KAAK,EACV,uBAAuB,EACvB,wBAAwB,EACzB,MAAM,8BAA8B,CAAC;AAQtC,MAAM,WAAW,8BAA8B;IAC7C,YAAY,EAAE,wBAAwB,CAAC;IACvC,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAiBD,sFAAsF;AACtF,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,IAAI,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,EAChD,IAAI,EAAE,uBAAuB,GAC5B,8BAA8B,CAkBhC"}
@@ -0,0 +1,32 @@
1
+ const DEFAULT_RESPONSIVE_PRESENTATION = {
2
+ compact: 'bottom',
3
+ medium: 'rail',
4
+ expanded: 'sidebar',
5
+ };
6
+ /*** Resolve the fixed presentation for one semantic responsive size. */
7
+ function resolveResponsivePresentation(mapping, size) {
8
+ switch (size) {
9
+ case 'compact':
10
+ return mapping.compact;
11
+ case 'medium':
12
+ return mapping.medium ?? mapping.expanded;
13
+ case 'expanded':
14
+ return mapping.expanded;
15
+ }
16
+ }
17
+ /*** Resolve one custom-tabs presentation for the current semantic responsive size. */
18
+ export function resolveCustomTabsPresentation(config, size) {
19
+ if (config.presentation === 'custom') {
20
+ if (!config.customPresentationId) {
21
+ throw new Error('Custom tabs presentation requires customPresentationId.');
22
+ }
23
+ return { presentation: 'custom', customPresentationId: config.customPresentationId };
24
+ }
25
+ if (config.presentation === 'responsive') {
26
+ return {
27
+ presentation: resolveResponsivePresentation(config.responsive ?? DEFAULT_RESPONSIVE_PRESENTATION, size),
28
+ };
29
+ }
30
+ return { presentation: config.presentation };
31
+ }
32
+ //# sourceMappingURL=resolveCustomTabsPresentation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveCustomTabsPresentation.js","sourceRoot":"","sources":["../../src/presentation/resolveCustomTabsPresentation.ts"],"names":[],"mappings":"AAWA,MAAM,+BAA+B,GAAG;IACtC,OAAO,EAAE,QAAQ;IACjB,MAAM,EAAE,MAAM;IACd,QAAQ,EAAE,SAAS;CAC4D,CAAC;AAOlF,wEAAwE;AACxE,SAAS,6BAA6B,CACpC,OAAmC,EACnC,IAA6B;IAE7B,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,SAAS;YACZ,OAAO,OAAO,CAAC,OAAO,CAAC;QACzB,KAAK,QAAQ;YACX,OAAO,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,QAAQ,CAAC;QAC5C,KAAK,UAAU;YACb,OAAO,OAAO,CAAC,QAAQ,CAAC;IAC5B,CAAC;AACH,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,6BAA6B,CAC3C,MAAgD,EAChD,IAA6B;IAE7B,IAAI,MAAM,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;QACrC,IAAI,CAAC,MAAM,CAAC,oBAAoB,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;QAC7E,CAAC;QACD,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,CAAC,oBAAoB,EAAE,CAAC;IACvF,CAAC;IAED,IAAI,MAAM,CAAC,YAAY,KAAK,YAAY,EAAE,CAAC;QACzC,OAAO;YACL,YAAY,EAAE,6BAA6B,CACzC,MAAM,CAAC,UAAU,IAAI,+BAA+B,EACpD,IAAI,CACL;SACF,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { NavigatorPreset, NavigatorType } from '@ankhorage/contracts/navigator';
2
+ /*** Resolve a canonical navigator preset into its ordered topology layers. */
3
+ export declare function resolveNavigatorPreset(preset: NavigatorPreset | undefined, fallbackType: NavigatorType): readonly NavigatorType[];
4
+ //# sourceMappingURL=resolveNavigatorPreset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveNavigatorPreset.d.ts","sourceRoot":"","sources":["../../src/topology/resolveNavigatorPreset.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAErF,8EAA8E;AAC9E,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,eAAe,GAAG,SAAS,EACnC,YAAY,EAAE,aAAa,GAC1B,SAAS,aAAa,EAAE,CA+B1B"}
@@ -0,0 +1,34 @@
1
+ /*** Resolve a canonical navigator preset into its ordered topology layers. */
2
+ export function resolveNavigatorPreset(preset, fallbackType) {
3
+ switch (preset) {
4
+ case undefined:
5
+ return [fallbackType];
6
+ case 'stack':
7
+ return ['stack'];
8
+ case 'tabs':
9
+ return ['tabs'];
10
+ case 'tabs-stack':
11
+ return ['tabs', 'stack'];
12
+ case 'drawer':
13
+ return ['drawer'];
14
+ case 'drawer-stack':
15
+ return ['drawer', 'stack'];
16
+ case 'drawer-tabs':
17
+ return ['drawer', 'tabs'];
18
+ case 'drawer-tabs-stack':
19
+ return ['drawer', 'tabs', 'stack'];
20
+ case 'root-stack-tabs':
21
+ return ['stack', 'tabs'];
22
+ case 'root-stack-tabs-stack':
23
+ return ['stack', 'tabs', 'stack'];
24
+ case 'root-stack-drawer':
25
+ return ['stack', 'drawer'];
26
+ case 'root-stack-drawer-stack':
27
+ return ['stack', 'drawer', 'stack'];
28
+ case 'root-stack-drawer-tabs':
29
+ return ['stack', 'drawer', 'tabs'];
30
+ case 'root-stack-drawer-tabs-stack':
31
+ return ['stack', 'drawer', 'tabs', 'stack'];
32
+ }
33
+ }
34
+ //# sourceMappingURL=resolveNavigatorPreset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveNavigatorPreset.js","sourceRoot":"","sources":["../../src/topology/resolveNavigatorPreset.ts"],"names":[],"mappings":"AAEA,8EAA8E;AAC9E,MAAM,UAAU,sBAAsB,CACpC,MAAmC,EACnC,YAA2B;IAE3B,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,SAAS;YACZ,OAAO,CAAC,YAAY,CAAC,CAAC;QACxB,KAAK,OAAO;YACV,OAAO,CAAC,OAAO,CAAC,CAAC;QACnB,KAAK,MAAM;YACT,OAAO,CAAC,MAAM,CAAC,CAAC;QAClB,KAAK,YAAY;YACf,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC3B,KAAK,QAAQ;YACX,OAAO,CAAC,QAAQ,CAAC,CAAC;QACpB,KAAK,cAAc;YACjB,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC7B,KAAK,aAAa;YAChB,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC5B,KAAK,mBAAmB;YACtB,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QACrC,KAAK,iBAAiB;YACpB,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC3B,KAAK,uBAAuB;YAC1B,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QACpC,KAAK,mBAAmB;YACtB,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC7B,KAAK,yBAAyB;YAC5B,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;QACtC,KAAK,wBAAwB;YAC3B,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QACrC,KAAK,8BAA8B;YACjC,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAChD,CAAC;AACH,CAAC"}
package/package.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "@ankhorage/navigator",
3
+ "version": "0.1.0",
4
+ "description": "Standalone manifest-driven navigator capability for Expo Router across React Native and React Native Web.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "sideEffects": false,
8
+ "homepage": "https://github.com/ankhorage/navigator#readme",
9
+ "bugs": {
10
+ "url": "https://github.com/ankhorage/navigator/issues"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/ankhorage/navigator.git"
15
+ },
16
+ "keywords": [
17
+ "ankhorage",
18
+ "react-native",
19
+ "react-native-web",
20
+ "expo",
21
+ "expo-router",
22
+ "navigator",
23
+ "navigation",
24
+ "tabs",
25
+ "drawer",
26
+ "responsive",
27
+ "typescript"
28
+ ],
29
+ "main": "./dist/index.js",
30
+ "types": "./dist/index.d.ts",
31
+ "files": [
32
+ "dist",
33
+ "README.md",
34
+ "CHANGELOG.md",
35
+ "LICENSE"
36
+ ],
37
+ "exports": {
38
+ ".": {
39
+ "types": "./dist/index.d.ts",
40
+ "default": "./dist/index.js"
41
+ },
42
+ "./metadata": {
43
+ "types": "./dist/metadata/index.d.ts",
44
+ "default": "./dist/metadata/index.js"
45
+ },
46
+ "./package.json": "./package.json"
47
+ },
48
+ "publishConfig": {
49
+ "access": "public"
50
+ },
51
+ "scripts": {
52
+ "build": "rm -rf dist tsconfig.tsbuildinfo && bun x tsc -p tsconfig.json",
53
+ "changeset": "ankhorage-changeset",
54
+ "changeset:status": "ankhorage-changeset status --since=origin/main",
55
+ "docs": "paradox",
56
+ "test": "bun test src",
57
+ "typecheck": "bun x tsc --noEmit -p tsconfig.json",
58
+ "version-packages": "ankhorage-changeset version",
59
+ "lint": "ankhorage-eslint . --max-warnings=0",
60
+ "lint:fix": "ankhorage-eslint . --fix --max-warnings=0",
61
+ "format": "ankhorage-prettier --write .",
62
+ "format:check": "ankhorage-prettier --check .",
63
+ "knip:check": "ankhorage-knip"
64
+ },
65
+ "dependencies": {
66
+ "@ankhorage/contracts": "^10.0.0"
67
+ },
68
+ "devDependencies": {
69
+ "@ankhorage/devtools": "^1.10.16",
70
+ "@ankhorage/paradox": "^0.1.24",
71
+ "@types/bun": "^1.4.1",
72
+ "@types/node": "^24.13.3",
73
+ "typescript": "~6.0.3"
74
+ },
75
+ "packageManager": "bun@1.4.1"
76
+ }