@ankhorage/expo-runtime 0.0.4 → 0.0.6

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.6
4
+
5
+ ### Patch Changes
6
+
7
+ - 5f947ed: Add Expo-aware runtime action bridge helpers for navigation, theme toggling, logging, alerts, and delegated action handlers.
8
+
9
+ ## 0.0.5
10
+
11
+ ### Patch Changes
12
+
13
+ - 0573919: Add a generated barcode scanner adapter source helper for standalone Expo app generation.
14
+
3
15
  ## 0.0.4
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"]}
@@ -0,0 +1,2 @@
1
+ export declare function getExpoBarcodeScannerViewSource(): string;
2
+ //# sourceMappingURL=generatedSources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generatedSources.d.ts","sourceRoot":"","sources":["../src/generatedSources.ts"],"names":[],"mappings":"AAMA,wBAAgB,+BAA+B,IAAI,MAAM,CAExD"}
@@ -0,0 +1,9 @@
1
+ const EXPO_BARCODE_SCANNER_VIEW_SOURCE = [
2
+ 'export { ExpoBarcodeScannerAdapter as ExpoBarcodeScannerView }',
3
+ ' from "@ankhorage/expo-runtime";',
4
+ '\n',
5
+ ];
6
+ export function getExpoBarcodeScannerViewSource() {
7
+ return EXPO_BARCODE_SCANNER_VIEW_SOURCE.join('');
8
+ }
9
+ //# sourceMappingURL=generatedSources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generatedSources.js","sourceRoot":"","sources":["../src/generatedSources.ts"],"names":[],"mappings":"AAAA,MAAM,gCAAgC,GAAG;IACvC,gEAAgE;IAChE,kCAAkC;IAClC,IAAI;CACI,CAAC;AAEX,MAAM,UAAU,+BAA+B;IAC7C,OAAO,gCAAgC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACnD,CAAC","sourcesContent":["const EXPO_BARCODE_SCANNER_VIEW_SOURCE = [\n 'export { ExpoBarcodeScannerAdapter as ExpoBarcodeScannerView }',\n ' from \"@ankhorage/expo-runtime\";',\n '\\n',\n] as const;\n\nexport function getExpoBarcodeScannerViewSource(): string {\n return EXPO_BARCODE_SCANNER_VIEW_SOURCE.join('');\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -2,8 +2,10 @@ 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';
8
+ export { getExpoBarcodeScannerViewSource } from './generatedSources';
7
9
  export type { ExpoRuntimeAdapterId, ExpoRuntimePlan, ExpoRuntimeProviderId, } from './resolveExpoRuntimePlan';
8
10
  export { resolveExpoRuntimePlan } from './resolveExpoRuntimePlan';
9
11
  //# 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,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,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,7 +1,9 @@
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';
7
+ export { getExpoBarcodeScannerViewSource } from './generatedSources';
6
8
  export { resolveExpoRuntimePlan } from './resolveExpoRuntimePlan';
7
9
  //# 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;AAM9F,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 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;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 {\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 type {\n ExpoRuntimeAdapterId,\n ExpoRuntimePlan,\n ExpoRuntimeProviderId,\n} from './resolveExpoRuntimePlan';\nexport { resolveExpoRuntimePlan } from './resolveExpoRuntimePlan';\n"]}
@@ -1,3 +1,4 @@
1
+ export { getExpoBarcodeScannerViewSource } from './generatedSources';
1
2
  export type { ExpoRuntimeAdapterId, ExpoRuntimePlan, ExpoRuntimeProviderId, } from './resolveExpoRuntimePlan';
2
3
  export { resolveExpoRuntimePlan } from './resolveExpoRuntimePlan';
3
4
  //# sourceMappingURL=planning.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"planning.d.ts","sourceRoot":"","sources":["../src/planning.ts"],"names":[],"mappings":"AAAA,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,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,2 +1,3 @@
1
+ export { getExpoBarcodeScannerViewSource } from './generatedSources';
1
2
  export { resolveExpoRuntimePlan } from './resolveExpoRuntimePlan';
2
3
  //# sourceMappingURL=planning.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"planning.js","sourceRoot":"","sources":["../src/planning.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC","sourcesContent":["export 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;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"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ankhorage/expo-runtime",
3
3
  "type": "module",
4
- "version": "0.0.4",
4
+ "version": "0.0.6",
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": {
@@ -22,7 +22,7 @@
22
22
  "runtime"
23
23
  ],
24
24
  "dependencies": {
25
- "@ankhorage/contracts": "^1.18.2",
25
+ "@ankhorage/contracts": "^1.18.3",
26
26
  "@ankhorage/permissions": "^0.2.0",
27
27
  "@ankhorage/zora": "^2.8.0"
28
28
  },
@@ -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
+ }
@@ -0,0 +1,9 @@
1
+ const EXPO_BARCODE_SCANNER_VIEW_SOURCE = [
2
+ 'export { ExpoBarcodeScannerAdapter as ExpoBarcodeScannerView }',
3
+ ' from "@ankhorage/expo-runtime";',
4
+ '\n',
5
+ ] as const;
6
+
7
+ export function getExpoBarcodeScannerViewSource(): string {
8
+ return EXPO_BARCODE_SCANNER_VIEW_SOURCE.join('');
9
+ }
package/src/index.ts CHANGED
@@ -10,8 +10,19 @@ 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';
25
+ export { getExpoBarcodeScannerViewSource } from './generatedSources';
15
26
  export type {
16
27
  ExpoRuntimeAdapterId,
17
28
  ExpoRuntimePlan,
package/src/planning.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export { getExpoBarcodeScannerViewSource } from './generatedSources';
1
2
  export type {
2
3
  ExpoRuntimeAdapterId,
3
4
  ExpoRuntimePlan,