akanjs 3.0.0-beta.23 → 3.0.0-beta.24

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 (97) hide show
  1. package/client/csrTypes.ts +27 -1
  2. package/client/native.ts +3 -0
  3. package/client/route/RouteDefinition.ts +41 -1
  4. package/package.json +16 -6
  5. package/server/akanApp.ts +2 -3
  6. package/server/artifact/builderRpc.ts +5 -0
  7. package/server/artifact/ipcTypes.ts +10 -0
  8. package/server/hmr/clientScript.ts +31 -2
  9. package/server/hmr/csrDevManifest.ts +25 -0
  10. package/server/hmr/csrDevRuntime.ts +474 -0
  11. package/server/hmr/csrDevShell.ts +111 -0
  12. package/server/hmr/csrDevSourceMap.ts +142 -0
  13. package/server/hmr/devHmrController.ts +33 -16
  14. package/server/hmr/runtimeMetadataFile.ts +17 -0
  15. package/server/hmr/wsHub.ts +30 -4
  16. package/server/routing/apiRouter.ts +2 -0
  17. package/server/types.tsx +2 -0
  18. package/server/vendor/react-dom.ts +1 -0
  19. package/server/vendor/react-refresh-runtime.ts +13 -1
  20. package/server/vendor/react.ts +2 -0
  21. package/server/webRouter.ts +32 -2
  22. package/store/agent/ScreenTarget.ts +1 -2
  23. package/store/agent/StoreSurfaceSource.ts +3 -1
  24. package/store/storeInstance.ts +28 -21
  25. package/types/client/csrTypes.d.ts +22 -1
  26. package/types/client/native.d.ts +1 -0
  27. package/types/client/route/RouteDefinition.d.ts +14 -0
  28. package/types/server/artifact/builderRpc.d.ts +2 -1
  29. package/types/server/artifact/ipcTypes.d.ts +11 -0
  30. package/types/server/hmr/csrDevManifest.d.ts +18 -0
  31. package/types/server/hmr/csrDevRuntime.d.ts +71 -0
  32. package/types/server/hmr/csrDevShell.d.ts +14 -0
  33. package/types/server/hmr/csrDevSourceMap.d.ts +17 -0
  34. package/types/server/hmr/devHmrController.d.ts +2 -1
  35. package/types/server/hmr/runtimeMetadataFile.d.ts +1 -0
  36. package/types/server/hmr/wsHub.d.ts +11 -0
  37. package/types/server/routing/apiRouter.d.ts +1 -0
  38. package/types/server/types.d.ts +2 -0
  39. package/types/server/vendor/react-dom.d.ts +1 -1
  40. package/types/server/vendor/react-refresh-runtime.d.ts +1 -1
  41. package/types/server/vendor/react.d.ts +1 -1
  42. package/types/store/storeInstance.d.ts +4 -3
  43. package/types/ui/System/Client.d.ts +5 -3
  44. package/types/vendor/@akanjs/native/plugins/app/src/index.d.ts +17 -0
  45. package/types/vendor/@akanjs/native/plugins/app-state/src/index.d.ts +6 -0
  46. package/types/vendor/use-agentic/AgentActivity.d.ts +11 -0
  47. package/types/vendor/use-agentic/AgentGate.d.ts +10 -0
  48. package/types/vendor/use-agentic/AgenticSurface.d.ts +15 -5
  49. package/types/vendor/use-agentic/index.d.ts +2 -0
  50. package/types/vendor/use-agentic/surfaceContext.d.ts +3 -0
  51. package/types/webkit/CsrRouteTable.d.ts +32 -0
  52. package/types/webkit/CsrStack.d.ts +45 -0
  53. package/types/webkit/RenderLayer.d.ts +2 -2
  54. package/types/webkit/bootCsr.d.ts +7 -8
  55. package/types/webkit/index.d.ts +4 -1
  56. package/types/webkit/nativeNavigation.d.ts +13 -0
  57. package/types/webkit/useCsrValues.d.ts +6 -4
  58. package/types/webkit/useFetch.d.ts +3 -1
  59. package/types/webkit/useHistory.d.ts +5 -1
  60. package/types/webkit/usePageActivity.d.ts +2 -0
  61. package/types/webkit/usePageFocusEffect.d.ts +7 -0
  62. package/types/webkit/usePageLocation.d.ts +13 -0
  63. package/ui/Data/Dashboard.tsx +2 -2
  64. package/ui/Data/ListContainer.tsx +2 -2
  65. package/ui/Layout/BottomInset.tsx +1 -1
  66. package/ui/Layout/Navbar.tsx +1 -1
  67. package/ui/Layout/TopInset.tsx +4 -3
  68. package/ui/System/CSR.tsx +40 -79
  69. package/ui/System/Client.tsx +28 -13
  70. package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeActivity.kt +53 -9
  71. package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativePlugin.kt +16 -0
  72. package/vendor/@akanjs/native/plugins/app/android/AppPlugin.kt +20 -0
  73. package/vendor/@akanjs/native/plugins/app/native-plugin.json +4 -4
  74. package/vendor/@akanjs/native/plugins/app/src/desktop.ts +3 -0
  75. package/vendor/@akanjs/native/plugins/app/src/index.ts +14 -2
  76. package/vendor/@akanjs/native/plugins/app-state/android/AppStatePlugin.kt +38 -2
  77. package/vendor/@akanjs/native/plugins/app-state/ios/AppStatePlugin.swift +19 -0
  78. package/vendor/@akanjs/native/plugins/app-state/native-plugin.json +1 -1
  79. package/vendor/@akanjs/native/plugins/app-state/src/index.ts +6 -1
  80. package/vendor/use-agentic/AgentActivity.tsx +26 -0
  81. package/vendor/use-agentic/AgentGate.ts +25 -0
  82. package/vendor/use-agentic/AgenticSurface.ts +92 -34
  83. package/vendor/use-agentic/index.ts +2 -0
  84. package/vendor/use-agentic/surfaceContext.ts +3 -0
  85. package/webkit/CsrRouteTable.ts +298 -0
  86. package/webkit/CsrStack.ts +98 -0
  87. package/webkit/RenderLayer.tsx +30 -8
  88. package/webkit/bootCsr.tsx +13 -217
  89. package/webkit/index.ts +4 -1
  90. package/webkit/nativeNavigation.ts +78 -5
  91. package/webkit/useCsrValues.ts +118 -22
  92. package/webkit/useFetch.ts +9 -2
  93. package/webkit/useHistory.ts +23 -6
  94. package/webkit/useLocation.ts +6 -3
  95. package/webkit/usePageActivity.ts +6 -0
  96. package/webkit/usePageFocusEffect.ts +13 -0
  97. package/webkit/usePageLocation.ts +17 -0
