@kokimoki/kit 1.6.6 → 1.7.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.
@@ -15,25 +15,110 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
15
15
  }) : function(o, v) {
16
16
  o["default"] = v;
17
17
  });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
25
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
26
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
37
  };
28
38
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.kokimokiKitPlugin = void 0;
30
- const bson_objectid_1 = __importDefault(require("bson-objectid"));
39
+ exports.getI18nMeta = void 0;
40
+ exports.kokimokiKitPlugin = kokimokiKitPlugin;
31
41
  const promises_1 = __importDefault(require("fs/promises"));
32
- const preprocess_style_1 = require("./preprocess-style");
33
- const version_1 = require("./version");
42
+ const path_1 = __importDefault(require("path"));
34
43
  const yaml = __importStar(require("yaml"));
35
44
  const v4_1 = require("zod/v4");
45
+ const api_1 = require("./api");
46
+ const credentials_1 = require("./credentials");
47
+ const dev_app_1 = require("./dev-app");
48
+ const dev_frame_1 = require("./dev-frame");
49
+ const dev_i18n_1 = require("./dev-i18n");
50
+ const dev_overlays_1 = require("./dev-overlays");
51
+ const preprocess_style_1 = require("./preprocess-style");
52
+ const production_loading_screen_1 = require("./production-loading-screen");
53
+ const version_1 = require("./version");
54
+ var dev_i18n_2 = require("./dev-i18n");
55
+ Object.defineProperty(exports, "getI18nMeta", { enumerable: true, get: function () { return dev_i18n_2.getI18nMeta; } });
36
56
  function kokimokiKitPlugin(config) {
57
+ // Cache for loaded i18n resources
58
+ let cachedI18n = null;
59
+ // Cache dev app info for syncing
60
+ let devAppInfo = null;
61
+ // Initialization state
62
+ let initState = "pending";
63
+ let initPromise = null;
64
+ // Cached dev app result
65
+ let cachedDevAppResult = null;
66
+ /**
67
+ * Start initialization if not already started.
68
+ * Returns a promise that resolves when initialization is complete.
69
+ */
70
+ function startInitialization() {
71
+ if (initPromise) {
72
+ return initPromise;
73
+ }
74
+ if (initState !== "pending") {
75
+ return Promise.resolve();
76
+ }
77
+ initState = "initializing";
78
+ initPromise = (async () => {
79
+ try {
80
+ // Get or create dev app
81
+ cachedDevAppResult = await (0, dev_app_1.getOrCreateDevApp)({
82
+ conceptId: config.conceptId,
83
+ endpoint: config.endpoint,
84
+ });
85
+ const { appId, buildUrl, organization, error: devAppError, } = cachedDevAppResult;
86
+ // Store dev app info for i18n syncing
87
+ if (organization && !devAppError) {
88
+ const credentials = await (0, credentials_1.readCredentials)();
89
+ const apiKey = credentials?.apiKeys?.[organization.id];
90
+ if (apiKey) {
91
+ devAppInfo = {
92
+ appId,
93
+ apiKey,
94
+ endpoint: config.endpoint ?? api_1.DEFAULT_ENDPOINT,
95
+ buildUrl,
96
+ };
97
+ // Sync i18n files to dev app on startup
98
+ const i18nResources = await getI18nResources();
99
+ await (0, dev_i18n_1.syncAllI18nToDevApp)(devAppInfo, i18nResources, config.i18nPrimaryLng ?? "en");
100
+ }
101
+ }
102
+ initState = "ready";
103
+ }
104
+ catch (e) {
105
+ console.error("[kokimoki-kit] Initialization error:", e);
106
+ initState = "error";
107
+ }
108
+ })();
109
+ return initPromise;
110
+ }
111
+ async function getI18nResources() {
112
+ if (cachedI18n)
113
+ return cachedI18n;
114
+ if (config.i18nPath) {
115
+ cachedI18n = await (0, dev_i18n_1.loadI18nFromPath)(config.i18nPath);
116
+ }
117
+ else {
118
+ cachedI18n = {};
119
+ }
120
+ return cachedI18n;
121
+ }
37
122
  return {
38
123
  name: "kokimoki-kit",
39
124
  async transform(code, id) {
@@ -44,9 +129,16 @@ function kokimokiKitPlugin(config) {
44
129
  return code;
45
130
  },
46
131
  async transformIndexHtml(html) {
132
+ // Load i18n resources (from path or config)
133
+ const i18nResources = await getI18nResources();
134
+ // Extract namespace names and language codes from i18n config
135
+ const i18nNamespaces = Object.keys(Object.values(i18nResources)[0] ?? {});
136
+ const i18nLanguages = Object.keys(i18nResources);
47
137
  if (process.env.NODE_ENV !== "development") {
138
+ // Remove any existing km-loading element from the HTML
139
+ const processedHtml = (0, production_loading_screen_1.removeExistingLoadingScreen)(html);
48
140
  // NOTE: Try https://github.com/jsdom/jsdom instead of regex parsing
49
- return html
141
+ const result = processedHtml
50
142
  .replace("<head>", `<head>
51
143
  <base href="%KM_BASE%">
52
144
  <script id="kokimoki-env" type="application/json">
@@ -58,6 +150,9 @@ function kokimokiKitPlugin(config) {
58
150
  "code": "%KM_CODE%",
59
151
  "clientContext": "%KM_CLIENT_CONTEXT%",
60
152
  "config": "%KM_CONFIG%",
153
+ "i18nPath": "km-i18n",
154
+ "i18nNamespaces": ${JSON.stringify(i18nNamespaces)},
155
+ "i18nLanguages": ${JSON.stringify(i18nLanguages)},
61
156
  "base": "%KM_BASE%",
62
157
  "assets": "%KM_ASSETS%"
63
158
  }
@@ -70,58 +165,67 @@ function kokimokiKitPlugin(config) {
70
165
 
71
166
  return "%KM_ASSETS%/" + path;
72
167
  };
73
- </script>
168
+ </script>${production_loading_screen_1.loadingScreenStyles}${production_loading_screen_1.loadingScreenScript}
74
169
  `)
170
+ .replace(/<body([^>]*)>/, `<body$1>${production_loading_screen_1.loadingScreenElement}`)
75
171
  .replace(/<link.*?href="(.*?)".*?>/g, (match, p1) => {
76
172
  return match.replace(p1, `%KM_ASSETS%${p1.startsWith("/") ? "" : "/"}${p1}`);
77
173
  })
78
174
  .replace(/<script.*?src="(.*?)".*?>/g, (match, p1) => {
79
175
  return match.replace(p1, `%KM_ASSETS%${p1.startsWith("/") ? "" : "/"}${p1}`);
80
176
  });
177
+ return result;
81
178
  }
82
- // Ensure .kokimoki directory exists
83
- try {
84
- await promises_1.default.mkdir(".kokimoki");
85
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
179
+ // Development mode: ensure initialization is started and wait for it
180
+ if (initState === "pending" || initState === "initializing") {
181
+ // Start initialization if not already started
182
+ startInitialization();
183
+ // Return loading page - it will poll and reload when ready
184
+ return (0, dev_overlays_1.renderLoadingPage)();
86
185
  }
87
- catch (e) {
88
- if (e?.code !== "EEXIST") {
89
- throw e;
90
- }
91
- }
92
- // Try to read the app id from the .kokimoki/app-id file
93
- let appId;
94
- try {
95
- appId = await promises_1.default.readFile(".kokimoki/app-id", "utf8");
96
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
186
+ // Get cached dev app result (should be available after initialization)
187
+ const { appId, buildUrl, organization, error: devAppError, } = cachedDevAppResult ?? {
188
+ appId: "",
189
+ buildUrl: undefined,
190
+ organization: undefined,
191
+ error: { code: "INIT_ERROR", message: "Initialization failed" },
192
+ };
193
+ // Check if stores configuration has changed
194
+ const currentStoresHash = (0, dev_app_1.computeStoresHash)(config.stores);
195
+ const previousStoresHash = await (0, dev_app_1.readStoresHash)();
196
+ const storesChanged = previousStoresHash !== null && previousStoresHash !== currentStoresHash;
197
+ // Write initial stores hash if it doesn't exist yet
198
+ if (previousStoresHash === null) {
199
+ await (0, dev_app_1.writeStoresHash)(currentStoresHash);
97
200
  }
98
- catch (e) {
99
- if (e?.code !== "ENOENT") {
100
- throw e;
101
- }
201
+ // Return full stores changed page if stores configuration has changed
202
+ if (storesChanged) {
203
+ return (0, dev_overlays_1.renderStoresChangedPage)(!!organization);
102
204
  }
103
- // If the app id doesn't exist, generate a new one
104
- if (!appId) {
105
- appId = new bson_objectid_1.default().toHexString();
106
- await promises_1.default.writeFile(".kokimoki/app-id", appId);
205
+ // Return full error page if there's a dev app error
206
+ if (devAppError) {
207
+ return (0, dev_overlays_1.renderErrorPage)(devAppError);
107
208
  }
108
209
  // Get default config
109
- // let defaultProjectConfig = config.schema.parse(undefined);
110
- // if (config.defaultProjectConfigPath) {
111
- const defaultProjectConfigFile = await promises_1.default.readFile(config.defaultProjectConfigPath, "utf8");
112
- const defaultProjectConfig = config.schema.parse(yaml.parse(defaultProjectConfigFile));
113
- // }
210
+ let defaultProjectConfig = {};
211
+ if (config.defaultProjectConfigPath && config.schema) {
212
+ const defaultProjectConfigFile = await promises_1.default.readFile(config.defaultProjectConfigPath, "utf8");
213
+ defaultProjectConfig = config.schema.parse(yaml.parse(defaultProjectConfigFile));
214
+ }
114
215
  // Inject the app id into the index.html
115
216
  html = html.replace("<head>", `<head>
116
217
  <script id="kokimoki-env" type="application/json">
117
218
  {
118
219
  "dev": true,
119
220
  "test": true,
120
- "host": "y-wss.kokimoki.com",
221
+ "host": "${config.host ?? "y-wss.kokimoki.com"}",
121
222
  "appId": "${appId}",
122
223
  "configObject": ${JSON.stringify(defaultProjectConfig)},
224
+ "i18nNamespaces": ${JSON.stringify(i18nNamespaces)},
225
+ "i18nLanguages": ${JSON.stringify(i18nLanguages)},
123
226
  "base": "/",
124
- "assets": "/"
227
+ "assets": "/",
228
+ "buildUrl": ${JSON.stringify(buildUrl ?? null)}
125
229
  }
126
230
  </script>`);
127
231
  // Inject default project style in development
@@ -143,6 +247,10 @@ function kokimokiKitPlugin(config) {
143
247
  throw e;
144
248
  }
145
249
  }
250
+ // Get i18n metadata
251
+ const i18n = config.i18nPath
252
+ ? await (0, dev_i18n_1.getI18nMeta)(config.i18nPath, config.i18nPrimaryLng ?? "en")
253
+ : null;
146
254
  // write config
147
255
  await promises_1.default.writeFile(".kokimoki/config.json", JSON.stringify({
148
256
  kokimokiKitVersion: version_1.KOKIMOKI_KIT_VERSION,
@@ -151,49 +259,174 @@ function kokimokiKitPlugin(config) {
151
259
  build: "dist",
152
260
  defaultProjectConfigPath: config.defaultProjectConfigPath,
153
261
  defaultProjectStylePath: config.defaultProjectStylePath,
262
+ i18n,
154
263
  }, null, 2));
155
264
  // write schema
156
- const jsonSchema = v4_1.z.toJSONSchema(config.schema, {
157
- override(ctx) {
158
- // check if schema is discriminated union
159
- if (ctx.zodSchema._zod.def.type === "union" &&
160
- "discriminator" in ctx.zodSchema._zod.def) {
161
- const discriminator = ctx.zodSchema._zod.def
162
- .discriminator;
163
- ctx.jsonSchema.type = "object";
164
- ctx.jsonSchema.discriminator = { propertyName: discriminator };
165
- ctx.jsonSchema.required = [discriminator];
166
- ctx.jsonSchema.oneOf = ctx.jsonSchema.anyOf.map((objectSchema) => ({
167
- properties: objectSchema.properties,
168
- additionalProperties: objectSchema.additionalProperties,
169
- required: (objectSchema.required ?? []).filter((prop) => prop !== discriminator),
170
- }));
171
- delete ctx.jsonSchema.anyOf;
172
- }
173
- // Remove fields that have a default from the required list
174
- if (ctx.jsonSchema.properties && ctx.jsonSchema.required) {
175
- const properties = ctx.jsonSchema.properties;
176
- ctx.jsonSchema.required = ctx.jsonSchema.required.filter((field) => !("default" in properties[field]));
177
- }
178
- // Make sure property has description if set in zod schema
179
- if ("description" in ctx.zodSchema) {
180
- ctx.jsonSchema.description = ctx.zodSchema.description;
181
- }
182
- },
183
- });
265
+ const jsonSchema = config.schema
266
+ ? v4_1.z.toJSONSchema(config.schema, {
267
+ override(ctx) {
268
+ // check if schema is discriminated union
269
+ if (ctx.zodSchema._zod.def.type === "union" &&
270
+ "discriminator" in ctx.zodSchema._zod.def) {
271
+ const discriminator = ctx.zodSchema._zod.def
272
+ .discriminator;
273
+ ctx.jsonSchema.type = "object";
274
+ ctx.jsonSchema.discriminator = { propertyName: discriminator };
275
+ ctx.jsonSchema.required = [discriminator];
276
+ ctx.jsonSchema.oneOf = ctx.jsonSchema.anyOf.map((objectSchema) => ({
277
+ properties: objectSchema.properties,
278
+ additionalProperties: objectSchema.additionalProperties,
279
+ required: (objectSchema.required ?? []).filter((prop) => prop !== discriminator),
280
+ }));
281
+ delete ctx.jsonSchema.anyOf;
282
+ }
283
+ // Remove fields that have a default from the required list
284
+ if (ctx.jsonSchema.properties && ctx.jsonSchema.required) {
285
+ const properties = ctx.jsonSchema.properties;
286
+ ctx.jsonSchema.required = ctx.jsonSchema.required.filter((field) => !("default" in properties[field]));
287
+ }
288
+ // Make sure property has description if set in zod schema
289
+ if ("description" in ctx.zodSchema) {
290
+ ctx.jsonSchema.description = ctx.zodSchema
291
+ .description;
292
+ }
293
+ },
294
+ })
295
+ : {
296
+ type: "object",
297
+ properties: {},
298
+ };
184
299
  await promises_1.default.writeFile(".kokimoki/schema.json", JSON.stringify(jsonSchema, null, 2));
185
- // // write schema defaults as json
186
- // await fs.writeFile(
187
- // ".kokimoki/schema-defaults.json",
188
- // JSON.stringify(defaultJsonSchemaValue, null, 2)
189
- // );
190
- // // write schema defaults as yaml
191
- // await fs.writeFile(
192
- // ".kokimoki/schema-defaults.yaml",
193
- // yaml.stringify(defaultJsonSchemaValue)
194
- // );
300
+ // write stores config with JSON schemas to build output
301
+ if (config.stores) {
302
+ const storesWithJsonSchema = config.stores.map((store) => ({
303
+ pattern: store.pattern,
304
+ local: store.local,
305
+ schema: v4_1.z.toJSONSchema(store.schema),
306
+ }));
307
+ this.emitFile({
308
+ type: "asset",
309
+ fileName: "km-stores.json",
310
+ source: JSON.stringify(storesWithJsonSchema, null, 2),
311
+ });
312
+ }
313
+ // write i18n files to build output as individual files per lng/ns
314
+ const i18nResources = await getI18nResources();
315
+ if (Object.keys(i18nResources).length > 0) {
316
+ const i18nManifest = {};
317
+ for (const [lng, namespaces] of Object.entries(i18nResources)) {
318
+ i18nManifest[lng] = {};
319
+ for (const [ns, translations] of Object.entries(namespaces)) {
320
+ const fileName = `km-i18n/${lng}/${ns}.json`;
321
+ this.emitFile({
322
+ type: "asset",
323
+ fileName,
324
+ source: JSON.stringify(translations, null, 2),
325
+ });
326
+ // Store relative path - server can prepend assets base or modify URLs
327
+ i18nManifest[lng][ns] = fileName;
328
+ }
329
+ }
330
+ // Emit manifest mapping lng/ns to URLs
331
+ this.emitFile({
332
+ type: "asset",
333
+ fileName: "km-i18n.json",
334
+ source: JSON.stringify(i18nManifest, null, 2),
335
+ });
336
+ }
195
337
  },
196
338
  configureServer(server) {
339
+ // Start initialization as early as possible (don't wait for first request)
340
+ startInitialization();
341
+ // Helper to check stores changed status
342
+ async function checkStoresChanged() {
343
+ const currentStoresHash = (0, dev_app_1.computeStoresHash)(config.stores);
344
+ const previousStoresHash = await (0, dev_app_1.readStoresHash)();
345
+ return (previousStoresHash !== null &&
346
+ previousStoresHash !== currentStoresHash);
347
+ }
348
+ // Serve i18n JSON files in development
349
+ server.middlewares.use("/__kokimoki/i18n", async (req, res, next) => {
350
+ // Parse URL: /__kokimoki/i18n/{lng}/{ns}.json
351
+ const match = req.url?.match(/^\/([^/]+)\/([^/]+)\.json$/);
352
+ if (!match) {
353
+ return next();
354
+ }
355
+ const [, lng, ns] = match;
356
+ try {
357
+ const i18nResources = await getI18nResources();
358
+ const translations = i18nResources[lng]?.[ns];
359
+ if (!translations) {
360
+ res.statusCode = 404;
361
+ res.setHeader("Content-Type", "application/json");
362
+ res.end(JSON.stringify({ error: `Translation not found: ${lng}/${ns}` }));
363
+ return;
364
+ }
365
+ res.statusCode = 200;
366
+ res.setHeader("Content-Type", "application/json");
367
+ res.setHeader("Cache-Control", "no-cache");
368
+ res.end(JSON.stringify(translations));
369
+ }
370
+ catch (e) {
371
+ res.statusCode = 500;
372
+ res.setHeader("Content-Type", "application/json");
373
+ res.end(JSON.stringify({
374
+ error: e instanceof Error ? e.message : "Unknown error",
375
+ }));
376
+ }
377
+ });
378
+ // API endpoint to check if initialization is complete
379
+ server.middlewares.use("/__kokimoki/ready", async (_req, res) => {
380
+ res.statusCode = 200;
381
+ res.setHeader("Content-Type", "application/json");
382
+ res.setHeader("Cache-Control", "no-cache");
383
+ res.end(JSON.stringify({
384
+ ready: initState === "ready" || initState === "error",
385
+ }));
386
+ });
387
+ // API endpoint to acknowledge stores hash change (dismiss the popup)
388
+ server.middlewares.use("/__kokimoki/stores-hash/acknowledge", async (_req, res) => {
389
+ try {
390
+ const currentStoresHash = (0, dev_app_1.computeStoresHash)(config.stores);
391
+ await (0, dev_app_1.writeStoresHash)(currentStoresHash);
392
+ res.statusCode = 200;
393
+ res.setHeader("Content-Type", "application/json");
394
+ res.end(JSON.stringify({ success: true }));
395
+ }
396
+ catch (e) {
397
+ res.statusCode = 500;
398
+ res.setHeader("Content-Type", "application/json");
399
+ res.end(JSON.stringify({
400
+ error: e instanceof Error ? e.message : "Unknown error",
401
+ }));
402
+ }
403
+ });
404
+ // API endpoint to reset dev app state (deletes app-id to create fresh dev app)
405
+ server.middlewares.use("/__kokimoki/dev-app/reset", async (_req, res) => {
406
+ try {
407
+ // Delete the app-id file so a new dev app will be created
408
+ await (0, dev_app_1.deleteAppId)();
409
+ // Update the stores hash
410
+ const currentStoresHash = (0, dev_app_1.computeStoresHash)(config.stores);
411
+ await (0, dev_app_1.writeStoresHash)(currentStoresHash);
412
+ // Reset initialization state so next request triggers re-initialization
413
+ initState = "pending";
414
+ initPromise = null;
415
+ cachedDevAppResult = null;
416
+ // Start re-initialization (polling from loading page will detect when ready)
417
+ startInitialization();
418
+ res.statusCode = 200;
419
+ res.setHeader("Content-Type", "application/json");
420
+ res.end(JSON.stringify({ success: true }));
421
+ }
422
+ catch (e) {
423
+ res.statusCode = 500;
424
+ res.setHeader("Content-Type", "application/json");
425
+ res.end(JSON.stringify({
426
+ error: e instanceof Error ? e.message : "Unknown error",
427
+ }));
428
+ }
429
+ });
197
430
  // reload when defaultProjectConfigPath or defaultProjectStylePath changes
198
431
  if (config.defaultProjectConfigPath) {
199
432
  server.watcher.add(config.defaultProjectConfigPath);
@@ -201,7 +434,25 @@ function kokimokiKitPlugin(config) {
201
434
  if (config.defaultProjectStylePath) {
202
435
  server.watcher.add(config.defaultProjectStylePath);
203
436
  }
204
- server.watcher.on("change", (file) => {
437
+ // Watch i18n folder for changes
438
+ if (config.i18nPath) {
439
+ const i18nAbsolutePath = path_1.default.resolve(process.cwd(), config.i18nPath);
440
+ server.watcher.add(i18nAbsolutePath);
441
+ }
442
+ server.watcher.on("change", async (file) => {
443
+ // Invalidate i18n cache on i18n file changes and sync to dev app
444
+ if (config.i18nPath) {
445
+ const i18nAbsolutePath = path_1.default.resolve(process.cwd(), config.i18nPath);
446
+ if (file.startsWith(i18nAbsolutePath) && file.endsWith(".json")) {
447
+ cachedI18n = null;
448
+ // Sync only the changed file to dev app (only primary language)
449
+ if (devAppInfo) {
450
+ await (0, dev_i18n_1.syncI18nFile)(devAppInfo, config.i18nPath, file, config.i18nPrimaryLng ?? "en");
451
+ }
452
+ server.ws.send({ type: "full-reload" });
453
+ return;
454
+ }
455
+ }
205
456
  if (config.defaultProjectConfigPath?.match(file) ||
206
457
  config.defaultProjectStylePath?.match(file)) {
207
458
  server.ws.send({
@@ -209,7 +460,77 @@ function kokimokiKitPlugin(config) {
209
460
  });
210
461
  }
211
462
  });
463
+ // Intercept all root HTML requests to show loading/error/stores-changed/dev-view at root level
464
+ // This runs BEFORE Vite's built-in middleware so we can intercept index.html requests
465
+ server.middlewares.use(async (req, res, next) => {
466
+ const url = new URL(req.url || "/", "http://localhost");
467
+ // Only intercept root path requests (with or without key param)
468
+ if (url.pathname !== "/") {
469
+ return next();
470
+ }
471
+ const hasKeyParam = url.searchParams.has("key");
472
+ // Show loading page while initializing (at root level only)
473
+ if (initState === "pending" || initState === "initializing") {
474
+ if (!hasKeyParam) {
475
+ res.statusCode = 200;
476
+ res.setHeader("Content-Type", "text/html");
477
+ res.setHeader("Cache-Control", "no-cache");
478
+ res.end((0, dev_overlays_1.renderLoadingPage)());
479
+ return;
480
+ }
481
+ // For iframe requests during init, let them wait via transformIndexHtml
482
+ return next();
483
+ }
484
+ const { organization, error: devAppError } = cachedDevAppResult ?? {
485
+ organization: undefined,
486
+ error: { code: "INIT_ERROR", message: "Initialization failed" },
487
+ };
488
+ // Show error page at root level only
489
+ if (devAppError && !hasKeyParam) {
490
+ res.statusCode = 200;
491
+ res.setHeader("Content-Type", "text/html");
492
+ res.setHeader("Cache-Control", "no-cache");
493
+ res.end((0, dev_overlays_1.renderErrorPage)(devAppError));
494
+ return;
495
+ }
496
+ // Check if stores configuration has changed
497
+ const storesChanged = await checkStoresChanged();
498
+ if (storesChanged) {
499
+ if (!hasKeyParam) {
500
+ // Root page: show stores changed page
501
+ res.statusCode = 200;
502
+ res.setHeader("Content-Type", "text/html");
503
+ res.setHeader("Cache-Control", "no-cache");
504
+ res.end((0, dev_overlays_1.renderStoresChangedPage)(!!organization));
505
+ }
506
+ else {
507
+ // Iframe: reload parent to show stores changed page at root level
508
+ res.statusCode = 200;
509
+ res.setHeader("Content-Type", "text/html");
510
+ res.setHeader("Cache-Control", "no-cache");
511
+ res.end(`<!DOCTYPE html>
512
+ <html><head><script>window.parent.location.reload();</script></head><body></body></html>`);
513
+ }
514
+ return;
515
+ }
516
+ // For requests with key param, continue to normal handling (Vite will serve index.html)
517
+ if (hasKeyParam) {
518
+ return next();
519
+ }
520
+ // Dev view disabled or not configured, continue to normal handling
521
+ if (config.devView === false || config.devView === undefined) {
522
+ return next();
523
+ }
524
+ // Render dev view HTML at root
525
+ const devViewHtml = (0, dev_frame_1.renderDevFrame)({
526
+ title: "Dev View",
527
+ rows: config.devView,
528
+ });
529
+ res.statusCode = 200;
530
+ res.setHeader("Content-Type", "text/html");
531
+ res.setHeader("Cache-Control", "no-cache");
532
+ res.end(devViewHtml);
533
+ });
212
534
  },
213
535
  };
214
536
  }
215
- exports.kokimokiKitPlugin = kokimokiKitPlugin;