@holo-js/cli 0.2.5 → 0.3.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/{agent-skills-MSEMQUXT.mjs → agent-skills-CHAXS2YN.mjs} +13 -6
- package/dist/bin/holo.mjs +436 -109
- package/dist/{broadcast-5MJ3GJK7.mjs → broadcast-5VZM5SGQ.mjs} +13 -10
- package/dist/{broadcast-MRJGFCFH.mjs → broadcast-AABT52BK.mjs} +13 -10
- package/dist/cache-A42JSVYA.mjs +138 -0
- package/dist/cache-DUVBBOZU.mjs +138 -0
- package/dist/{cache-migrations-CPYQC7T6.mjs → cache-migrations-4WKSEN2Q.mjs} +12 -10
- package/dist/{cache-migrations-APDFSFEX.mjs → cache-migrations-PD2T5AV4.mjs} +14 -12
- package/dist/chunk-3RFRE7XG.mjs +491 -0
- package/dist/{chunk-PT72AMV7.mjs → chunk-6BHJLCAO.mjs} +256 -392
- package/dist/{chunk-KS5TWO75.mjs → chunk-EQUFFLRG.mjs} +7 -7
- package/dist/chunk-HX6MOYBO.mjs +2 -0
- package/dist/{chunk-YEFJBN56.mjs → chunk-JVLZ3SOW.mjs} +11 -8
- package/dist/{chunk-UPXQONEH.mjs → chunk-KRADH7PK.mjs} +55 -51
- package/dist/chunk-LALDNNPH.mjs +428 -0
- package/dist/chunk-LHK5YUR4.mjs +156 -0
- package/dist/{chunk-LBJAJLKU.mjs → chunk-M3IPUCTM.mjs} +1 -1
- package/dist/{chunk-TWITVSFZ.mjs → chunk-OI54DOHK.mjs} +256 -392
- package/dist/{chunk-LXGQCG56.mjs → chunk-OLZEMIDQ.mjs} +1 -1
- package/dist/{chunk-VP2E62DF.mjs → chunk-QSVMXMTQ.mjs} +49 -349
- package/dist/{chunk-J76GH2DR.mjs → chunk-R4ZYMEDU.mjs} +129 -86
- package/dist/{chunk-XDRCZ3Z5.mjs → chunk-TH5YCOZB.mjs} +57 -53
- package/dist/chunk-TRYHX5S4.mjs +360 -0
- package/dist/chunk-XBOVM6LO.mjs +41 -0
- package/dist/{chunk-ILU426CF.mjs → chunk-YACY2572.mjs} +482 -2
- package/dist/{config-MD27U4FM.mjs → config-BZHQK532.mjs} +3 -3
- package/dist/{dev-CT4DRTLN.mjs → dev-7KGRV56X.mjs} +10 -8
- package/dist/{dev-QDLR6C7K.mjs → dev-GQT37XJU.mjs} +10 -8
- package/dist/{discovery-GWTBF5RZ.mjs → discovery-HNH3BLDL.mjs} +8 -6
- package/dist/{generators-PUN5TFP5.mjs → generators-3FVW4AEJ.mjs} +18 -15
- package/dist/{generators-WQHI7DBW.mjs → generators-JWVNGUUE.mjs} +14 -11
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +434 -107
- package/dist/{media-migrations-D3CDNMPT.mjs → media-migrations-EG34NYAG.mjs} +13 -11
- package/dist/{media-migrations-IWCSCKSA.mjs → media-migrations-LGI7R7AR.mjs} +11 -9
- package/dist/plugins-ZGLB6JO6.mjs +24 -0
- package/dist/{queue-O3OSLGOV.mjs → queue-BEZDCZCY.mjs} +54 -16
- package/dist/{queue-GRB2IH5M.mjs → queue-R54DUUYA.mjs} +54 -16
- package/dist/{queue-migrations-7T42S4FX.mjs → queue-migrations-AM3AKT76.mjs} +16 -11
- package/dist/{queue-migrations-HFZB64OG.mjs → queue-migrations-U477YSFD.mjs} +18 -13
- package/dist/{runtime-TTRP2DGL.mjs → runtime-4TJYZLQV.mjs} +10 -8
- package/dist/{runtime-M3G5OKQV.mjs → runtime-MSRQD5W5.mjs} +10 -8
- package/dist/{runtime-GSXF4NB3.mjs → runtime-VR5BDPYQ.mjs} +1 -1
- package/dist/runtime-worker.mjs +7 -1
- package/dist/{scaffold-5BOVJYF3.mjs → scaffold-ASEX5LEX.mjs} +8 -4
- package/dist/{scaffold-NY3MWUZF.mjs → scaffold-ZAD3XUSD.mjs} +8 -4
- package/dist/{security-ST6ED7EK.mjs → security-4QLV7QCH.mjs} +8 -6
- package/dist/{security-H7AGDFBX.mjs → security-KYTLP4DF.mjs} +8 -6
- package/package.json +35 -8
- package/dist/cache-KVRYC7FB.mjs +0 -66
- package/dist/cache-WWE4Q4YE.mjs +0 -66
- package/dist/chunk-D7O4SU6N.mjs +0 -2
- package/dist/chunk-FGQ2I2YH.mjs +0 -22
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
import {
|
|
2
|
+
fileExists
|
|
3
|
+
} from "./chunk-M3IPUCTM.mjs";
|
|
4
|
+
import {
|
|
5
|
+
prepareProjectDiscovery
|
|
6
|
+
} from "./chunk-QSVMXMTQ.mjs";
|
|
7
|
+
import {
|
|
8
|
+
loadProjectConfig,
|
|
9
|
+
resolveGeneratedSchemaPath
|
|
10
|
+
} from "./chunk-JVLZ3SOW.mjs";
|
|
11
|
+
import {
|
|
12
|
+
loadGeneratedProjectRegistry
|
|
13
|
+
} from "./chunk-R4ZYMEDU.mjs";
|
|
14
|
+
import {
|
|
15
|
+
CLI_RUNTIME_ROOT,
|
|
16
|
+
GENERATED_SCHEMA_RUNTIME_PATH,
|
|
17
|
+
bundleProjectModule
|
|
18
|
+
} from "./chunk-YACY2572.mjs";
|
|
19
|
+
|
|
20
|
+
// src/runtime.ts
|
|
21
|
+
import { spawn } from "child_process";
|
|
22
|
+
import { mkdir, readFile, rm, symlink, writeFile } from "fs/promises";
|
|
23
|
+
import { dirname, join, resolve } from "path";
|
|
24
|
+
import { setTimeout } from "timers/promises";
|
|
25
|
+
import { fileURLToPath, pathToFileURL } from "url";
|
|
26
|
+
import { writeConfigCache } from "@holo-js/config";
|
|
27
|
+
var runtimeImportMeta = import.meta;
|
|
28
|
+
var RUNTIME_DEPENDENCY_LOCK_RETRY_MS = 10;
|
|
29
|
+
function resolveConfigModuleUrl(runtimeResolve = runtimeImportMeta.resolve?.bind(runtimeImportMeta)) {
|
|
30
|
+
if (typeof runtimeResolve === "function") {
|
|
31
|
+
const resolved = runtimeResolve("@holo-js/config");
|
|
32
|
+
if (resolved.startsWith("file://")) {
|
|
33
|
+
const resolvedPath = fileURLToPath(resolved);
|
|
34
|
+
const normalized = resolvedPath.replace(/\\/g, "/");
|
|
35
|
+
if (normalized.endsWith("/src/index.ts") || normalized.endsWith("/src/index.mts") || normalized.endsWith("/src/index.js") || normalized.endsWith("/src/index.mjs")) {
|
|
36
|
+
return pathToFileURL(resolve(dirname(dirname(resolvedPath)), "dist/index.mjs")).href;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return resolved;
|
|
40
|
+
}
|
|
41
|
+
return pathToFileURL(resolve(dirname(fileURLToPath(import.meta.url)), "../node_modules/@holo-js/config/dist/index.mjs")).href;
|
|
42
|
+
}
|
|
43
|
+
async function initializeProjectRuntime(projectRoot, options = {}) {
|
|
44
|
+
const { initializeHolo } = await import("@holo-js/core");
|
|
45
|
+
return initializeHolo(projectRoot, options);
|
|
46
|
+
}
|
|
47
|
+
function parseBooleanEnv(value) {
|
|
48
|
+
if (value === "true") return true;
|
|
49
|
+
if (value === "false") return false;
|
|
50
|
+
return void 0;
|
|
51
|
+
}
|
|
52
|
+
async function cacheProjectConfig(projectRoot, cacheWriter = writeConfigCache) {
|
|
53
|
+
try {
|
|
54
|
+
return await cacheWriter(projectRoot, { processEnv: process.env });
|
|
55
|
+
} catch (error) {
|
|
56
|
+
throw new Error(error instanceof Error && error.message ? error.message : "Failed to cache config.");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function createEnvRuntimeConfig() {
|
|
60
|
+
return {
|
|
61
|
+
db: {
|
|
62
|
+
defaultConnection: "default",
|
|
63
|
+
connections: {
|
|
64
|
+
default: {
|
|
65
|
+
driver: process.env.DB_DRIVER,
|
|
66
|
+
url: process.env.DB_URL,
|
|
67
|
+
host: process.env.DB_HOST,
|
|
68
|
+
port: process.env.DB_PORT,
|
|
69
|
+
username: process.env.DB_USERNAME,
|
|
70
|
+
password: process.env.DB_PASSWORD,
|
|
71
|
+
database: process.env.DB_DATABASE,
|
|
72
|
+
schema: process.env.DB_SCHEMA,
|
|
73
|
+
ssl: parseBooleanEnv(process.env.DB_SSL),
|
|
74
|
+
logging: parseBooleanEnv(process.env.DB_LOGGING)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function normalizeRuntimeConnectionInput(connection) {
|
|
81
|
+
if (typeof connection === "string") {
|
|
82
|
+
return { url: connection };
|
|
83
|
+
}
|
|
84
|
+
return connection ? { ...connection } : {};
|
|
85
|
+
}
|
|
86
|
+
function isDefined(value) {
|
|
87
|
+
return value !== void 0;
|
|
88
|
+
}
|
|
89
|
+
function filterDefinedRuntimeConnectionInput(connection) {
|
|
90
|
+
return Object.fromEntries(
|
|
91
|
+
Object.entries(connection).filter(([, value]) => isDefined(value))
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
function mergeRuntimeDatabaseConfig(config, envRuntimeConfig) {
|
|
95
|
+
const envDefault = envRuntimeConfig.db.connections.default;
|
|
96
|
+
const hasEnvOverrides = Object.values(envDefault).some(isDefined);
|
|
97
|
+
if (!config) {
|
|
98
|
+
return envRuntimeConfig.db;
|
|
99
|
+
}
|
|
100
|
+
if (!hasEnvOverrides) {
|
|
101
|
+
return config;
|
|
102
|
+
}
|
|
103
|
+
const defaultConnection = config.defaultConnection ?? "default";
|
|
104
|
+
const connections = { ...config.connections ?? {} };
|
|
105
|
+
connections[defaultConnection] = {
|
|
106
|
+
...normalizeRuntimeConnectionInput(connections[defaultConnection]),
|
|
107
|
+
...filterDefinedRuntimeConnectionInput(envDefault)
|
|
108
|
+
};
|
|
109
|
+
return {
|
|
110
|
+
...config,
|
|
111
|
+
defaultConnection,
|
|
112
|
+
connections
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
var RUNTIME_MIGRATION_NAME_PATTERN = /^\d{4}_\d{2}_\d{2}_\d{6}_[a-z0-9_]+$/;
|
|
116
|
+
function inferRuntimeMigrationName(entry) {
|
|
117
|
+
const fileName = entry.split("/").pop()?.replace(/\.[^.]+$/, "");
|
|
118
|
+
if (!fileName || !RUNTIME_MIGRATION_NAME_PATTERN.test(fileName)) {
|
|
119
|
+
throw new Error(`Registered migration "${entry}" must use a timestamped file name matching YYYY_MM_DD_HHMMSS_description.`);
|
|
120
|
+
}
|
|
121
|
+
return fileName;
|
|
122
|
+
}
|
|
123
|
+
function normalizeRuntimeMigration(entry, migration) {
|
|
124
|
+
return {
|
|
125
|
+
...migration,
|
|
126
|
+
name: typeof migration.name === "string" ? migration.name : inferRuntimeMigrationName(entry)
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function compileFreshDropIdentifierPath(quoteIdentifier, identifier) {
|
|
130
|
+
if (!identifier.includes(".")) {
|
|
131
|
+
return quoteIdentifier(identifier);
|
|
132
|
+
}
|
|
133
|
+
return identifier.split(".").map((part) => quoteIdentifier(part)).join(".");
|
|
134
|
+
}
|
|
135
|
+
async function dropAllTablesForFresh(connection, schema) {
|
|
136
|
+
const tables = await schema.getTables();
|
|
137
|
+
if (connection.getDialect().name === "postgres") {
|
|
138
|
+
const schemaName = connection.getSchemaName();
|
|
139
|
+
const quoteIdentifier = connection.getDialect().quoteIdentifier;
|
|
140
|
+
for (const tableName of tables) {
|
|
141
|
+
const qualifiedTableName = schemaName ? `${schemaName}.${tableName}` : tableName;
|
|
142
|
+
await connection.executeCompiled({
|
|
143
|
+
sql: `DROP TABLE IF EXISTS ${compileFreshDropIdentifierPath(quoteIdentifier, qualifiedTableName)} CASCADE`,
|
|
144
|
+
source: `schema:dropTableFresh:${qualifiedTableName}`
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
await schema.withoutForeignKeyConstraints(async () => {
|
|
150
|
+
for (const tableName of tables) {
|
|
151
|
+
await schema.dropTable(tableName);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
async function getRuntimeEnvironment(projectRoot) {
|
|
156
|
+
let project = await loadProjectConfig(projectRoot, { required: true });
|
|
157
|
+
if (!await loadGeneratedProjectRegistry(projectRoot)) {
|
|
158
|
+
await prepareProjectDiscovery(projectRoot, project.config);
|
|
159
|
+
project = await loadProjectConfig(projectRoot, { required: true });
|
|
160
|
+
}
|
|
161
|
+
const generatedSchemaPath = resolveGeneratedSchemaPath(projectRoot, project.config);
|
|
162
|
+
const hasGeneratedSchema = await fileExists(generatedSchemaPath);
|
|
163
|
+
const bundleInputs = [
|
|
164
|
+
...project.config.models.map((entry) => resolve(projectRoot, entry)),
|
|
165
|
+
...project.config.migrations.map((entry) => resolve(projectRoot, entry)),
|
|
166
|
+
...project.config.seeders.map((entry) => resolve(projectRoot, entry)),
|
|
167
|
+
...hasGeneratedSchema ? [generatedSchemaPath] : []
|
|
168
|
+
];
|
|
169
|
+
const bundled = [];
|
|
170
|
+
try {
|
|
171
|
+
for (const entryPath of bundleInputs) {
|
|
172
|
+
bundled.push(await bundleProjectModule(projectRoot, entryPath, { external: ["@holo-js/db"] }));
|
|
173
|
+
}
|
|
174
|
+
} catch (error) {
|
|
175
|
+
await Promise.all(bundled.map((entry) => entry.cleanup()));
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
178
|
+
const bundledModels = bundled.slice(0, project.config.models.length).map((entry) => entry.path);
|
|
179
|
+
const bundledMigrations = bundled.slice(project.config.models.length, project.config.models.length + project.config.migrations.length).map((entry) => entry.path);
|
|
180
|
+
const bundledSeeders = bundled.slice(project.config.models.length + project.config.migrations.length).slice(0, project.config.seeders.length).map((entry) => entry.path);
|
|
181
|
+
const bundledGeneratedSchema = hasGeneratedSchema ? bundled[project.config.models.length + project.config.migrations.length + project.config.seeders.length]?.path : void 0;
|
|
182
|
+
return {
|
|
183
|
+
project,
|
|
184
|
+
bundledModels,
|
|
185
|
+
bundledMigrations,
|
|
186
|
+
bundledSeeders,
|
|
187
|
+
...bundledGeneratedSchema ? { bundledGeneratedSchema } : {},
|
|
188
|
+
async cleanup() {
|
|
189
|
+
await Promise.all(bundled.map((entry) => entry.cleanup()));
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
async function resolvePackageRootFromSpecifier(specifier) {
|
|
194
|
+
let current = dirname(fileURLToPath(import.meta.resolve(specifier)));
|
|
195
|
+
while (true) {
|
|
196
|
+
if (await fileExists(join(current, "package.json"))) {
|
|
197
|
+
return current;
|
|
198
|
+
}
|
|
199
|
+
const parent = dirname(current);
|
|
200
|
+
if (parent === current) {
|
|
201
|
+
throw new Error(`Could not resolve package root for "${specifier}".`);
|
|
202
|
+
}
|
|
203
|
+
current = parent;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function isFileExistsError(error) {
|
|
207
|
+
return error instanceof Error && "code" in error && error.code === "EEXIST";
|
|
208
|
+
}
|
|
209
|
+
async function withRuntimeDependencyLock(projectRoot, callback) {
|
|
210
|
+
const runtimeRoot = join(projectRoot, CLI_RUNTIME_ROOT);
|
|
211
|
+
const lockDir = join(runtimeRoot, "node_modules.lock");
|
|
212
|
+
await mkdir(runtimeRoot, { recursive: true });
|
|
213
|
+
while (true) {
|
|
214
|
+
try {
|
|
215
|
+
await mkdir(lockDir);
|
|
216
|
+
break;
|
|
217
|
+
} catch (error) {
|
|
218
|
+
if (!isFileExistsError(error)) {
|
|
219
|
+
throw error;
|
|
220
|
+
}
|
|
221
|
+
await setTimeout(RUNTIME_DEPENDENCY_LOCK_RETRY_MS);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
try {
|
|
225
|
+
return await callback();
|
|
226
|
+
} finally {
|
|
227
|
+
await rm(lockDir, { recursive: true, force: true });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
async function readRuntimeDependencyReferenceCount(refPath) {
|
|
231
|
+
const raw = await readFile(refPath, "utf8").catch(() => void 0);
|
|
232
|
+
const count = raw === void 0 ? 0 : Number.parseInt(raw, 10);
|
|
233
|
+
return Number.isFinite(count) && count > 0 ? count : 0;
|
|
234
|
+
}
|
|
235
|
+
async function ensureRuntimeDependencyLink(projectRoot) {
|
|
236
|
+
return await withRuntimeDependencyLock(projectRoot, async () => {
|
|
237
|
+
const runtimeRoot = join(projectRoot, CLI_RUNTIME_ROOT);
|
|
238
|
+
const packageRoot = await resolvePackageRootFromSpecifier("@holo-js/db");
|
|
239
|
+
const nodeModulesDir = join(runtimeRoot, "node_modules");
|
|
240
|
+
const namespaceDir = join(nodeModulesDir, "@holo-js");
|
|
241
|
+
const targetPath = join(namespaceDir, "db");
|
|
242
|
+
const refPath = join(nodeModulesDir, ".holo-js-runtime-refs");
|
|
243
|
+
const references = await readRuntimeDependencyReferenceCount(refPath);
|
|
244
|
+
await mkdir(namespaceDir, { recursive: true });
|
|
245
|
+
if (references === 0) {
|
|
246
|
+
await rm(targetPath, { recursive: true, force: true });
|
|
247
|
+
await symlink(packageRoot, targetPath, "junction");
|
|
248
|
+
}
|
|
249
|
+
await writeFile(refPath, String(references + 1), "utf8");
|
|
250
|
+
return runtimeRoot;
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
async function cleanupRuntimeDependencyLink(projectRoot) {
|
|
254
|
+
await withRuntimeDependencyLock(projectRoot, async () => {
|
|
255
|
+
const nodeModulesDir = join(projectRoot, CLI_RUNTIME_ROOT, "node_modules");
|
|
256
|
+
const refPath = join(nodeModulesDir, ".holo-js-runtime-refs");
|
|
257
|
+
const references = await readRuntimeDependencyReferenceCount(refPath);
|
|
258
|
+
const nextReferences = references - 1;
|
|
259
|
+
if (nextReferences <= 0) {
|
|
260
|
+
await rm(nodeModulesDir, { recursive: true, force: true });
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
await writeFile(refPath, String(nextReferences), "utf8");
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
function resolveRuntimeWorkerPath() {
|
|
267
|
+
const runtimePath = fileURLToPath(import.meta.url);
|
|
268
|
+
return runtimePath.endsWith("/src/runtime.ts") ? resolve(dirname(runtimePath), "runtime-worker.ts") : resolve(dirname(runtimePath), "runtime-worker.mjs");
|
|
269
|
+
}
|
|
270
|
+
function supportsNodeTypeStripping(version = process.versions.node) {
|
|
271
|
+
const [major = "0", minor = "0"] = version.split(".");
|
|
272
|
+
const majorVersion = Number.parseInt(major, 10);
|
|
273
|
+
const minorVersion = Number.parseInt(minor, 10);
|
|
274
|
+
return majorVersion > 22 || majorVersion === 22 && minorVersion >= 6;
|
|
275
|
+
}
|
|
276
|
+
function resolveCompiledRuntimeWorkerPath(workerPath) {
|
|
277
|
+
return resolve(dirname(workerPath), "../dist/runtime-worker.mjs");
|
|
278
|
+
}
|
|
279
|
+
function createRuntimeInvocation(workerPath = resolveRuntimeWorkerPath()) {
|
|
280
|
+
if (workerPath.endsWith(".ts")) {
|
|
281
|
+
return {
|
|
282
|
+
command: "node",
|
|
283
|
+
args: supportsNodeTypeStripping() ? ["--experimental-strip-types", workerPath] : [resolveCompiledRuntimeWorkerPath(workerPath)]
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
return {
|
|
287
|
+
command: "node",
|
|
288
|
+
args: [workerPath]
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
function runRuntimeInvocation(command, args, options) {
|
|
292
|
+
return new Promise((resolveInvocation) => {
|
|
293
|
+
const child = spawn(command, args, {
|
|
294
|
+
cwd: options.cwd,
|
|
295
|
+
env: options.env,
|
|
296
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
297
|
+
});
|
|
298
|
+
let stdout = "";
|
|
299
|
+
let stderr = "";
|
|
300
|
+
let error = null;
|
|
301
|
+
child.stdout.setEncoding("utf8");
|
|
302
|
+
child.stdout.on("data", (chunk) => {
|
|
303
|
+
stdout += chunk;
|
|
304
|
+
});
|
|
305
|
+
child.stderr.setEncoding("utf8");
|
|
306
|
+
child.stderr.on("data", (chunk) => {
|
|
307
|
+
stderr += chunk;
|
|
308
|
+
});
|
|
309
|
+
child.on("error", (spawnError) => {
|
|
310
|
+
error = spawnError;
|
|
311
|
+
});
|
|
312
|
+
child.on("close", (status) => {
|
|
313
|
+
resolveInvocation({
|
|
314
|
+
status,
|
|
315
|
+
error,
|
|
316
|
+
stdout,
|
|
317
|
+
stderr
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
function getRuntimeFailureMessage(kind, result) {
|
|
323
|
+
const stderr = result.stderr?.trim();
|
|
324
|
+
if (stderr) {
|
|
325
|
+
return formatRuntimeFailureText(stderr);
|
|
326
|
+
}
|
|
327
|
+
const stdout = result.stdout?.trim();
|
|
328
|
+
if (stdout) {
|
|
329
|
+
return formatRuntimeFailureText(stdout);
|
|
330
|
+
}
|
|
331
|
+
const errorCode = result.error && "code" in result.error ? result.error.code : void 0;
|
|
332
|
+
if (typeof errorCode === "string" && errorCode.length > 0) {
|
|
333
|
+
return `Failed to launch runtime command "${kind}": ${errorCode}.`;
|
|
334
|
+
}
|
|
335
|
+
return `Runtime command "${kind}" failed.`;
|
|
336
|
+
}
|
|
337
|
+
function stripRuntimeErrorPrefix(line) {
|
|
338
|
+
return line.replace(/^(?:[A-Za-z][\w.]*Error|Error):\s+/, "");
|
|
339
|
+
}
|
|
340
|
+
function isRuntimeErrorSourceLine(line) {
|
|
341
|
+
return line.startsWith("throw ") || line.startsWith("at ") || line === "^" || /^\^+$/.test(line);
|
|
342
|
+
}
|
|
343
|
+
function formatRuntimeFailureText(text) {
|
|
344
|
+
const lines = text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
345
|
+
const friendlyLine = lines.find((line) => line.includes("Unable to ") && !isRuntimeErrorSourceLine(line));
|
|
346
|
+
if (friendlyLine) {
|
|
347
|
+
return stripRuntimeErrorPrefix(friendlyLine);
|
|
348
|
+
}
|
|
349
|
+
const errorLine = lines.find((line) => /^(?:[A-Za-z][\w.]*Error|Error):\s+/.test(line));
|
|
350
|
+
if (errorLine) {
|
|
351
|
+
return stripRuntimeErrorPrefix(errorLine);
|
|
352
|
+
}
|
|
353
|
+
return lines[0] ?? text.trim();
|
|
354
|
+
}
|
|
355
|
+
async function withRuntimeEnvironment(projectRoot, kind, options, callback) {
|
|
356
|
+
if (kind === "seed") {
|
|
357
|
+
const project = await loadProjectConfig(projectRoot, { required: true });
|
|
358
|
+
await prepareProjectDiscovery(projectRoot, project.config);
|
|
359
|
+
}
|
|
360
|
+
const environment = await getRuntimeEnvironment(projectRoot);
|
|
361
|
+
let dependencyLinkEnsured = false;
|
|
362
|
+
try {
|
|
363
|
+
const envRuntimeConfig = createEnvRuntimeConfig();
|
|
364
|
+
const runtimeDatabaseConfig = mergeRuntimeDatabaseConfig(
|
|
365
|
+
environment.project.config.database,
|
|
366
|
+
envRuntimeConfig
|
|
367
|
+
);
|
|
368
|
+
const runtimeRoot = await ensureRuntimeDependencyLink(projectRoot);
|
|
369
|
+
dependencyLinkEnsured = true;
|
|
370
|
+
const runtimePayload = JSON.stringify({
|
|
371
|
+
kind,
|
|
372
|
+
projectRoot,
|
|
373
|
+
runtimeConfig: {
|
|
374
|
+
db: runtimeDatabaseConfig
|
|
375
|
+
},
|
|
376
|
+
models: environment.bundledModels.map((entry) => pathToFileURL(entry).href),
|
|
377
|
+
migrations: environment.bundledMigrations.map((entry) => pathToFileURL(entry).href),
|
|
378
|
+
seeders: environment.bundledSeeders.map((entry) => pathToFileURL(entry).href),
|
|
379
|
+
generatedSchema: environment.bundledGeneratedSchema ? pathToFileURL(environment.bundledGeneratedSchema).href : void 0,
|
|
380
|
+
generatedSchemaOutputPath: resolveGeneratedSchemaPath(projectRoot, environment.project.config),
|
|
381
|
+
generatedSchemaRuntimeOutputPath: resolve(projectRoot, GENERATED_SCHEMA_RUNTIME_PATH),
|
|
382
|
+
options
|
|
383
|
+
});
|
|
384
|
+
const runtime = createRuntimeInvocation();
|
|
385
|
+
const result = await runRuntimeInvocation(runtime.command, runtime.args, {
|
|
386
|
+
cwd: runtimeRoot,
|
|
387
|
+
env: {
|
|
388
|
+
...process.env,
|
|
389
|
+
HOLO_RUNTIME_PAYLOAD: runtimePayload
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
if (result.status !== 0) {
|
|
393
|
+
throw new Error(getRuntimeFailureMessage(kind, result));
|
|
394
|
+
}
|
|
395
|
+
return await callback((result.stdout ?? "").trim());
|
|
396
|
+
} finally {
|
|
397
|
+
if (dependencyLinkEnsured) {
|
|
398
|
+
await cleanupRuntimeDependencyLink(projectRoot);
|
|
399
|
+
}
|
|
400
|
+
await environment.cleanup();
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export {
|
|
405
|
+
resolveConfigModuleUrl,
|
|
406
|
+
initializeProjectRuntime,
|
|
407
|
+
parseBooleanEnv,
|
|
408
|
+
cacheProjectConfig,
|
|
409
|
+
createEnvRuntimeConfig,
|
|
410
|
+
normalizeRuntimeConnectionInput,
|
|
411
|
+
isDefined,
|
|
412
|
+
filterDefinedRuntimeConnectionInput,
|
|
413
|
+
mergeRuntimeDatabaseConfig,
|
|
414
|
+
RUNTIME_MIGRATION_NAME_PATTERN,
|
|
415
|
+
inferRuntimeMigrationName,
|
|
416
|
+
normalizeRuntimeMigration,
|
|
417
|
+
compileFreshDropIdentifierPath,
|
|
418
|
+
dropAllTablesForFresh,
|
|
419
|
+
getRuntimeEnvironment,
|
|
420
|
+
resolvePackageRootFromSpecifier,
|
|
421
|
+
ensureRuntimeDependencyLink,
|
|
422
|
+
cleanupRuntimeDependencyLink,
|
|
423
|
+
resolveRuntimeWorkerPath,
|
|
424
|
+
createRuntimeInvocation,
|
|
425
|
+
runRuntimeInvocation,
|
|
426
|
+
getRuntimeFailureMessage,
|
|
427
|
+
withRuntimeEnvironment
|
|
428
|
+
};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HOLO_PACKAGE_VERSION,
|
|
3
|
+
readTextFile,
|
|
4
|
+
writeTextFile
|
|
5
|
+
} from "./chunk-YACY2572.mjs";
|
|
6
|
+
|
|
7
|
+
// src/package-json.ts
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
function normalizeDependencyMap(value) {
|
|
10
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
11
|
+
return {};
|
|
12
|
+
}
|
|
13
|
+
return Object.fromEntries(
|
|
14
|
+
Object.entries(value).filter(([, dependencyVersion]) => typeof dependencyVersion === "string").sort(([left], [right]) => left.localeCompare(right))
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
function isWorkspaceDependencyVersion(value) {
|
|
18
|
+
return typeof value === "string" && value.startsWith("workspace:");
|
|
19
|
+
}
|
|
20
|
+
function assertValidDependencyName(packageName) {
|
|
21
|
+
if (!/^@[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*$/.test(packageName) && !/^[a-z0-9][a-z0-9._-]*$/.test(packageName)) {
|
|
22
|
+
throw new Error(`Invalid dependency package name: ${packageName || "(empty)"}.`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function resolveInstalledPackageJsonPath(projectRoot, packageName) {
|
|
26
|
+
return join(projectRoot, "node_modules", ...packageName.split("/"), "package.json");
|
|
27
|
+
}
|
|
28
|
+
async function resolveInstalledPackageVersion(projectRoot, packageName) {
|
|
29
|
+
const packageJson = await readTextFile(resolveInstalledPackageJsonPath(projectRoot, packageName));
|
|
30
|
+
if (!packageJson) {
|
|
31
|
+
return void 0;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const parsed = JSON.parse(packageJson);
|
|
35
|
+
return typeof parsed.version === "string" && parsed.version.trim() ? `^${parsed.version.trim()}` : void 0;
|
|
36
|
+
} catch {
|
|
37
|
+
return void 0;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function resolvePackageVersion(projectRoot, packageName, dependencies, devDependencies) {
|
|
41
|
+
const currentPackageVersion = dependencies[packageName] ?? devDependencies[packageName];
|
|
42
|
+
if (typeof currentPackageVersion === "string") {
|
|
43
|
+
return currentPackageVersion;
|
|
44
|
+
}
|
|
45
|
+
const workspaceVersion = Object.entries({
|
|
46
|
+
...dependencies,
|
|
47
|
+
...devDependencies
|
|
48
|
+
}).find(([dependencyName, dependencyVersion]) => dependencyName.startsWith("@holo-js/") && isWorkspaceDependencyVersion(dependencyVersion))?.[1];
|
|
49
|
+
if (packageName.startsWith("@holo-js/")) {
|
|
50
|
+
return workspaceVersion ?? `^${HOLO_PACKAGE_VERSION}`;
|
|
51
|
+
}
|
|
52
|
+
return await resolveInstalledPackageVersion(projectRoot, packageName) ?? "latest";
|
|
53
|
+
}
|
|
54
|
+
function sortDependencyMap(dependencies) {
|
|
55
|
+
return Object.fromEntries(
|
|
56
|
+
Object.entries(dependencies).sort(([left], [right]) => left.localeCompare(right))
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
async function readPackageJsonDependencyState(projectRoot) {
|
|
60
|
+
const packageJsonPath = join(projectRoot, "package.json");
|
|
61
|
+
const packageJson = await readTextFile(packageJsonPath);
|
|
62
|
+
if (!packageJson) {
|
|
63
|
+
throw new Error(`Missing package.json in ${projectRoot}.`);
|
|
64
|
+
}
|
|
65
|
+
let parsed;
|
|
66
|
+
try {
|
|
67
|
+
parsed = JSON.parse(packageJson);
|
|
68
|
+
} catch {
|
|
69
|
+
throw new Error(`Invalid package.json in ${projectRoot}.`);
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
packageJsonPath,
|
|
73
|
+
parsed,
|
|
74
|
+
dependencies: normalizeDependencyMap(parsed.dependencies),
|
|
75
|
+
devDependencies: normalizeDependencyMap(parsed.devDependencies)
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
async function writePackageJsonDependencyState(state) {
|
|
79
|
+
state.parsed.dependencies = sortDependencyMap(state.dependencies);
|
|
80
|
+
if (Object.keys(state.devDependencies).length > 0) {
|
|
81
|
+
state.parsed.devDependencies = sortDependencyMap(state.devDependencies);
|
|
82
|
+
} else {
|
|
83
|
+
delete state.parsed.devDependencies;
|
|
84
|
+
}
|
|
85
|
+
await writeTextFile(state.packageJsonPath, `${JSON.stringify(state.parsed, null, 2)}
|
|
86
|
+
`);
|
|
87
|
+
}
|
|
88
|
+
async function hasProjectDependency(projectRoot, packageName) {
|
|
89
|
+
const packageJson = await readTextFile(join(projectRoot, "package.json"));
|
|
90
|
+
if (!packageJson) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
const parsed = JSON.parse(packageJson);
|
|
95
|
+
return typeof parsed.dependencies?.[packageName] === "string" || typeof parsed.devDependencies?.[packageName] === "string";
|
|
96
|
+
} catch {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async function upsertProjectDependency(projectRoot, packageName) {
|
|
101
|
+
assertValidDependencyName(packageName);
|
|
102
|
+
const state = await readPackageJsonDependencyState(projectRoot);
|
|
103
|
+
const nextVersion = await resolvePackageVersion(projectRoot, packageName, state.dependencies, state.devDependencies);
|
|
104
|
+
if (state.dependencies[packageName] === nextVersion && typeof state.devDependencies[packageName] === "undefined") {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
state.dependencies[packageName] = nextVersion;
|
|
108
|
+
delete state.devDependencies[packageName];
|
|
109
|
+
await writePackageJsonDependencyState(state);
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
async function pinProjectDependencyVersions(projectRoot, packageNames) {
|
|
113
|
+
const state = await readPackageJsonDependencyState(projectRoot);
|
|
114
|
+
let changed = false;
|
|
115
|
+
for (const packageName of packageNames) {
|
|
116
|
+
assertValidDependencyName(packageName);
|
|
117
|
+
if (packageName.startsWith("@holo-js/")) {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const installedVersion = await resolveInstalledPackageVersion(projectRoot, packageName);
|
|
121
|
+
if (!installedVersion) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (state.dependencies[packageName] === "latest") {
|
|
125
|
+
state.dependencies[packageName] = installedVersion;
|
|
126
|
+
changed = true;
|
|
127
|
+
}
|
|
128
|
+
if (state.devDependencies[packageName] === "latest") {
|
|
129
|
+
state.devDependencies[packageName] = installedVersion;
|
|
130
|
+
changed = true;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (!changed) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
await writePackageJsonDependencyState(state);
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
async function removeProjectDependency(projectRoot, packageName) {
|
|
140
|
+
assertValidDependencyName(packageName);
|
|
141
|
+
const state = await readPackageJsonDependencyState(projectRoot);
|
|
142
|
+
if (typeof state.dependencies[packageName] === "undefined" && typeof state.devDependencies[packageName] === "undefined") {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
delete state.dependencies[packageName];
|
|
146
|
+
delete state.devDependencies[packageName];
|
|
147
|
+
await writePackageJsonDependencyState(state);
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export {
|
|
152
|
+
hasProjectDependency,
|
|
153
|
+
upsertProjectDependency,
|
|
154
|
+
pinProjectDependencyVersions,
|
|
155
|
+
removeProjectDependency
|
|
156
|
+
};
|