@@ -103,6 +103,9 @@ export type LayoutErrorRender = (props: LayoutErrorProps) => PromiseOrObject<Rea
103
103
  export interface RouteRender {
104
104
  render: LayoutRender | PageRender;
105
105
  isAsync?: boolean;
106
+ /** CSR: a page's render reads the query; a layout's reads only the params of its own path (`paramNames`). */
107
+ kind?: "page" | "layout";
108
+ paramNames?: string[];
106
109
  Loading?: LayoutLoadingRender | PageLoadingRender;
107
110
  /** Loads the module and fills `Loading` without running `render`/`resolveHead` (the suffix compose path). */
108
111
  resolveLoading?: () => void | Promise<void>;
@@ -221,6 +224,7 @@ export interface Location {
221
224
  searchParams: { [key: string]: string | string[] };
222
225
  pathRoute: PathRoute;
223
226
  hash: string;
227
+ entryId?: string;
224
228
  }
225
229
  export type CsrNavigationPhase = "idle" | "preparing" | "transitioning";
226
230
  export type CsrNavigationKind = "push" | "replace" | "back" | "popForward" | "popBack";
@@ -247,6 +251,7 @@ export interface History {
247
251
  idxMap: Map<string, number>;
248
252
  cachedLocationMap: Map<string, Location>;
249
253
  idx: number;
254
+ dormant?: Set<string>;
250
255
  }
251
256
 
252
257
  export interface RouterProps {
@@ -256,14 +261,24 @@ export interface RouterProps {
256
261
  back: () => void | Promise<void>;
257
262
  }
258
263
 
264
+ export type CsrPageType = "current" | "prev" | "pending" | "cached";
265
+ export interface CsrStackEntry {
266
+ key: string;
267
+ location: Location;
268
+ pageType: CsrPageType;
269
+ zIndex: number;
270
+ }
271
+
259
272
  export interface RouteState {
260
273
  clientWidth: number;
261
274
  clientHeight: number;
262
275
  location: Location;
263
276
  prevLocation: Location | null;
264
277
  pendingLocation: Location | null;
278
+ stackEntries: CsrStackEntry[];
265
279
  navigationIntent: NavigationIntent | null;
266
280
  phase: CsrNavigationPhase;
281
+ isBackgrounded: boolean;
267
282
  history: RefObject<History>;
268
283
  topSafeAreaRef: RefObject<HTMLDivElement | null>;
269
284
  bottomSafeAreaRef: RefObject<HTMLDivElement | null>;
@@ -304,7 +319,8 @@ export const useCsr = () => {
304
319
  };
305
320
 
306
321
  export interface PathContextType {
307
- pageType: "current" | "prev" | "cached" | "pending";
322
+ pageType: CsrPageType;
323
+ pageKey?: string;
308
324
  location: Location;
309
325
  prefix?: string;
310
326
  gestureEnabled: boolean;
@@ -317,6 +333,16 @@ export const usePathCtx = () => {
317
333
  return contextValues;
318
334
  };
319
335
 
336
+ export type PageActivity = "current" | "prev" | "pending" | "hidden";
337
+ export interface PageActivityState {
338
+ activity: PageActivity;
339
+ focused: boolean;
340
+ }
341
+ export const pageActivityContext = sharedContext<PageActivityState>("pageActivity", {
342
+ activity: "current",
343
+ focused: true,
344
+ });
345
+
320
346
  export interface PathRoute {
321
347
  path: string;
322
348
  pathSegments: string[];
package/client/native.ts CHANGED
@@ -35,6 +35,9 @@ export const isNativeApp = () => isNative && (platform === "ios" || platform ===
35
35
 
36
36
  export const nativePlatform = () => (isNativeApp() ? (platform as "ios" | "android") : null);
37
37
 
38
+ export const desktopPlatform = () =>
39
+ isNative && (platform === "macos" || platform === "windows" || platform === "linux") ? platform : null;
40
+
38
41
  /** http(s) in the in-app browser, anything else (mailto:, tel:) in the app the system picks. */
39
42
  export const openExternalUrl = async (url: string) => {
40
43
  if (!isNativeApp()) {
@@ -17,6 +17,8 @@ export type RouteArgsShape = Record<string, unknown>;
17
17
  export type RouteKind = "page" | "layout" | "rootLayout";
18
18
  /** Interned: a definition built inside the pages bundle must be recognised by a loader bundled apart from it. */
19
19
  export const routeDefinitionMarker = Symbol.for("akan.routeDefinition");
20
+ /** Interned like the marker: the declared args ride on the module's render function, read by the CSR render cache. */
21
+ export const routeArgsMarker = Symbol.for("akan.routeArgs");
20
22
 
21
23
  type HeadStage<Args> = Head | ((args: Args) => PromiseOrObject<Head | null | undefined>);
22
24
 
@@ -44,6 +46,11 @@ export abstract class RouteDefinition<
44
46
  #head?: HeadStage<never>;
45
47
  #loading?: (args: never) => PromiseOrObject<ReactNode>;
46
48
  #render?: (args: never) => PromiseOrObject<ReactNode>;
49
+ #renderRoute?: {
50
+ render: (args: never) => PromiseOrObject<ReactNode>;
51
+ argCount: number;
52
+ route: (props: RouteRenderProps) => Promise<ReactNode>;
53
+ };
47
54
 
48
55
  config(config: PageConfig) {
49
56
  this.#config = config;
@@ -122,7 +129,7 @@ export abstract class RouteDefinition<
122
129
  const head = this.#head;
123
130
  const loading = this.#loading;
124
131
  const module: PageModule & LayoutModule = {
125
- default: (async (props: RouteRenderProps) => await render(this.#argsOf(props) as never)) as never,
132
+ default: this.#renderRouteOf(render) as never,
126
133
  ...(this.#config ? { pageConfig: this.#config } : {}),
127
134
  ...(head === undefined
128
135
  ? {}
@@ -143,6 +150,39 @@ export abstract class RouteDefinition<
143
150
  return module;
144
151
  }
145
152
 
153
+ #renderRouteOf(render: (args: never) => PromiseOrObject<ReactNode>) {
154
+ const kept = this.#renderRoute;
155
+ if (kept?.render === render && kept.argCount === this.args.length) return kept.route;
156
+ const route = async (props: RouteRenderProps) => await render(this.#argsOf(props) as never);
157
+ Object.defineProperty(route, routeArgsMarker, { value: this.args.map(({ kind, name }) => ({ kind, name })) });
158
+ this.#renderRoute = { render, argCount: this.args.length, route };
159
+ return route;
160
+ }
161
+
162
+ /**
163
+ * What a route's render output depends on: a chain's declared args plus `lang`. A legacy module declares nothing,
164
+ * so it depends on the params of its own path (`paramNames`, every param when unknown) and, for a page, every
165
+ * search value — a layout reads no query.
166
+ */
167
+ static renderArgsKey(
168
+ render: unknown,
169
+ props: { params: Record<string, string>; searchParams?: Record<string, string | string[]> },
170
+ { isPage, paramNames }: { isPage: boolean; paramNames?: string[] },
171
+ ): string {
172
+ const declared = (render as { [routeArgsMarker]?: Pick<RouteArgInfo, "kind" | "name">[] } | null)?.[
173
+ routeArgsMarker
174
+ ];
175
+ const searchParams = props.searchParams ?? {};
176
+ if (!declared) {
177
+ const params = paramNames ? paramNames.map((name) => props.params[name]) : props.params;
178
+ return JSON.stringify([params, isPage ? searchParams : null]);
179
+ }
180
+ return JSON.stringify([
181
+ props.params.lang,
182
+ ...declared.map(({ kind, name }) => (kind === "param" ? props.params[name] : searchParams[name])),
183
+ ]);
184
+ }
185
+
146
186
  #argsOf(props: RouteRenderProps): RouteBaseArgs & Args & Extra {
147
187
  const { lang = parseAkanI18nEnv().defaultLocale } = props.params;
148
188
  const resolved = this.resolveArgs({ params: props.params, searchParams: props.searchParams ?? {} });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akanjs",
3
- "version": "3.0.0-beta.23",
3
+ "version": "3.0.0-beta.24",
4
4
  "sourceType": "module",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -200,6 +200,16 @@
200
200
  "types": "./types/server/logging/logTailClient.d.ts",
201
201
  "import": "./server/logging/logTailClient.ts",
202
202
  "default": "./server/logging/logTailClient.ts"
203
+ },
204
+ "./server/hmr/csrDevManifest": {
205
+ "types": "./types/server/hmr/csrDevManifest.d.ts",
206
+ "import": "./server/hmr/csrDevManifest.ts",
207
+ "default": "./server/hmr/csrDevManifest.ts"
208
+ },
209
+ "./server/hmr/runtimeMetadataFile": {
210
+ "types": "./types/server/hmr/runtimeMetadataFile.d.ts",
211
+ "import": "./server/hmr/runtimeMetadataFile.ts",
212
+ "default": "./server/hmr/runtimeMetadataFile.ts"
203
213
  }
204
214
  },
205
215
  "dependencies": {
@@ -224,13 +234,13 @@
224
234
  "ioredis": "^5.10.1",
225
235
  "postgres": "^3.4.9",
226
236
  "protobufjs": "^8.4.0",
227
- "react": "19.2.7",
228
- "react-dom": "19.2.7",
237
+ "react": "19.3.0",
238
+ "react-dom": "19.3.0",
229
239
  "react-icons": "^5.6.0",
230
- "react-refresh": "^0.18.0",
231
- "react-server-dom-webpack": "^19.2.7",
240
+ "react-refresh": "^0.19.0",
241
+ "react-server-dom-webpack": "^19.3.0",
232
242
  "react-spring": "^10.0.4",
233
- "scheduler": "^0.27.0",
243
+ "scheduler": "^0.28.0",
234
244
  "tailwind-scrollbar": "^4.0.2",
235
245
  "typescript": "^6.0.3"
236
246
  },
package/server/akanApp.ts CHANGED
@@ -762,9 +762,7 @@ export class AkanApp {
762
762
  return await this.#proxyResponse(req, upstreamRes);
763
763
  } catch (error) {
764
764
  if (AkanApp.#isUpstreamOpenFailure(error)) {
765
- this.logger.error(
766
- `Child ${child.idx}/${child.role} upstream is unreachable (${socketPath}); restarting`,
767
- );
765
+ this.logger.error(`Child ${child.idx}/${child.role} upstream is unreachable (${socketPath}); restarting`);
768
766
  this.#scheduleChildRestart(child, child.proc, "upstream-open-failed");
769
767
  return AkanApp.#unavailableResponse(req, "Federation child upstream is unreachable; restarting");
770
768
  }
@@ -1055,6 +1053,7 @@ export class AkanApp {
1055
1053
  case "invalidate":
1056
1054
  case "css-updated":
1057
1055
  case "pages-updated":
1056
+ case "csr-updated":
1058
1057
  case "build-status":
1059
1058
  this.#fanoutToFederation(message);
1060
1059
  return;
@@ -8,6 +8,7 @@ import type {
8
8
  BuilderRes,
9
9
  BuildRouteClientResult,
10
10
  BuildRouteResultPayload,
11
+ CsrUpdatedPayload,
11
12
  CssPayload,
12
13
  DevBuildStatus,
13
14
  PagesBundlePayload,
@@ -19,6 +20,7 @@ export interface BuilderRpcEventHandlers {
19
20
  onCssUpdated?: (css: CssPayload) => void;
20
21
  /** A fresh `pages-*.js` bundle: re-import it in the running worker (`RscWorker.reload`) rather than respawning. */
21
22
  onPagesUpdated?: (bundle: PagesBundlePayload) => void;
23
+ onCsrUpdated?: (update: CsrUpdatedPayload) => void;
22
24
  onBuildStatus?: (status: DevBuildStatus) => void;
23
25
  }
24
26
 
@@ -59,6 +61,9 @@ export class BuilderRpc {
59
61
  case "pages-updated":
60
62
  handlers.onPagesUpdated?.(ev.data);
61
63
  return;
64
+ case "csr-updated":
65
+ handlers.onCsrUpdated?.(ev.data);
66
+ return;
62
67
  case "build-status":
63
68
  handlers.onBuildStatus?.(ev.data);
64
69
  return;
@@ -94,6 +94,15 @@ export interface BuilderMetrics {
94
94
  workCount: number;
95
95
  }
96
96
 
97
+ export interface CsrUpdatedPayload {
98
+ generation: number;
99
+ mode: "registry" | "artifact";
100
+ reload: boolean;
101
+ reason?: string;
102
+ patchUrl?: string;
103
+ changedIds?: string[];
104
+ }
105
+
97
106
  export type BuilderEvent =
98
107
 
99
108
  | { type: "builder-ready"; buildId?: string }
@@ -107,6 +116,7 @@ export type BuilderEvent =
107
116
  }
108
117
  | { type: "css-updated"; data: CssPayload }
109
118
  | { type: "pages-updated"; data: PagesBundlePayload }
119
+ | { type: "csr-updated"; data: CsrUpdatedPayload }
110
120
  | { type: "build-status"; data: DevBuildStatus }
111
121
  | { type: "builder-metrics"; data: BuilderMetrics };
112
122
 
@@ -5,8 +5,9 @@ export const HMR_CLIENT_SCRIPT = `(function(){
5
5
  self.__AKAN_HMR_INSTALLED__ = true;
6
6
  var syncNavigationEnabled = ${JSON.stringify(isSyncNavigationEnabled())};
7
7
  var syncNavigationClientId = Math.random().toString(36).slice(2) + Date.now().toString(36);
8
+ var clientKind = self.__AKAN_HMR_CLIENT__ === "csr" ? "csr" : "ssr";
8
9
  var proto = location.protocol === "https:" ? "wss:" : "ws:";
9
- var url = proto + "//" + location.host + "/_akan/hmr";
10
+ var url = proto + "//" + location.host + "/_akan/hmr" + (clientKind === "csr" ? "?client=csr" : "");
10
11
  var attempts = 0;
11
12
  var socket = null;
12
13
  var lastBuildId = null;
@@ -43,7 +44,7 @@ export const HMR_CLIENT_SCRIPT = `(function(){
43
44
  else pendingRefreshRegistrations.push([type, id]);
44
45
  };
45
46
  self.$RefreshSig$ = self.$RefreshSig$ || function(){ return function(type){ return type; }; };
46
- ensureRefreshRuntime().catch(function(err){
47
+ if (clientKind === "ssr") ensureRefreshRuntime().catch(function(err){
47
48
  console.warn("[akan-hmr] React Refresh runtime preload failed", err);
48
49
  });
49
50
 
@@ -56,6 +57,10 @@ export const HMR_CLIENT_SCRIPT = `(function(){
56
57
  try { msg = JSON.parse(ev.data); } catch (e){ return; }
57
58
  if (!msg || typeof msg.type !== "string") return;
58
59
  if (msg.type === "hello") {
60
+ if (clientKind === "csr") {
61
+ if (csrGenerationMoved(msg.csrGeneration)) reloadForCsr("missed a CSR update while disconnected");
62
+ return;
63
+ }
59
64
  if (lastBuildId !== null && msg.buildId !== lastBuildId) {
60
65
  location.reload();
61
66
  return;
@@ -77,6 +82,10 @@ export const HMR_CLIENT_SCRIPT = `(function(){
77
82
  refreshClient(msg);
78
83
  return;
79
84
  }
85
+ if (msg.type === "csr-update") {
86
+ applyCsrUpdate(msg);
87
+ return;
88
+ }
80
89
  if (msg.type === "css-update") {
81
90
  var cssUrl = selectCssUrl(msg.cssAssets);
82
91
  if (cssUrl) swapCss(cssUrl);
@@ -112,6 +121,26 @@ export const HMR_CLIENT_SCRIPT = `(function(){
112
121
  socket.addEventListener("error", function(){ try { socket && socket.close(); } catch(e){} });
113
122
  }
114
123
 
124
+ function csrGenerationMoved(generation){
125
+ if (typeof generation !== "number") return false;
126
+ var current = self.__akan ? self.__akan.generation : self.__AKAN_CSR_GENERATION__;
127
+ return typeof current === "number" && current !== generation;
128
+ }
129
+
130
+ function reloadForCsr(reason){
131
+ console.warn("[akan-hmr] reloading the CSR page: " + reason);
132
+ beginHmrOverlay("Reloading...", true);
133
+ setTimeout(function(){ location.reload(); }, 30);
134
+ }
135
+
136
+ function applyCsrUpdate(msg){
137
+ if (msg.reload || !self.__akan || typeof self.__akan.hot !== "function") {
138
+ reloadForCsr(msg.reason || "the CSR bundle was rebuilt");
139
+ return;
140
+ }
141
+ self.__akan.hot(msg);
142
+ }
143
+
115
144
  function schedule(){
116
145
  attempts = Math.min(attempts + 1, 6);
117
146
  var delay = Math.min(30000, 250 * Math.pow(2, attempts - 1));
@@ -0,0 +1,25 @@
1
+ export const CSR_DEV_DIRNAME = "csr-dev";
2
+ export const CSR_DEV_ROUTE_PREFIX = "/_akan/csr-dev/";
3
+ export const CSR_DEV_APP_FILE = "app.js";
4
+ export const CSR_DEV_MANIFEST_FILE = "manifest.json";
5
+
6
+ export type DevCsrMode = "registry" | "artifact";
7
+
8
+ export interface CsrDevManifest {
9
+ version: 1;
10
+ generation: number;
11
+ vendorFile: string;
12
+ entries: Record<string, string>;
13
+ }
14
+
15
+ /** Where each module's generated code starts in a combined file (`app.js`, a patch), for its source map. */
16
+ export interface CsrDevLayout {
17
+ lineCount: number;
18
+ modules: [id: string, line: number][];
19
+ }
20
+
21
+ export const csrDevModuleFile = (id: string, extension: ".js" | ".js.map") =>
22
+ `modules/${Bun.hash(id).toString(36)}${extension}`;
23
+
24
+ export const resolveDevCsrMode = (env: Record<string, string | undefined> = process.env): DevCsrMode =>
25
+ env.AKAN_DEV_CSR === "artifact" ? "artifact" : "registry";