@holo-js/cli 0.1.8 → 0.2.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/dist/bin/holo.mjs +167 -81
- package/dist/broadcast-WI6PJS5P.mjs +203 -0
- package/dist/broadcast-YWS4N5QU.mjs +203 -0
- package/dist/{cache-JGGCYQQG.mjs → cache-KWNQECAA.mjs} +6 -6
- package/dist/cache-QARFSW4F.mjs +66 -0
- package/dist/{cache-migrations-3V7LI4CC.mjs → cache-migrations-3OXR4FN5.mjs} +50 -30
- package/dist/cache-migrations-MDFMDVTK.mjs +173 -0
- package/dist/{chunk-O6AXHL7Z.mjs → chunk-2DKQKZML.mjs} +230 -126
- package/dist/{chunk-D4NXGVV4.mjs → chunk-2RGJTPYF.mjs} +36 -25
- package/dist/{chunk-2NUEWM2P.mjs → chunk-EWYXSN2C.mjs} +82 -119
- package/dist/{chunk-F4MT6GBK.mjs → chunk-FGQ2I2YH.mjs} +1 -1
- package/dist/chunk-I7QBCEV7.mjs +33 -0
- package/dist/{chunk-R6BWRY3E.mjs → chunk-ILU426CF.mjs} +3 -1
- package/dist/{chunk-QIOHKKXP.mjs → chunk-IUDD5FYL.mjs} +28 -273
- package/dist/{chunk-SABHUOON.mjs → chunk-KWRIBHC3.mjs} +229 -142
- package/dist/{chunk-57SJ566R.mjs → chunk-LBJAJLKU.mjs} +1 -1
- package/dist/{chunk-BAFQ2GOA.mjs → chunk-LXGQCG56.mjs} +1 -1
- package/dist/{chunk-DFKX4YT4.mjs → chunk-ONKESAQA.mjs} +2 -2
- package/dist/chunk-QA7TP5EO.mjs +448 -0
- package/dist/chunk-UPZH6KCF.mjs +3306 -0
- package/dist/{chunk-5EU32E7X.mjs → chunk-VRGB6DIS.mjs} +116 -12
- package/dist/{config-K7SBKT2C.mjs → config-TWEO2R4N.mjs} +3 -3
- package/dist/{dev-RZLZX75U.mjs → dev-2OULECTU.mjs} +7 -7
- package/dist/dev-PJMEGTAC.mjs +42 -0
- package/dist/{discovery-SFRDA4VX.mjs → discovery-7FXND7Y6.mjs} +3 -3
- package/dist/{generators-UJA6WP7J.mjs → generators-4BP7B47W.mjs} +11 -34
- package/dist/generators-Z4XLSMC7.mjs +520 -0
- package/dist/index.mjs +169 -83
- package/dist/{media-migrations-76KFHA2U.mjs → media-migrations-BFEL7NFG.mjs} +9 -20
- package/dist/media-migrations-VR7DLLR6.mjs +106 -0
- package/dist/{queue-JGVKSPUM.mjs → queue-SVOJPTRO.mjs} +10 -10
- package/dist/queue-YCBQTCYI.mjs +625 -0
- package/dist/{queue-migrations-3TYOTL45.mjs → queue-migrations-HPXOO3NA.mjs} +13 -12
- package/dist/queue-migrations-X4P7FZKJ.mjs +167 -0
- package/dist/{runtime-4AAMJI34.mjs → runtime-CPKR663Y.mjs} +9 -9
- package/dist/runtime-GIE56H47.mjs +57 -0
- package/dist/{runtime-ZKD6URAV.mjs → runtime-GSXF4NB3.mjs} +1 -1
- package/dist/runtime-worker.d.ts +2 -0
- package/dist/runtime-worker.mjs +242 -0
- package/dist/{scaffold-TMP7PWOA.mjs → scaffold-3QPGYQEQ.mjs} +9 -5
- package/dist/scaffold-RGAAHC6I.mjs +139 -0
- package/dist/{security-ILU74RIZ.mjs → security-7H5TNHZY.mjs} +6 -6
- package/dist/security-BZGD6ONY.mjs +71 -0
- package/package.json +9 -7
- package/dist/broadcast-3VPGBNCR.mjs +0 -84
- package/dist/chunk-ZXDU7RHU.mjs +0 -9
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import {
|
|
2
|
+
writeLine
|
|
3
|
+
} from "./chunk-I7QBCEV7.mjs";
|
|
4
|
+
import {
|
|
5
|
+
initializeProjectRuntime
|
|
6
|
+
} from "./chunk-IUDD5FYL.mjs";
|
|
7
|
+
import "./chunk-LBJAJLKU.mjs";
|
|
8
|
+
import "./chunk-D7O4SU6N.mjs";
|
|
9
|
+
import {
|
|
10
|
+
prepareProjectDiscovery
|
|
11
|
+
} from "./chunk-2RGJTPYF.mjs";
|
|
12
|
+
import "./chunk-UPZH6KCF.mjs";
|
|
13
|
+
import {
|
|
14
|
+
loadProjectConfig
|
|
15
|
+
} from "./chunk-ONKESAQA.mjs";
|
|
16
|
+
import {
|
|
17
|
+
loadGeneratedProjectRegistry
|
|
18
|
+
} from "./chunk-2DKQKZML.mjs";
|
|
19
|
+
import {
|
|
20
|
+
importProjectModule,
|
|
21
|
+
resolveProjectPackageImportSpecifier
|
|
22
|
+
} from "./chunk-ILU426CF.mjs";
|
|
23
|
+
|
|
24
|
+
// src/broadcast.ts
|
|
25
|
+
import { basename, extname, resolve } from "path";
|
|
26
|
+
import { readFile } from "fs/promises";
|
|
27
|
+
import { loadConfigDirectory } from "@holo-js/config";
|
|
28
|
+
function hasLoadedRedisConfigSection(loadedFiles) {
|
|
29
|
+
return Array.isArray(loadedFiles) && loadedFiles.some((filePath) => {
|
|
30
|
+
return basename(filePath, extname(filePath)) === "redis";
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
async function hasProjectPackageDependency(projectRoot, packageName) {
|
|
34
|
+
try {
|
|
35
|
+
const manifest = JSON.parse(await readFile(resolve(projectRoot, "package.json"), "utf8"));
|
|
36
|
+
return Boolean(
|
|
37
|
+
manifest.dependencies?.[packageName] || manifest.devDependencies?.[packageName] || manifest.optionalDependencies?.[packageName] || manifest.peerDependencies?.[packageName]
|
|
38
|
+
);
|
|
39
|
+
} catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async function loadBroadcastCliModule(projectRoot) {
|
|
44
|
+
try {
|
|
45
|
+
return await import(resolveProjectPackageImportSpecifier(projectRoot, "@holo-js/broadcast"));
|
|
46
|
+
} catch (error) {
|
|
47
|
+
const details = error instanceof Error ? error.message : String(error);
|
|
48
|
+
throw new Error(
|
|
49
|
+
`Unable to load @holo-js/broadcast from ${projectRoot}. Install it with "holo install broadcast". ${details}`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async function loadRealtimeServerModule(projectRoot) {
|
|
54
|
+
const specifier = resolveProjectPackageImportSpecifier(projectRoot, "@holo-js/realtime/server");
|
|
55
|
+
try {
|
|
56
|
+
return await import(specifier);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
const details = error instanceof Error ? error.message : String(error);
|
|
59
|
+
if (/Cannot find module|ERR_MODULE_NOT_FOUND|MODULE_NOT_FOUND/i.test(details) && details.includes(specifier)) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function safeDecodeCookieSegment(value) {
|
|
66
|
+
try {
|
|
67
|
+
return decodeURIComponent(value);
|
|
68
|
+
} catch {
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function readCookieFromHeader(header, name) {
|
|
73
|
+
for (const segment of header?.split(";") ?? []) {
|
|
74
|
+
const separator = segment.indexOf("=");
|
|
75
|
+
if (separator <= 0) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const cookieName = safeDecodeCookieSegment(segment.slice(0, separator).trim());
|
|
79
|
+
if (cookieName === name) {
|
|
80
|
+
return safeDecodeCookieSegment(segment.slice(separator + 1).trim());
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return void 0;
|
|
84
|
+
}
|
|
85
|
+
function createRealtimeAuthRequestAccessors(headers) {
|
|
86
|
+
return {
|
|
87
|
+
async getCookie(name) {
|
|
88
|
+
return readCookieFromHeader(headers.get("cookie"), name);
|
|
89
|
+
},
|
|
90
|
+
async getHeader(name) {
|
|
91
|
+
return headers.get(name) ?? void 0;
|
|
92
|
+
},
|
|
93
|
+
async appendResponseCookie(_cookie) {
|
|
94
|
+
},
|
|
95
|
+
async redirectResponse(url) {
|
|
96
|
+
throw new Error(`Realtime auth attempted to redirect to "${url}". Realtime requests cannot redirect.`);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
async function createRealtimeWorkerBindings(projectRoot) {
|
|
101
|
+
if (!await hasProjectPackageDependency(projectRoot, "@holo-js/realtime")) {
|
|
102
|
+
return void 0;
|
|
103
|
+
}
|
|
104
|
+
const realtime = await loadRealtimeServerModule(projectRoot);
|
|
105
|
+
if (!realtime) {
|
|
106
|
+
return void 0;
|
|
107
|
+
}
|
|
108
|
+
const runtime = await initializeProjectRuntime(projectRoot);
|
|
109
|
+
realtime.configureRealtimeRuntime?.({
|
|
110
|
+
async runWithAuthRequestAccessors(accessors, callback) {
|
|
111
|
+
const runner = runtime.runWithAuthRequestAccessors;
|
|
112
|
+
return runner ? await runner(accessors, callback) : await callback();
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
const definitions = /* @__PURE__ */ new Map();
|
|
116
|
+
const resolveDefinition = async (name) => {
|
|
117
|
+
const cached = definitions.get(name);
|
|
118
|
+
if (cached) {
|
|
119
|
+
return await cached;
|
|
120
|
+
}
|
|
121
|
+
const resolved = realtime.resolveRealtimeDefinition(name, {
|
|
122
|
+
projectRoot,
|
|
123
|
+
importModule: async (absolutePath) => await importProjectModule(projectRoot, absolutePath)
|
|
124
|
+
}).catch((error) => {
|
|
125
|
+
definitions.delete(name);
|
|
126
|
+
throw error;
|
|
127
|
+
});
|
|
128
|
+
definitions.set(name, resolved);
|
|
129
|
+
return await resolved;
|
|
130
|
+
};
|
|
131
|
+
const withRealtimeRequest = async (context, callback) => {
|
|
132
|
+
return await callback(createRealtimeAuthRequestAccessors(context.headers));
|
|
133
|
+
};
|
|
134
|
+
return {
|
|
135
|
+
async query(name, args, context) {
|
|
136
|
+
return await withRealtimeRequest(context, async (authRequest) => {
|
|
137
|
+
return await realtime.executeRealtimeQuery(await resolveDefinition(name), args, { authRequest });
|
|
138
|
+
});
|
|
139
|
+
},
|
|
140
|
+
async mutate(name, args, context) {
|
|
141
|
+
return await withRealtimeRequest(context, async (authRequest) => {
|
|
142
|
+
return await realtime.executeRealtimeMutation(await resolveDefinition(name), args, { authRequest });
|
|
143
|
+
});
|
|
144
|
+
},
|
|
145
|
+
async subscribe(name, args, options) {
|
|
146
|
+
return await withRealtimeRequest(options.context, async (authRequest) => {
|
|
147
|
+
return await realtime.subscribeRealtimeQuery(await resolveDefinition(name), args, {
|
|
148
|
+
onData: options.onData,
|
|
149
|
+
onError: options.onError
|
|
150
|
+
}, { authRequest });
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
async function runBroadcastWorkCommand(io, projectRoot, dependencies = {}) {
|
|
156
|
+
const loadConfig = dependencies.loadConfig ?? loadConfigDirectory;
|
|
157
|
+
const loadModule = dependencies.loadModule ?? loadBroadcastCliModule;
|
|
158
|
+
const config = await loadConfig(projectRoot);
|
|
159
|
+
const project = await loadProjectConfig(projectRoot);
|
|
160
|
+
const loadRegistry = dependencies.loadRegistry ?? loadGeneratedProjectRegistry;
|
|
161
|
+
await loadRegistry(projectRoot).catch(() => void 0);
|
|
162
|
+
const registry = await prepareProjectDiscovery(projectRoot, project.config);
|
|
163
|
+
const broadcastModule = await loadModule(projectRoot);
|
|
164
|
+
const realtime = await createRealtimeWorkerBindings(projectRoot);
|
|
165
|
+
const worker = await broadcastModule.startBroadcastWorker({
|
|
166
|
+
config: config.broadcast,
|
|
167
|
+
queue: config.queue,
|
|
168
|
+
...hasLoadedRedisConfigSection(config.loadedFiles) ? { redis: config.redis } : {},
|
|
169
|
+
...registry ? {
|
|
170
|
+
channelAuth: {
|
|
171
|
+
registry: {
|
|
172
|
+
projectRoot,
|
|
173
|
+
channels: registry.channels
|
|
174
|
+
},
|
|
175
|
+
importModule: async (absolutePath) => await importProjectModule(projectRoot, absolutePath)
|
|
176
|
+
}
|
|
177
|
+
} : {},
|
|
178
|
+
...realtime ? { realtime } : {}
|
|
179
|
+
});
|
|
180
|
+
writeLine(io.stdout, `[broadcast] Worker listening on ${worker.host}:${worker.port}`);
|
|
181
|
+
await new Promise((resolvePromise) => {
|
|
182
|
+
let stopped = false;
|
|
183
|
+
const stop = async () => {
|
|
184
|
+
if (stopped) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
stopped = true;
|
|
188
|
+
process.off("SIGINT", onSignal);
|
|
189
|
+
process.off("SIGTERM", onSignal);
|
|
190
|
+
await worker.stop();
|
|
191
|
+
resolvePromise();
|
|
192
|
+
};
|
|
193
|
+
const onSignal = () => {
|
|
194
|
+
void stop();
|
|
195
|
+
};
|
|
196
|
+
process.on("SIGINT", onSignal);
|
|
197
|
+
process.on("SIGTERM", onSignal);
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
export {
|
|
201
|
+
loadBroadcastCliModule,
|
|
202
|
+
runBroadcastWorkCommand
|
|
203
|
+
};
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
writeLine
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-I7QBCEV7.mjs";
|
|
4
4
|
import "./chunk-D7O4SU6N.mjs";
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
5
|
+
import "./chunk-2RGJTPYF.mjs";
|
|
6
|
+
import "./chunk-KWRIBHC3.mjs";
|
|
7
|
+
import "./chunk-ONKESAQA.mjs";
|
|
8
|
+
import "./chunk-2DKQKZML.mjs";
|
|
9
9
|
import {
|
|
10
10
|
resolveProjectPackageImportSpecifier
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-ILU426CF.mjs";
|
|
12
12
|
|
|
13
13
|
// src/cache.ts
|
|
14
14
|
import { loadConfigDirectory } from "@holo-js/config";
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import {
|
|
2
|
+
writeLine
|
|
3
|
+
} from "./chunk-I7QBCEV7.mjs";
|
|
4
|
+
import "./chunk-D7O4SU6N.mjs";
|
|
5
|
+
import "./chunk-2RGJTPYF.mjs";
|
|
6
|
+
import "./chunk-UPZH6KCF.mjs";
|
|
7
|
+
import "./chunk-ONKESAQA.mjs";
|
|
8
|
+
import "./chunk-2DKQKZML.mjs";
|
|
9
|
+
import {
|
|
10
|
+
resolveProjectPackageImportSpecifier
|
|
11
|
+
} from "./chunk-ILU426CF.mjs";
|
|
12
|
+
|
|
13
|
+
// src/cache.ts
|
|
14
|
+
import { loadConfigDirectory } from "@holo-js/config";
|
|
15
|
+
function resolveCacheFacade(cacheModule) {
|
|
16
|
+
const candidate = cacheModule.default;
|
|
17
|
+
if (candidate) {
|
|
18
|
+
return candidate;
|
|
19
|
+
}
|
|
20
|
+
return cacheModule;
|
|
21
|
+
}
|
|
22
|
+
async function loadCacheCliModule(projectRoot) {
|
|
23
|
+
return await import(resolveProjectPackageImportSpecifier(projectRoot, "@holo-js/cache"));
|
|
24
|
+
}
|
|
25
|
+
async function initializeCacheMaintenanceEnvironment(projectRoot) {
|
|
26
|
+
const loadedConfig = await loadConfigDirectory(projectRoot);
|
|
27
|
+
const cacheModule = await loadCacheCliModule(projectRoot);
|
|
28
|
+
const cache = resolveCacheFacade(cacheModule);
|
|
29
|
+
cache.configureCacheRuntime({
|
|
30
|
+
config: loadedConfig.cache,
|
|
31
|
+
databaseConfig: loadedConfig.database,
|
|
32
|
+
redisConfig: loadedConfig.redis
|
|
33
|
+
});
|
|
34
|
+
return {
|
|
35
|
+
cache,
|
|
36
|
+
async cleanup() {
|
|
37
|
+
cache.resetCacheRuntime();
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
async function runCacheClearCommand(io, projectRoot, driverName, dependencies = {}) {
|
|
42
|
+
const environment = await (dependencies.initializeCache ?? initializeCacheMaintenanceEnvironment)(projectRoot);
|
|
43
|
+
try {
|
|
44
|
+
const repository = driverName?.trim() ? environment.cache.driver(driverName) : environment.cache;
|
|
45
|
+
await (dependencies.flush ?? (async (target) => await target.flush()))(repository);
|
|
46
|
+
writeLine(io.stdout, driverName?.trim() ? `[cache] Cleared cache store "${driverName}".` : "[cache] Cleared the default cache store.");
|
|
47
|
+
} finally {
|
|
48
|
+
await environment.cleanup();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async function runCacheForgetCommand(io, projectRoot, key, driverName, dependencies = {}) {
|
|
52
|
+
const environment = await (dependencies.initializeCache ?? initializeCacheMaintenanceEnvironment)(projectRoot);
|
|
53
|
+
try {
|
|
54
|
+
const repository = driverName?.trim() ? environment.cache.driver(driverName) : environment.cache;
|
|
55
|
+
const forgotten = await (dependencies.forget ?? (async (target, targetKey) => await target.forget(targetKey)))(repository, key);
|
|
56
|
+
writeLine(io.stdout, forgotten ? `[cache] Forgot key "${key}".` : `[cache] Key "${key}" was not present.`);
|
|
57
|
+
} finally {
|
|
58
|
+
await environment.cleanup();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export {
|
|
62
|
+
initializeCacheMaintenanceEnvironment,
|
|
63
|
+
loadCacheCliModule,
|
|
64
|
+
runCacheClearCommand,
|
|
65
|
+
runCacheForgetCommand
|
|
66
|
+
};
|
|
@@ -1,40 +1,45 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runProjectPrepare
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-EWYXSN2C.mjs";
|
|
4
|
+
import "./chunk-FGQ2I2YH.mjs";
|
|
5
5
|
import {
|
|
6
6
|
getRegistryMigrationSlug,
|
|
7
7
|
hasRegisteredCreateTableMigration,
|
|
8
8
|
hasRegisteredMigrationSlug,
|
|
9
9
|
nextMigrationTemplate
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-LXGQCG56.mjs";
|
|
11
11
|
import {
|
|
12
12
|
writeLine
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import "./chunk-
|
|
13
|
+
} from "./chunk-I7QBCEV7.mjs";
|
|
14
|
+
import "./chunk-LBJAJLKU.mjs";
|
|
15
15
|
import "./chunk-D7O4SU6N.mjs";
|
|
16
16
|
import {
|
|
17
17
|
prepareProjectDiscovery
|
|
18
|
-
} from "./chunk-
|
|
19
|
-
import "./chunk-
|
|
18
|
+
} from "./chunk-2RGJTPYF.mjs";
|
|
19
|
+
import "./chunk-UPZH6KCF.mjs";
|
|
20
20
|
import {
|
|
21
21
|
ensureProjectConfig
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-ONKESAQA.mjs";
|
|
23
23
|
import {
|
|
24
24
|
loadGeneratedProjectRegistry
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-2DKQKZML.mjs";
|
|
26
26
|
import {
|
|
27
27
|
makeProjectRelativePath,
|
|
28
28
|
resolveDefaultArtifactPath,
|
|
29
29
|
writeTextFile
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-ILU426CF.mjs";
|
|
31
31
|
|
|
32
32
|
// src/cache-migrations.ts
|
|
33
33
|
import { resolve } from "path";
|
|
34
|
+
import {
|
|
35
|
+
CACHE_DATABASE_TABLE_DEFINITIONS,
|
|
36
|
+
DEFAULT_CACHE_DATABASE_LOCK_TABLE as CACHE_DB_DEFAULT_LOCK_TABLE,
|
|
37
|
+
DEFAULT_CACHE_DATABASE_TABLE as CACHE_DB_DEFAULT_TABLE
|
|
38
|
+
} from "@holo-js/cache-db";
|
|
34
39
|
import { loadConfigDirectory } from "@holo-js/config";
|
|
35
40
|
import { normalizeMigrationSlug } from "@holo-js/db";
|
|
36
|
-
var DEFAULT_CACHE_DATABASE_TABLE =
|
|
37
|
-
var DEFAULT_CACHE_DATABASE_LOCK_TABLE =
|
|
41
|
+
var DEFAULT_CACHE_DATABASE_TABLE = CACHE_DB_DEFAULT_TABLE;
|
|
42
|
+
var DEFAULT_CACHE_DATABASE_LOCK_TABLE = CACHE_DB_DEFAULT_LOCK_TABLE;
|
|
38
43
|
async function loadCacheConfig(projectRoot) {
|
|
39
44
|
const loadedConfig = await loadConfigDirectory(projectRoot);
|
|
40
45
|
if (!loadedConfig || typeof loadedConfig !== "object" || !("cache" in loadedConfig) || !loadedConfig.cache || typeof loadedConfig.cache !== "object" || !("drivers" in loadedConfig.cache) || typeof loadedConfig.cache.drivers !== "object" || loadedConfig.cache.drivers === null || Array.isArray(loadedConfig.cache.drivers)) {
|
|
@@ -58,32 +63,47 @@ function normalizeCacheMigrationName(tableName) {
|
|
|
58
63
|
function escapeSingleQuotedString(value) {
|
|
59
64
|
return value.replaceAll("\\", "\\\\").replaceAll("'", "\\'");
|
|
60
65
|
}
|
|
66
|
+
function renderCacheTableColumn(columnDefinition) {
|
|
67
|
+
const calls = [
|
|
68
|
+
`table.${columnDefinition.kind}('${escapeSingleQuotedString(columnDefinition.name)}')`
|
|
69
|
+
];
|
|
70
|
+
if (columnDefinition.primaryKey) {
|
|
71
|
+
calls.push("primaryKey()");
|
|
72
|
+
}
|
|
73
|
+
if (columnDefinition.nullable) {
|
|
74
|
+
calls.push("nullable()");
|
|
75
|
+
}
|
|
76
|
+
return ` ${calls.join(".")}`;
|
|
77
|
+
}
|
|
78
|
+
function renderCacheTableCreateStatement(tableName, tableDefinition) {
|
|
79
|
+
return [
|
|
80
|
+
` await schema.createTable('${escapeSingleQuotedString(tableName)}', (table) => {`,
|
|
81
|
+
...tableDefinition.columns.map(renderCacheTableColumn),
|
|
82
|
+
` table.index(['${escapeSingleQuotedString(tableDefinition.indexColumn)}'], '${escapeSingleQuotedString(tableDefinition.indexName(tableName))}')`,
|
|
83
|
+
" })"
|
|
84
|
+
];
|
|
85
|
+
}
|
|
86
|
+
function resolveCacheDatabaseTableDefinition(role) {
|
|
87
|
+
const tableDefinition = CACHE_DATABASE_TABLE_DEFINITIONS.find((definition) => definition.role === role);
|
|
88
|
+
if (!tableDefinition) {
|
|
89
|
+
throw new Error(`Missing cache database table definition for "${role}".`);
|
|
90
|
+
}
|
|
91
|
+
return tableDefinition;
|
|
92
|
+
}
|
|
61
93
|
function renderCacheTableMigration(tableName = DEFAULT_CACHE_DATABASE_TABLE, lockTableName = DEFAULT_CACHE_DATABASE_LOCK_TABLE) {
|
|
62
|
-
const
|
|
63
|
-
const
|
|
64
|
-
const escapedTableIndexName = escapeSingleQuotedString(`${tableName.replaceAll(".", "_")}_expires_at_index`);
|
|
65
|
-
const escapedLockTableIndexName = escapeSingleQuotedString(`${lockTableName.replaceAll(".", "_")}_expires_at_index`);
|
|
94
|
+
const entryTableDefinition = resolveCacheDatabaseTableDefinition("entries");
|
|
95
|
+
const lockTableDefinition = resolveCacheDatabaseTableDefinition("locks");
|
|
66
96
|
return [
|
|
67
97
|
"import { defineMigration, type MigrationContext } from '@holo-js/db'",
|
|
68
98
|
"",
|
|
69
99
|
"export default defineMigration({",
|
|
70
100
|
" async up({ schema }: MigrationContext) {",
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
" table.text('payload')",
|
|
74
|
-
" table.bigInteger('expires_at').nullable()",
|
|
75
|
-
` table.index(['expires_at'], '${escapedTableIndexName}')`,
|
|
76
|
-
" })",
|
|
77
|
-
` await schema.createTable('${escapedLockTableName}', (table) => {`,
|
|
78
|
-
" table.string('name').primaryKey()",
|
|
79
|
-
" table.string('owner')",
|
|
80
|
-
" table.bigInteger('expires_at')",
|
|
81
|
-
` table.index(['expires_at'], '${escapedLockTableIndexName}')`,
|
|
82
|
-
" })",
|
|
101
|
+
...renderCacheTableCreateStatement(tableName, entryTableDefinition),
|
|
102
|
+
...renderCacheTableCreateStatement(lockTableName, lockTableDefinition),
|
|
83
103
|
" },",
|
|
84
104
|
" async down({ schema }: MigrationContext) {",
|
|
85
|
-
` await schema.dropTable('${
|
|
86
|
-
` await schema.dropTable('${
|
|
105
|
+
` await schema.dropTable('${escapeSingleQuotedString(lockTableName)}')`,
|
|
106
|
+
` await schema.dropTable('${escapeSingleQuotedString(tableName)}')`,
|
|
87
107
|
" },",
|
|
88
108
|
"})",
|
|
89
109
|
""
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import {
|
|
2
|
+
runProjectPrepare
|
|
3
|
+
} from "./chunk-QA7TP5EO.mjs";
|
|
4
|
+
import "./chunk-FGQ2I2YH.mjs";
|
|
5
|
+
import {
|
|
6
|
+
getRegistryMigrationSlug,
|
|
7
|
+
hasRegisteredCreateTableMigration,
|
|
8
|
+
hasRegisteredMigrationSlug,
|
|
9
|
+
nextMigrationTemplate
|
|
10
|
+
} from "./chunk-LXGQCG56.mjs";
|
|
11
|
+
import "./chunk-LBJAJLKU.mjs";
|
|
12
|
+
import {
|
|
13
|
+
writeLine
|
|
14
|
+
} from "./chunk-I7QBCEV7.mjs";
|
|
15
|
+
import "./chunk-D7O4SU6N.mjs";
|
|
16
|
+
import {
|
|
17
|
+
prepareProjectDiscovery
|
|
18
|
+
} from "./chunk-2RGJTPYF.mjs";
|
|
19
|
+
import "./chunk-KWRIBHC3.mjs";
|
|
20
|
+
import {
|
|
21
|
+
ensureProjectConfig
|
|
22
|
+
} from "./chunk-ONKESAQA.mjs";
|
|
23
|
+
import {
|
|
24
|
+
loadGeneratedProjectRegistry
|
|
25
|
+
} from "./chunk-2DKQKZML.mjs";
|
|
26
|
+
import {
|
|
27
|
+
makeProjectRelativePath,
|
|
28
|
+
resolveDefaultArtifactPath,
|
|
29
|
+
writeTextFile
|
|
30
|
+
} from "./chunk-ILU426CF.mjs";
|
|
31
|
+
|
|
32
|
+
// src/cache-migrations.ts
|
|
33
|
+
import { resolve } from "path";
|
|
34
|
+
import {
|
|
35
|
+
CACHE_DATABASE_TABLE_DEFINITIONS,
|
|
36
|
+
DEFAULT_CACHE_DATABASE_LOCK_TABLE as CACHE_DB_DEFAULT_LOCK_TABLE,
|
|
37
|
+
DEFAULT_CACHE_DATABASE_TABLE as CACHE_DB_DEFAULT_TABLE
|
|
38
|
+
} from "@holo-js/cache-db";
|
|
39
|
+
import { loadConfigDirectory } from "@holo-js/config";
|
|
40
|
+
import { normalizeMigrationSlug } from "@holo-js/db";
|
|
41
|
+
var DEFAULT_CACHE_DATABASE_TABLE = CACHE_DB_DEFAULT_TABLE;
|
|
42
|
+
var DEFAULT_CACHE_DATABASE_LOCK_TABLE = CACHE_DB_DEFAULT_LOCK_TABLE;
|
|
43
|
+
async function loadCacheConfig(projectRoot) {
|
|
44
|
+
const loadedConfig = await loadConfigDirectory(projectRoot);
|
|
45
|
+
if (!loadedConfig || typeof loadedConfig !== "object" || !("cache" in loadedConfig) || !loadedConfig.cache || typeof loadedConfig.cache !== "object" || !("drivers" in loadedConfig.cache) || typeof loadedConfig.cache.drivers !== "object" || loadedConfig.cache.drivers === null || Array.isArray(loadedConfig.cache.drivers)) {
|
|
46
|
+
throw new Error("Cache config is missing or malformed. Expected a cache config object with a drivers property.");
|
|
47
|
+
}
|
|
48
|
+
const cacheConfig = loadedConfig.cache;
|
|
49
|
+
for (const [driverName, driverConfig] of Object.entries(cacheConfig.drivers)) {
|
|
50
|
+
if (driverConfig.driver !== "database") {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const databaseDriver = driverConfig;
|
|
54
|
+
if (typeof databaseDriver.table !== "string" || !databaseDriver.table.trim() || typeof databaseDriver.lockTable !== "string" || !databaseDriver.lockTable.trim()) {
|
|
55
|
+
throw new Error(`Database cache driver "${driverName}" must define non-empty "table" and "lockTable" strings.`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return cacheConfig;
|
|
59
|
+
}
|
|
60
|
+
function normalizeCacheMigrationName(tableName) {
|
|
61
|
+
return normalizeMigrationSlug(`create_${tableName.replaceAll(".", "_")}_cache_table`);
|
|
62
|
+
}
|
|
63
|
+
function escapeSingleQuotedString(value) {
|
|
64
|
+
return value.replaceAll("\\", "\\\\").replaceAll("'", "\\'");
|
|
65
|
+
}
|
|
66
|
+
function renderCacheTableColumn(columnDefinition) {
|
|
67
|
+
const calls = [
|
|
68
|
+
`table.${columnDefinition.kind}('${escapeSingleQuotedString(columnDefinition.name)}')`
|
|
69
|
+
];
|
|
70
|
+
if (columnDefinition.primaryKey) {
|
|
71
|
+
calls.push("primaryKey()");
|
|
72
|
+
}
|
|
73
|
+
if (columnDefinition.nullable) {
|
|
74
|
+
calls.push("nullable()");
|
|
75
|
+
}
|
|
76
|
+
return ` ${calls.join(".")}`;
|
|
77
|
+
}
|
|
78
|
+
function renderCacheTableCreateStatement(tableName, tableDefinition) {
|
|
79
|
+
return [
|
|
80
|
+
` await schema.createTable('${escapeSingleQuotedString(tableName)}', (table) => {`,
|
|
81
|
+
...tableDefinition.columns.map(renderCacheTableColumn),
|
|
82
|
+
` table.index(['${escapeSingleQuotedString(tableDefinition.indexColumn)}'], '${escapeSingleQuotedString(tableDefinition.indexName(tableName))}')`,
|
|
83
|
+
" })"
|
|
84
|
+
];
|
|
85
|
+
}
|
|
86
|
+
function resolveCacheDatabaseTableDefinition(role) {
|
|
87
|
+
const tableDefinition = CACHE_DATABASE_TABLE_DEFINITIONS.find((definition) => definition.role === role);
|
|
88
|
+
if (!tableDefinition) {
|
|
89
|
+
throw new Error(`Missing cache database table definition for "${role}".`);
|
|
90
|
+
}
|
|
91
|
+
return tableDefinition;
|
|
92
|
+
}
|
|
93
|
+
function renderCacheTableMigration(tableName = DEFAULT_CACHE_DATABASE_TABLE, lockTableName = DEFAULT_CACHE_DATABASE_LOCK_TABLE) {
|
|
94
|
+
const entryTableDefinition = resolveCacheDatabaseTableDefinition("entries");
|
|
95
|
+
const lockTableDefinition = resolveCacheDatabaseTableDefinition("locks");
|
|
96
|
+
return [
|
|
97
|
+
"import { defineMigration, type MigrationContext } from '@holo-js/db'",
|
|
98
|
+
"",
|
|
99
|
+
"export default defineMigration({",
|
|
100
|
+
" async up({ schema }: MigrationContext) {",
|
|
101
|
+
...renderCacheTableCreateStatement(tableName, entryTableDefinition),
|
|
102
|
+
...renderCacheTableCreateStatement(lockTableName, lockTableDefinition),
|
|
103
|
+
" },",
|
|
104
|
+
" async down({ schema }: MigrationContext) {",
|
|
105
|
+
` await schema.dropTable('${escapeSingleQuotedString(lockTableName)}')`,
|
|
106
|
+
` await schema.dropTable('${escapeSingleQuotedString(tableName)}')`,
|
|
107
|
+
" },",
|
|
108
|
+
"})",
|
|
109
|
+
""
|
|
110
|
+
].join("\n");
|
|
111
|
+
}
|
|
112
|
+
function resolveDatabaseCacheTables(cacheConfig) {
|
|
113
|
+
const configured = Object.values(cacheConfig.drivers).filter((driver) => driver.driver === "database").map((driver) => ({
|
|
114
|
+
table: driver.table,
|
|
115
|
+
lockTable: driver.lockTable
|
|
116
|
+
}));
|
|
117
|
+
if (configured.length === 0) {
|
|
118
|
+
throw new Error("The configured cache drivers do not use the database driver.");
|
|
119
|
+
}
|
|
120
|
+
return Object.freeze(configured);
|
|
121
|
+
}
|
|
122
|
+
async function runCacheTableCommand(io, projectRoot) {
|
|
123
|
+
const project = await ensureProjectConfig(projectRoot);
|
|
124
|
+
const registry = await loadGeneratedProjectRegistry(projectRoot) ?? await prepareProjectDiscovery(projectRoot, project.config);
|
|
125
|
+
const cacheConfig = await loadCacheConfig(projectRoot);
|
|
126
|
+
const migrationsDir = resolve(projectRoot, project.config.paths.migrations);
|
|
127
|
+
const createdFiles = [];
|
|
128
|
+
const resolvedTables = resolveDatabaseCacheTables(cacheConfig);
|
|
129
|
+
const seenPhysicalTables = /* @__PURE__ */ new Set();
|
|
130
|
+
const seenSlugs = /* @__PURE__ */ new Map();
|
|
131
|
+
for (const { table, lockTable } of resolvedTables) {
|
|
132
|
+
const migrationName = normalizeCacheMigrationName(table);
|
|
133
|
+
const previousTable = seenSlugs.get(migrationName);
|
|
134
|
+
if (table === lockTable || seenPhysicalTables.has(table) || seenPhysicalTables.has(lockTable) || previousTable && previousTable !== table) {
|
|
135
|
+
throw new Error(`A migration for cache tables "${table}" and "${lockTable}" already exists.`);
|
|
136
|
+
}
|
|
137
|
+
seenPhysicalTables.add(table);
|
|
138
|
+
seenPhysicalTables.add(lockTable);
|
|
139
|
+
seenSlugs.set(migrationName, table);
|
|
140
|
+
}
|
|
141
|
+
for (const { table, lockTable } of resolvedTables) {
|
|
142
|
+
const migrationName = normalizeCacheMigrationName(table);
|
|
143
|
+
if (hasRegisteredMigrationSlug(registry, migrationName) || hasRegisteredCreateTableMigration(registry, table) || hasRegisteredCreateTableMigration(registry, lockTable)) {
|
|
144
|
+
throw new Error(`A migration for cache tables "${table}" and "${lockTable}" already exists.`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
for (const { table, lockTable } of resolvedTables) {
|
|
148
|
+
const migrationTemplate = await nextMigrationTemplate(normalizeCacheMigrationName(table), migrationsDir);
|
|
149
|
+
const migrationFilePath = resolveDefaultArtifactPath(projectRoot, project.config.paths.migrations, migrationTemplate.fileName);
|
|
150
|
+
await writeTextFile(migrationFilePath, renderCacheTableMigration(table, lockTable));
|
|
151
|
+
createdFiles.push(migrationFilePath);
|
|
152
|
+
}
|
|
153
|
+
await runProjectPrepare(projectRoot);
|
|
154
|
+
for (const filePath of createdFiles) {
|
|
155
|
+
writeLine(io.stdout, `Created migration: ${makeProjectRelativePath(projectRoot, filePath)}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
var cacheMigrationInternals = {
|
|
159
|
+
getRegistryMigrationSlug,
|
|
160
|
+
hasRegisteredMigrationSlug,
|
|
161
|
+
hasRegisteredCreateTableMigration,
|
|
162
|
+
nextMigrationTemplate
|
|
163
|
+
};
|
|
164
|
+
export {
|
|
165
|
+
DEFAULT_CACHE_DATABASE_LOCK_TABLE,
|
|
166
|
+
DEFAULT_CACHE_DATABASE_TABLE,
|
|
167
|
+
cacheMigrationInternals,
|
|
168
|
+
loadCacheConfig,
|
|
169
|
+
normalizeCacheMigrationName,
|
|
170
|
+
renderCacheTableMigration,
|
|
171
|
+
resolveDatabaseCacheTables,
|
|
172
|
+
runCacheTableCommand
|
|
173
|
+
};
|