@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.
- package/README.md +76 -0
- package/dist/api.d.ts +63 -0
- package/dist/api.js +95 -0
- package/dist/credentials.d.ts +25 -0
- package/dist/credentials.js +44 -0
- package/dist/dev-app.d.ts +57 -0
- package/dist/dev-app.js +271 -0
- package/dist/dev-frame/index.d.ts +5 -0
- package/dist/dev-frame/index.js +9 -0
- package/dist/dev-frame/render-dev-frame.d.ts +30 -0
- package/dist/dev-frame/render-dev-frame.js +160 -0
- package/dist/dev-frame/styles.d.ts +4 -0
- package/dist/dev-frame/styles.js +191 -0
- package/dist/dev-i18n.d.ts +56 -0
- package/dist/dev-i18n.js +164 -0
- package/dist/dev-overlays.d.ts +19 -0
- package/dist/dev-overlays.js +300 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +7 -0
- package/dist/kokimoki-kit-plugin.d.ts +47 -4
- package/dist/kokimoki-kit-plugin.js +403 -82
- package/dist/preprocess-style.js +13 -14
- package/dist/preprocess-style.spec.js +1 -1
- package/dist/production-loading-screen.d.ts +20 -0
- package/dist/production-loading-screen.js +123 -0
- package/dist/schema-builder.d.ts +89 -74
- package/dist/schema-builder.js +149 -132
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/zod.d.ts +1 -0
- package/dist/zod.js +5 -0
- package/docs/kokimoki-kit.instructions.md +341 -0
- package/llms.txt +46 -0
- package/package.json +10 -3
|
@@ -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 (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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.
|
|
30
|
-
|
|
39
|
+
exports.getI18nMeta = void 0;
|
|
40
|
+
exports.kokimokiKitPlugin = kokimokiKitPlugin;
|
|
31
41
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
32
|
-
const
|
|
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
|
-
|
|
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
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
//
|
|
104
|
-
if (
|
|
105
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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 =
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
.
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
//
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
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;
|