@ankhorage/expo-runtime 0.0.5 → 0.0.7

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @ankhorage/expo-runtime
2
2
 
3
+ ## 0.0.7
4
+
5
+ ### Patch Changes
6
+
7
+ - 7eddf62: Add generated layout helper output for Expo provider imports.
8
+
9
+ ## 0.0.6
10
+
11
+ ### Patch Changes
12
+
13
+ - 5f947ed: Add Expo-aware runtime action bridge helpers for navigation, theme toggling, logging, alerts, and delegated action handlers.
14
+
3
15
  ## 0.0.5
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  # EXPO-RUNTIME
5
5
 
6
- ![license: MIT](././paradox/badges/license.svg) ![npm: v0.0.3](././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)
6
+ ![license: MIT](././paradox/badges/license.svg) ![npm: v0.0.5](././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
7
 
8
8
  Declarative runtime integration for Expo apps: maps app capabilities to permissions, packages, config plugins, providers, and adapters
9
9
 
@@ -16,5 +16,6 @@ Declarative runtime integration for Expo apps: maps app capabilities to permissi
16
16
  - [Module relationships](././paradox/diagrams/module-relationships.mmd)
17
17
  - [Export graph](././paradox/diagrams/export-graph.mmd)
18
18
  - [createExpoRuntimeRegistry sequence](././paradox/diagrams/sequences/create-expo-runtime-registry.mmd)
19
+ - [executeExpoRuntimeAction sequence](././paradox/diagrams/sequences/execute-expo-runtime-action.mmd)
19
20
  - [ExpoBarcodeScannerAdapter sequence](././paradox/diagrams/sequences/expo-barcode-scanner-adapter.mmd)
20
21
  - [resolveExpoRuntimePlan sequence](././paradox/diagrams/sequences/resolve-expo-runtime-plan.mmd)
@@ -0,0 +1,29 @@
1
+ import type { Action } from '@ankhorage/contracts';
2
+ export type ExpoRuntimeThemeMode = 'dark' | 'light';
3
+ export interface ExpoRuntimeRouterLike {
4
+ push: (args: {
5
+ pathname: string;
6
+ params: Record<string, number | string>;
7
+ }) => void;
8
+ }
9
+ export interface ExpoRuntimeActionHandlerArgs {
10
+ action: Action;
11
+ }
12
+ export type ExpoRuntimeActionHandlers = Record<string, (args: ExpoRuntimeActionHandlerArgs) => Promise<void> | void>;
13
+ export interface ExecuteExpoRuntimeActionArgs {
14
+ action: unknown;
15
+ router: ExpoRuntimeRouterLike;
16
+ mode: ExpoRuntimeThemeMode;
17
+ setMode: (mode: ExpoRuntimeThemeMode) => void;
18
+ actionHandlers?: ExpoRuntimeActionHandlers;
19
+ requestAnimationFrameImpl?: (callback: () => void) => number | void;
20
+ alertImpl?: (message: string) => void;
21
+ consoleImpl?: Pick<typeof console, 'log'>;
22
+ }
23
+ export interface ExpoRuntimeRouteResolution {
24
+ resolvedPath: string;
25
+ unusedParams: Record<string, number | string>;
26
+ }
27
+ export declare function resolveExpoRuntimeRoutePath(pathname: string, params?: Record<string, number | string>): ExpoRuntimeRouteResolution;
28
+ export declare function executeExpoRuntimeAction(args: ExecuteExpoRuntimeActionArgs): Promise<void>;
29
+ //# sourceMappingURL=expoActionBridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expoActionBridge.d.ts","sourceRoot":"","sources":["../src/expoActionBridge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAEnD,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,OAAO,CAAC;AAEpD,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,CAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAA;KAAE,KAAK,IAAI,CAAC;CACrF;AAED,MAAM,WAAW,4BAA4B;IAC3C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,yBAAyB,GAAG,MAAM,CAC5C,MAAM,EACN,CAAC,IAAI,EAAE,4BAA4B,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAC7D,CAAC;AAEF,MAAM,WAAW,4BAA4B;IAC3C,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,qBAAqB,CAAC;IAC9B,IAAI,EAAE,oBAAoB,CAAC;IAC3B,OAAO,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAC9C,cAAc,CAAC,EAAE,yBAAyB,CAAC;IAC3C,yBAAyB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,GAAG,IAAI,CAAC;IACpE,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,WAAW,CAAC,EAAE,IAAI,CAAC,OAAO,OAAO,EAAE,KAAK,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,0BAA0B;IACzC,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;CAC/C;AAED,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GACvC,0BAA0B,CAmB5B;AAUD,wBAAsB,wBAAwB,CAAC,IAAI,EAAE,4BAA4B,GAAG,OAAO,CAAC,IAAI,CAAC,CA0EhG"}
@@ -0,0 +1,78 @@
1
+ export function resolveExpoRuntimeRoutePath(pathname, params) {
2
+ if (!params) {
3
+ return { resolvedPath: pathname, unusedParams: {} };
4
+ }
5
+ let resolvedPath = pathname;
6
+ const unusedParams = { ...params };
7
+ Object.keys(params).forEach((key) => {
8
+ const placeholder = `[${key}]`;
9
+ if (!resolvedPath.includes(placeholder)) {
10
+ return;
11
+ }
12
+ resolvedPath = resolvedPath.replace(placeholder, String(params[key]));
13
+ delete unusedParams[key];
14
+ });
15
+ return { resolvedPath, unusedParams };
16
+ }
17
+ function isAction(value) {
18
+ return typeof value === 'object' && value !== null && 'type' in value;
19
+ }
20
+ function isActionCallback(value) {
21
+ return typeof value === 'function';
22
+ }
23
+ export async function executeExpoRuntimeAction(args) {
24
+ const { action, router, mode, setMode, actionHandlers, requestAnimationFrameImpl = (callback) => {
25
+ callback();
26
+ }, alertImpl, consoleImpl = console, } = args;
27
+ if (!action) {
28
+ return;
29
+ }
30
+ if (isActionCallback(action)) {
31
+ action();
32
+ return;
33
+ }
34
+ if (!isAction(action)) {
35
+ return;
36
+ }
37
+ if (action.type === 'navigate' && action.payload.route) {
38
+ const { route, params } = action.payload;
39
+ requestAnimationFrameImpl(() => {
40
+ let pathname = route;
41
+ if (!pathname.startsWith('/') && !pathname.startsWith('(')) {
42
+ pathname = `/${pathname}`;
43
+ }
44
+ const { resolvedPath, unusedParams } = resolveExpoRuntimeRoutePath(pathname, params);
45
+ router.push({
46
+ pathname: resolvedPath,
47
+ params: unusedParams,
48
+ });
49
+ });
50
+ return;
51
+ }
52
+ if (action.type === 'toggleDarkMode') {
53
+ setMode(mode === 'dark' ? 'light' : 'dark');
54
+ return;
55
+ }
56
+ if (action.type === 'search') {
57
+ consoleImpl.log('[Ankh Action] Search:', action.payload);
58
+ return;
59
+ }
60
+ if (action.type === 'filter') {
61
+ consoleImpl.log('[Ankh Action] Filter:', action.payload);
62
+ return;
63
+ }
64
+ if (action.type === 'alert') {
65
+ const message = action.payload?.message ?? JSON.stringify(action.payload);
66
+ alertImpl?.(message);
67
+ return;
68
+ }
69
+ if (action.type === 'console') {
70
+ consoleImpl.log('[Ankh Action]', action.payload);
71
+ return;
72
+ }
73
+ const configuredHandler = actionHandlers?.[action.type];
74
+ if (configuredHandler) {
75
+ await configuredHandler({ action });
76
+ }
77
+ }
78
+ //# sourceMappingURL=expoActionBridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expoActionBridge.js","sourceRoot":"","sources":["../src/expoActionBridge.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Action } from '@ankhorage/contracts';\n\nexport type ExpoRuntimeThemeMode = 'dark' | 'light';\n\nexport interface ExpoRuntimeRouterLike {\n push: (args: { pathname: string; params: Record<string, number | string> }) => void;\n}\n\nexport interface ExpoRuntimeActionHandlerArgs {\n action: Action;\n}\n\nexport type ExpoRuntimeActionHandlers = Record<\n string,\n (args: ExpoRuntimeActionHandlerArgs) => Promise<void> | void\n>;\n\nexport interface ExecuteExpoRuntimeActionArgs {\n action: unknown;\n router: ExpoRuntimeRouterLike;\n mode: ExpoRuntimeThemeMode;\n setMode: (mode: ExpoRuntimeThemeMode) => void;\n actionHandlers?: ExpoRuntimeActionHandlers;\n requestAnimationFrameImpl?: (callback: () => void) => number | void;\n alertImpl?: (message: string) => void;\n consoleImpl?: Pick<typeof console, 'log'>;\n}\n\nexport interface ExpoRuntimeRouteResolution {\n resolvedPath: string;\n unusedParams: Record<string, number | string>;\n}\n\nexport function resolveExpoRuntimeRoutePath(\n pathname: string,\n params?: Record<string, number | string>,\n): ExpoRuntimeRouteResolution {\n if (!params) {\n return { resolvedPath: pathname, unusedParams: {} };\n }\n\n let resolvedPath = pathname;\n const unusedParams: Record<string, number | string> = { ...params };\n\n Object.keys(params).forEach((key) => {\n const placeholder = `[${key}]`;\n if (!resolvedPath.includes(placeholder)) {\n return;\n }\n\n resolvedPath = resolvedPath.replace(placeholder, String(params[key]));\n delete unusedParams[key];\n });\n\n return { resolvedPath, unusedParams };\n}\n\nfunction isAction(value: unknown): value is Action {\n return typeof value === 'object' && value !== null && 'type' in value;\n}\n\nfunction isActionCallback(value: unknown): value is () => void {\n return typeof value === 'function';\n}\n\nexport async function executeExpoRuntimeAction(args: ExecuteExpoRuntimeActionArgs): Promise<void> {\n const {\n action,\n router,\n mode,\n setMode,\n actionHandlers,\n requestAnimationFrameImpl = (callback) => {\n callback();\n },\n alertImpl,\n consoleImpl = console,\n } = args;\n\n if (!action) {\n return;\n }\n\n if (isActionCallback(action)) {\n action();\n return;\n }\n\n if (!isAction(action)) {\n return;\n }\n\n if (action.type === 'navigate' && action.payload.route) {\n const { route, params } = action.payload;\n requestAnimationFrameImpl(() => {\n let pathname = route;\n if (!pathname.startsWith('/') && !pathname.startsWith('(')) {\n pathname = `/${pathname}`;\n }\n\n const { resolvedPath, unusedParams } = resolveExpoRuntimeRoutePath(pathname, params);\n router.push({\n pathname: resolvedPath,\n params: unusedParams,\n });\n });\n return;\n }\n\n if (action.type === 'toggleDarkMode') {\n setMode(mode === 'dark' ? 'light' : 'dark');\n return;\n }\n\n if (action.type === 'search') {\n consoleImpl.log('[Ankh Action] Search:', action.payload);\n return;\n }\n\n if (action.type === 'filter') {\n consoleImpl.log('[Ankh Action] Filter:', action.payload);\n return;\n }\n\n if (action.type === 'alert') {\n const message = action.payload?.message ?? JSON.stringify(action.payload);\n alertImpl?.(message);\n return;\n }\n\n if (action.type === 'console') {\n consoleImpl.log('[Ankh Action]', action.payload);\n return;\n }\n\n const configuredHandler = actionHandlers?.[action.type];\n if (configuredHandler) {\n await configuredHandler({ action });\n }\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -2,9 +2,11 @@ export { BARCODE_SCAN_DEDUPE_WINDOW_MS, BARCODE_SCANNER_TYPES, type BarcodeScanR
2
2
  export type { ComponentRegistry } from './componentRegistry';
3
3
  export { createComponentRegistry } from './componentRegistry';
4
4
  export { createExpoRuntimeRegistry } from './createExpoRuntimeRegistry';
5
+ export { executeExpoRuntimeAction, type ExecuteExpoRuntimeActionArgs, type ExpoRuntimeActionHandlerArgs, type ExpoRuntimeActionHandlers, type ExpoRuntimeRouteResolution, type ExpoRuntimeRouterLike, type ExpoRuntimeThemeMode, resolveExpoRuntimeRoutePath, } from './expoActionBridge';
5
6
  export { ExpoBarcodeScannerAdapter } from './ExpoBarcodeScannerAdapter';
6
7
  export { ExpoRuntimeProviders, type ExpoRuntimeProvidersProps } from './ExpoRuntimeProviders';
7
8
  export { getExpoBarcodeScannerViewSource } from './generatedSources';
9
+ export { type ExpoRuntimeLayoutIntegrationPlan, resolveExpoRuntimeLayoutIntegration, } from './layoutIntegrationPlanning';
8
10
  export type { ExpoRuntimeAdapterId, ExpoRuntimePlan, ExpoRuntimeProviderId, } from './resolveExpoRuntimePlan';
9
11
  export { resolveExpoRuntimePlan } from './resolveExpoRuntimePlan';
10
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,EACrB,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,2CAA2C,EAC3C,8BAA8B,EAC9B,uBAAuB,GACxB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EAAE,oBAAoB,EAAE,KAAK,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAC9F,OAAO,EAAE,+BAA+B,EAAE,MAAM,oBAAoB,CAAC;AACrE,YAAY,EACV,oBAAoB,EACpB,eAAe,EACf,qBAAqB,GACtB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,EACrB,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,2CAA2C,EAC3C,8BAA8B,EAC9B,uBAAuB,GACxB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EACL,wBAAwB,EACxB,KAAK,4BAA4B,EACjC,KAAK,4BAA4B,EACjC,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAC/B,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,2BAA2B,GAC5B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EAAE,oBAAoB,EAAE,KAAK,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAC9F,OAAO,EAAE,+BAA+B,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EACL,KAAK,gCAAgC,EACrC,mCAAmC,GACpC,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,oBAAoB,EACpB,eAAe,EACf,qBAAqB,GACtB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC"}
package/dist/index.js CHANGED
@@ -1,8 +1,10 @@
1
1
  export { BARCODE_SCAN_DEDUPE_WINDOW_MS, BARCODE_SCANNER_TYPES, mapPermissionStatusToCameraPermissionStatus, normalizeExpoBarcodeScanResult, shouldIgnoreBarcodeScan, } from './barcodeScanRuntime';
2
2
  export { createComponentRegistry } from './componentRegistry';
3
3
  export { createExpoRuntimeRegistry } from './createExpoRuntimeRegistry';
4
+ export { executeExpoRuntimeAction, resolveExpoRuntimeRoutePath, } from './expoActionBridge';
4
5
  export { ExpoBarcodeScannerAdapter } from './ExpoBarcodeScannerAdapter';
5
6
  export { ExpoRuntimeProviders } from './ExpoRuntimeProviders';
6
7
  export { getExpoBarcodeScannerViewSource } from './generatedSources';
8
+ export { resolveExpoRuntimeLayoutIntegration, } from './layoutIntegrationPlanning';
7
9
  export { resolveExpoRuntimePlan } from './resolveExpoRuntimePlan';
8
10
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,EAGrB,2CAA2C,EAC3C,8BAA8B,EAC9B,uBAAuB,GACxB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EAAE,oBAAoB,EAAkC,MAAM,wBAAwB,CAAC;AAC9F,OAAO,EAAE,+BAA+B,EAAE,MAAM,oBAAoB,CAAC;AAMrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC","sourcesContent":["export {\n BARCODE_SCAN_DEDUPE_WINDOW_MS,\n BARCODE_SCANNER_TYPES,\n type BarcodeScanRecord,\n type ExpoBarcodeScanResultLike,\n mapPermissionStatusToCameraPermissionStatus,\n normalizeExpoBarcodeScanResult,\n shouldIgnoreBarcodeScan,\n} from './barcodeScanRuntime';\nexport type { ComponentRegistry } from './componentRegistry';\nexport { createComponentRegistry } from './componentRegistry';\nexport { createExpoRuntimeRegistry } from './createExpoRuntimeRegistry';\nexport { ExpoBarcodeScannerAdapter } from './ExpoBarcodeScannerAdapter';\nexport { ExpoRuntimeProviders, type ExpoRuntimeProvidersProps } from './ExpoRuntimeProviders';\nexport { getExpoBarcodeScannerViewSource } from './generatedSources';\nexport type {\n ExpoRuntimeAdapterId,\n ExpoRuntimePlan,\n ExpoRuntimeProviderId,\n} from './resolveExpoRuntimePlan';\nexport { resolveExpoRuntimePlan } from './resolveExpoRuntimePlan';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,EAGrB,2CAA2C,EAC3C,8BAA8B,EAC9B,uBAAuB,GACxB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EACL,wBAAwB,EAOxB,2BAA2B,GAC5B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EAAE,oBAAoB,EAAkC,MAAM,wBAAwB,CAAC;AAC9F,OAAO,EAAE,+BAA+B,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAEL,mCAAmC,GACpC,MAAM,6BAA6B,CAAC;AAMrC,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC","sourcesContent":["export {\n BARCODE_SCAN_DEDUPE_WINDOW_MS,\n BARCODE_SCANNER_TYPES,\n type BarcodeScanRecord,\n type ExpoBarcodeScanResultLike,\n mapPermissionStatusToCameraPermissionStatus,\n normalizeExpoBarcodeScanResult,\n shouldIgnoreBarcodeScan,\n} from './barcodeScanRuntime';\nexport type { ComponentRegistry } from './componentRegistry';\nexport { createComponentRegistry } from './componentRegistry';\nexport { createExpoRuntimeRegistry } from './createExpoRuntimeRegistry';\nexport {\n executeExpoRuntimeAction,\n type ExecuteExpoRuntimeActionArgs,\n type ExpoRuntimeActionHandlerArgs,\n type ExpoRuntimeActionHandlers,\n type ExpoRuntimeRouteResolution,\n type ExpoRuntimeRouterLike,\n type ExpoRuntimeThemeMode,\n resolveExpoRuntimeRoutePath,\n} from './expoActionBridge';\nexport { ExpoBarcodeScannerAdapter } from './ExpoBarcodeScannerAdapter';\nexport { ExpoRuntimeProviders, type ExpoRuntimeProvidersProps } from './ExpoRuntimeProviders';\nexport { getExpoBarcodeScannerViewSource } from './generatedSources';\nexport {\n type ExpoRuntimeLayoutIntegrationPlan,\n resolveExpoRuntimeLayoutIntegration,\n} from './layoutIntegrationPlanning';\nexport type {\n ExpoRuntimeAdapterId,\n ExpoRuntimePlan,\n ExpoRuntimeProviderId,\n} from './resolveExpoRuntimePlan';\nexport { resolveExpoRuntimePlan } from './resolveExpoRuntimePlan';\n"]}
@@ -0,0 +1,9 @@
1
+ import type { ExpoRuntimePlan } from './resolveExpoRuntimePlan';
2
+ export interface ExpoRuntimeLayoutIntegrationPlan {
3
+ readonly imports: readonly string[];
4
+ readonly moduleDeclarations: readonly string[];
5
+ readonly providerStart: readonly string[];
6
+ readonly providerEnd: readonly string[];
7
+ }
8
+ export declare function resolveExpoRuntimeLayoutIntegration(runtimePlan: ExpoRuntimePlan | undefined): ExpoRuntimeLayoutIntegrationPlan;
9
+ //# sourceMappingURL=layoutIntegrationPlanning.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layoutIntegrationPlanning.d.ts","sourceRoot":"","sources":["../src/layoutIntegrationPlanning.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,kBAAkB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/C,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAED,wBAAgB,mCAAmC,CACjD,WAAW,EAAE,eAAe,GAAG,SAAS,GACvC,gCAAgC,CA2BlC"}
@@ -0,0 +1,22 @@
1
+ export function resolveExpoRuntimeLayoutIntegration(runtimePlan) {
2
+ const imports = [];
3
+ const expoRuntimeImports = [];
4
+ const moduleDeclarations = [];
5
+ const providerStart = [];
6
+ const providerEnd = [];
7
+ if ((runtimePlan?.providers.length ?? 0) > 0) {
8
+ expoRuntimeImports.push('ExpoRuntimeProviders');
9
+ providerStart.push(`<ExpoRuntimeProviders providers={['${(runtimePlan?.providers ?? []).join("', '")}']}>`);
10
+ providerEnd.push('</ExpoRuntimeProviders>');
11
+ }
12
+ if (expoRuntimeImports.length > 0) {
13
+ imports.push(`import { ${Array.from(new Set(expoRuntimeImports)).join(', ')} } from '@ankhorage/expo-runtime';`);
14
+ }
15
+ return {
16
+ imports,
17
+ moduleDeclarations,
18
+ providerStart,
19
+ providerEnd,
20
+ };
21
+ }
22
+ //# sourceMappingURL=layoutIntegrationPlanning.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layoutIntegrationPlanning.js","sourceRoot":"","sources":["../src/layoutIntegrationPlanning.ts"],"names":[],"mappings":"AASA,MAAM,UAAU,mCAAmC,CACjD,WAAwC;IAExC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,kBAAkB,GAAa,EAAE,CAAC;IACxC,MAAM,kBAAkB,GAAa,EAAE,CAAC;IACxC,MAAM,aAAa,GAAa,EAAE,CAAC;IACnC,MAAM,WAAW,GAAa,EAAE,CAAC;IAEjC,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7C,kBAAkB,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QAChD,aAAa,CAAC,IAAI,CAChB,sCAAsC,CAAC,WAAW,EAAE,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CACxF,CAAC;QACF,WAAW,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IAC9C,CAAC;IAED,IAAI,kBAAkB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,OAAO,CAAC,IAAI,CACV,YAAY,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,oCAAoC,CACnG,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO;QACP,kBAAkB;QAClB,aAAa;QACb,WAAW;KACZ,CAAC;AACJ,CAAC","sourcesContent":["import type { ExpoRuntimePlan } from './resolveExpoRuntimePlan';\n\nexport interface ExpoRuntimeLayoutIntegrationPlan {\n readonly imports: readonly string[];\n readonly moduleDeclarations: readonly string[];\n readonly providerStart: readonly string[];\n readonly providerEnd: readonly string[];\n}\n\nexport function resolveExpoRuntimeLayoutIntegration(\n runtimePlan: ExpoRuntimePlan | undefined,\n): ExpoRuntimeLayoutIntegrationPlan {\n const imports: string[] = [];\n const expoRuntimeImports: string[] = [];\n const moduleDeclarations: string[] = [];\n const providerStart: string[] = [];\n const providerEnd: string[] = [];\n\n if ((runtimePlan?.providers.length ?? 0) > 0) {\n expoRuntimeImports.push('ExpoRuntimeProviders');\n providerStart.push(\n `<ExpoRuntimeProviders providers={['${(runtimePlan?.providers ?? []).join(\"', '\")}']}>`,\n );\n providerEnd.push('</ExpoRuntimeProviders>');\n }\n\n if (expoRuntimeImports.length > 0) {\n imports.push(\n `import { ${Array.from(new Set(expoRuntimeImports)).join(', ')} } from '@ankhorage/expo-runtime';`,\n );\n }\n\n return {\n imports,\n moduleDeclarations,\n providerStart,\n providerEnd,\n };\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  export { getExpoBarcodeScannerViewSource } from './generatedSources';
2
+ export { type ExpoRuntimeLayoutIntegrationPlan, resolveExpoRuntimeLayoutIntegration, } from './layoutIntegrationPlanning';
2
3
  export type { ExpoRuntimeAdapterId, ExpoRuntimePlan, ExpoRuntimeProviderId, } from './resolveExpoRuntimePlan';
3
4
  export { resolveExpoRuntimePlan } from './resolveExpoRuntimePlan';
4
5
  //# sourceMappingURL=planning.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"planning.d.ts","sourceRoot":"","sources":["../src/planning.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,+BAA+B,EAAE,MAAM,oBAAoB,CAAC;AACrE,YAAY,EACV,oBAAoB,EACpB,eAAe,EACf,qBAAqB,GACtB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"planning.d.ts","sourceRoot":"","sources":["../src/planning.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,+BAA+B,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EACL,KAAK,gCAAgC,EACrC,mCAAmC,GACpC,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,oBAAoB,EACpB,eAAe,EACf,qBAAqB,GACtB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC"}
package/dist/planning.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export { getExpoBarcodeScannerViewSource } from './generatedSources';
2
+ export { resolveExpoRuntimeLayoutIntegration, } from './layoutIntegrationPlanning';
2
3
  export { resolveExpoRuntimePlan } from './resolveExpoRuntimePlan';
3
4
  //# sourceMappingURL=planning.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"planning.js","sourceRoot":"","sources":["../src/planning.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,+BAA+B,EAAE,MAAM,oBAAoB,CAAC;AAMrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC","sourcesContent":["export { getExpoBarcodeScannerViewSource } from './generatedSources';\nexport type {\n ExpoRuntimeAdapterId,\n ExpoRuntimePlan,\n ExpoRuntimeProviderId,\n} from './resolveExpoRuntimePlan';\nexport { resolveExpoRuntimePlan } from './resolveExpoRuntimePlan';\n"]}
1
+ {"version":3,"file":"planning.js","sourceRoot":"","sources":["../src/planning.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,+BAA+B,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAEL,mCAAmC,GACpC,MAAM,6BAA6B,CAAC;AAMrC,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC","sourcesContent":["export { getExpoBarcodeScannerViewSource } from './generatedSources';\nexport {\n type ExpoRuntimeLayoutIntegrationPlan,\n resolveExpoRuntimeLayoutIntegration,\n} from './layoutIntegrationPlanning';\nexport type {\n ExpoRuntimeAdapterId,\n ExpoRuntimePlan,\n ExpoRuntimeProviderId,\n} from './resolveExpoRuntimePlan';\nexport { resolveExpoRuntimePlan } from './resolveExpoRuntimePlan';\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ankhorage/expo-runtime",
3
3
  "type": "module",
4
- "version": "0.0.5",
4
+ "version": "0.0.7",
5
5
  "description": "Declarative runtime integration for Expo apps: maps app capabilities to permissions, packages, config plugins, providers, and adapters",
6
6
  "homepage": "https://github.com/ankhorage/expo-runtime#readme",
7
7
  "bugs": {
@@ -0,0 +1,54 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+
3
+ import { executeExpoRuntimeAction, resolveExpoRuntimeRoutePath } from './expoActionBridge';
4
+
5
+ describe('expoActionBridge', () => {
6
+ test('resolves dynamic route params and keeps unused params', () => {
7
+ expect(resolveExpoRuntimeRoutePath('/users/[id]', { id: 42, tab: 'profile' })).toEqual({
8
+ resolvedPath: '/users/42',
9
+ unusedParams: { tab: 'profile' },
10
+ });
11
+ });
12
+
13
+ test('executes navigation actions', async () => {
14
+ const pushes: { pathname: string; params: Record<string, number | string> }[] = [];
15
+
16
+ await executeExpoRuntimeAction({
17
+ action: { type: 'navigate', payload: { route: 'users/[id]', params: { id: 42 } } },
18
+ router: { push: (args) => pushes.push(args) },
19
+ mode: 'dark',
20
+ setMode: () => undefined,
21
+ });
22
+
23
+ expect(pushes).toEqual([{ pathname: '/users/42', params: {} }]);
24
+ });
25
+
26
+ test('toggles dark mode', async () => {
27
+ const modes: string[] = [];
28
+
29
+ await executeExpoRuntimeAction({
30
+ action: { type: 'toggleDarkMode' },
31
+ router: { push: () => undefined },
32
+ mode: 'dark',
33
+ setMode: (mode) => modes.push(mode),
34
+ });
35
+
36
+ expect(modes).toEqual(['light']);
37
+ });
38
+
39
+ test('delegates configured actions', async () => {
40
+ const calls: string[] = [];
41
+
42
+ await executeExpoRuntimeAction({
43
+ action: { type: 'setLanguage', payload: { locale: 'de' } },
44
+ router: { push: () => undefined },
45
+ mode: 'dark',
46
+ setMode: () => undefined,
47
+ actionHandlers: {
48
+ setLanguage: ({ action }) => calls.push(action.type),
49
+ },
50
+ });
51
+
52
+ expect(calls).toEqual(['setLanguage']);
53
+ });
54
+ });
@@ -0,0 +1,140 @@
1
+ import type { Action } from '@ankhorage/contracts';
2
+
3
+ export type ExpoRuntimeThemeMode = 'dark' | 'light';
4
+
5
+ export interface ExpoRuntimeRouterLike {
6
+ push: (args: { pathname: string; params: Record<string, number | string> }) => void;
7
+ }
8
+
9
+ export interface ExpoRuntimeActionHandlerArgs {
10
+ action: Action;
11
+ }
12
+
13
+ export type ExpoRuntimeActionHandlers = Record<
14
+ string,
15
+ (args: ExpoRuntimeActionHandlerArgs) => Promise<void> | void
16
+ >;
17
+
18
+ export interface ExecuteExpoRuntimeActionArgs {
19
+ action: unknown;
20
+ router: ExpoRuntimeRouterLike;
21
+ mode: ExpoRuntimeThemeMode;
22
+ setMode: (mode: ExpoRuntimeThemeMode) => void;
23
+ actionHandlers?: ExpoRuntimeActionHandlers;
24
+ requestAnimationFrameImpl?: (callback: () => void) => number | void;
25
+ alertImpl?: (message: string) => void;
26
+ consoleImpl?: Pick<typeof console, 'log'>;
27
+ }
28
+
29
+ export interface ExpoRuntimeRouteResolution {
30
+ resolvedPath: string;
31
+ unusedParams: Record<string, number | string>;
32
+ }
33
+
34
+ export function resolveExpoRuntimeRoutePath(
35
+ pathname: string,
36
+ params?: Record<string, number | string>,
37
+ ): ExpoRuntimeRouteResolution {
38
+ if (!params) {
39
+ return { resolvedPath: pathname, unusedParams: {} };
40
+ }
41
+
42
+ let resolvedPath = pathname;
43
+ const unusedParams: Record<string, number | string> = { ...params };
44
+
45
+ Object.keys(params).forEach((key) => {
46
+ const placeholder = `[${key}]`;
47
+ if (!resolvedPath.includes(placeholder)) {
48
+ return;
49
+ }
50
+
51
+ resolvedPath = resolvedPath.replace(placeholder, String(params[key]));
52
+ delete unusedParams[key];
53
+ });
54
+
55
+ return { resolvedPath, unusedParams };
56
+ }
57
+
58
+ function isAction(value: unknown): value is Action {
59
+ return typeof value === 'object' && value !== null && 'type' in value;
60
+ }
61
+
62
+ function isActionCallback(value: unknown): value is () => void {
63
+ return typeof value === 'function';
64
+ }
65
+
66
+ export async function executeExpoRuntimeAction(args: ExecuteExpoRuntimeActionArgs): Promise<void> {
67
+ const {
68
+ action,
69
+ router,
70
+ mode,
71
+ setMode,
72
+ actionHandlers,
73
+ requestAnimationFrameImpl = (callback) => {
74
+ callback();
75
+ },
76
+ alertImpl,
77
+ consoleImpl = console,
78
+ } = args;
79
+
80
+ if (!action) {
81
+ return;
82
+ }
83
+
84
+ if (isActionCallback(action)) {
85
+ action();
86
+ return;
87
+ }
88
+
89
+ if (!isAction(action)) {
90
+ return;
91
+ }
92
+
93
+ if (action.type === 'navigate' && action.payload.route) {
94
+ const { route, params } = action.payload;
95
+ requestAnimationFrameImpl(() => {
96
+ let pathname = route;
97
+ if (!pathname.startsWith('/') && !pathname.startsWith('(')) {
98
+ pathname = `/${pathname}`;
99
+ }
100
+
101
+ const { resolvedPath, unusedParams } = resolveExpoRuntimeRoutePath(pathname, params);
102
+ router.push({
103
+ pathname: resolvedPath,
104
+ params: unusedParams,
105
+ });
106
+ });
107
+ return;
108
+ }
109
+
110
+ if (action.type === 'toggleDarkMode') {
111
+ setMode(mode === 'dark' ? 'light' : 'dark');
112
+ return;
113
+ }
114
+
115
+ if (action.type === 'search') {
116
+ consoleImpl.log('[Ankh Action] Search:', action.payload);
117
+ return;
118
+ }
119
+
120
+ if (action.type === 'filter') {
121
+ consoleImpl.log('[Ankh Action] Filter:', action.payload);
122
+ return;
123
+ }
124
+
125
+ if (action.type === 'alert') {
126
+ const message = action.payload?.message ?? JSON.stringify(action.payload);
127
+ alertImpl?.(message);
128
+ return;
129
+ }
130
+
131
+ if (action.type === 'console') {
132
+ consoleImpl.log('[Ankh Action]', action.payload);
133
+ return;
134
+ }
135
+
136
+ const configuredHandler = actionHandlers?.[action.type];
137
+ if (configuredHandler) {
138
+ await configuredHandler({ action });
139
+ }
140
+ }
package/src/index.ts CHANGED
@@ -10,9 +10,23 @@ export {
10
10
  export type { ComponentRegistry } from './componentRegistry';
11
11
  export { createComponentRegistry } from './componentRegistry';
12
12
  export { createExpoRuntimeRegistry } from './createExpoRuntimeRegistry';
13
+ export {
14
+ executeExpoRuntimeAction,
15
+ type ExecuteExpoRuntimeActionArgs,
16
+ type ExpoRuntimeActionHandlerArgs,
17
+ type ExpoRuntimeActionHandlers,
18
+ type ExpoRuntimeRouteResolution,
19
+ type ExpoRuntimeRouterLike,
20
+ type ExpoRuntimeThemeMode,
21
+ resolveExpoRuntimeRoutePath,
22
+ } from './expoActionBridge';
13
23
  export { ExpoBarcodeScannerAdapter } from './ExpoBarcodeScannerAdapter';
14
24
  export { ExpoRuntimeProviders, type ExpoRuntimeProvidersProps } from './ExpoRuntimeProviders';
15
25
  export { getExpoBarcodeScannerViewSource } from './generatedSources';
26
+ export {
27
+ type ExpoRuntimeLayoutIntegrationPlan,
28
+ resolveExpoRuntimeLayoutIntegration,
29
+ } from './layoutIntegrationPlanning';
16
30
  export type {
17
31
  ExpoRuntimeAdapterId,
18
32
  ExpoRuntimePlan,
@@ -0,0 +1,55 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+
3
+ import { resolveExpoRuntimeLayoutIntegration } from './layoutIntegrationPlanning';
4
+ import type { ExpoRuntimePlan } from './resolveExpoRuntimePlan';
5
+
6
+ const EMPTY_PLAN: ExpoRuntimePlan = {
7
+ permissions: [],
8
+ capabilities: [],
9
+ impliedPermissions: [],
10
+ dependencies: [],
11
+ nativeConfig: {
12
+ androidPermissions: [],
13
+ configHints: [],
14
+ plugins: [],
15
+ },
16
+ providers: [],
17
+ runtimeAdapters: [],
18
+ usesExpoRuntimeRegistry: false,
19
+ needsPermissionsProvider: false,
20
+ diagnostics: [],
21
+ };
22
+
23
+ const PERMISSIONS_PROVIDER_START = "<ExpoRuntimeProviders providers={['permissions']}>";
24
+
25
+ describe('layoutIntegrationPlanning', () => {
26
+ test('returns an empty integration plan when no providers are required', () => {
27
+ expect(resolveExpoRuntimeLayoutIntegration(undefined)).toEqual({
28
+ imports: [],
29
+ moduleDeclarations: [],
30
+ providerStart: [],
31
+ providerEnd: [],
32
+ });
33
+ expect(resolveExpoRuntimeLayoutIntegration(EMPTY_PLAN)).toEqual({
34
+ imports: [],
35
+ moduleDeclarations: [],
36
+ providerStart: [],
37
+ providerEnd: [],
38
+ });
39
+ });
40
+
41
+ test('adds ExpoRuntimeProviders import and wrapper when providers are required', () => {
42
+ expect(
43
+ resolveExpoRuntimeLayoutIntegration({
44
+ ...EMPTY_PLAN,
45
+ providers: ['permissions'],
46
+ needsPermissionsProvider: true,
47
+ }),
48
+ ).toEqual({
49
+ imports: ["import { ExpoRuntimeProviders } from '@ankhorage/expo-runtime';"],
50
+ moduleDeclarations: [],
51
+ providerStart: [PERMISSIONS_PROVIDER_START],
52
+ providerEnd: ['</ExpoRuntimeProviders>'],
53
+ });
54
+ });
55
+ });
@@ -0,0 +1,39 @@
1
+ import type { ExpoRuntimePlan } from './resolveExpoRuntimePlan';
2
+
3
+ export interface ExpoRuntimeLayoutIntegrationPlan {
4
+ readonly imports: readonly string[];
5
+ readonly moduleDeclarations: readonly string[];
6
+ readonly providerStart: readonly string[];
7
+ readonly providerEnd: readonly string[];
8
+ }
9
+
10
+ export function resolveExpoRuntimeLayoutIntegration(
11
+ runtimePlan: ExpoRuntimePlan | undefined,
12
+ ): ExpoRuntimeLayoutIntegrationPlan {
13
+ const imports: string[] = [];
14
+ const expoRuntimeImports: string[] = [];
15
+ const moduleDeclarations: string[] = [];
16
+ const providerStart: string[] = [];
17
+ const providerEnd: string[] = [];
18
+
19
+ if ((runtimePlan?.providers.length ?? 0) > 0) {
20
+ expoRuntimeImports.push('ExpoRuntimeProviders');
21
+ providerStart.push(
22
+ `<ExpoRuntimeProviders providers={['${(runtimePlan?.providers ?? []).join("', '")}']}>`,
23
+ );
24
+ providerEnd.push('</ExpoRuntimeProviders>');
25
+ }
26
+
27
+ if (expoRuntimeImports.length > 0) {
28
+ imports.push(
29
+ `import { ${Array.from(new Set(expoRuntimeImports)).join(', ')} } from '@ankhorage/expo-runtime';`,
30
+ );
31
+ }
32
+
33
+ return {
34
+ imports,
35
+ moduleDeclarations,
36
+ providerStart,
37
+ providerEnd,
38
+ };
39
+ }
package/src/planning.ts CHANGED
@@ -1,4 +1,8 @@
1
1
  export { getExpoBarcodeScannerViewSource } from './generatedSources';
2
+ export {
3
+ type ExpoRuntimeLayoutIntegrationPlan,
4
+ resolveExpoRuntimeLayoutIntegration,
5
+ } from './layoutIntegrationPlanning';
2
6
  export type {
3
7
  ExpoRuntimeAdapterId,
4
8
  ExpoRuntimePlan,