@amerged/ohmyhost-cli 0.1.30 → 0.1.32
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 +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-PRLVV4ZG.js → chunk-EPW2O2ZL.js} +1554 -709
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/apps/product-cli/package.json +1 -1
- package/src/apps/product-cli/src/composition.ts +2 -1
- package/src/apps/product-cli/src/repository-init.ts +443 -713
- package/src/apps/product-cli/src/runtime-contract.ts +18 -5
- package/src/packages/contracts/package.json +10 -0
- package/src/packages/contracts/src/framework-admission-corpus.ts +390 -0
- package/src/packages/contracts/src/framework-admission.ts +641 -197
- package/src/packages/contracts/src/framework-build-script.mjs +155 -13
- package/src/packages/contracts/src/framework-build-script.ts +15 -1
- package/src/packages/contracts/src/framework-source-rules.ts +1101 -0
- package/src/packages/contracts/src/operation-failure.ts +22 -4
- package/src/packages/contracts/src/problem-policy.ts +2 -2
- package/src/packages/sdk-ts/package.json +1 -1
- package/src/packages/sdk-ts/src/generated/sdk.gen.ts +1 -1
- package/src/packages/sdk-ts/src/generated/types.gen.ts +8 -0
|
@@ -140,16 +140,16 @@ var require_ignore = __commonJS({
|
|
|
140
140
|
}
|
|
141
141
|
};
|
|
142
142
|
var NEVER_MATCH = "[]";
|
|
143
|
-
var
|
|
143
|
+
var PLACEHOLDER2 = "\0";
|
|
144
144
|
var REGEX_RESTORE_PLACEHOLDER = new RegExp(
|
|
145
|
-
`${
|
|
145
|
+
`${PLACEHOLDER2}(\\d+)${PLACEHOLDER2}`,
|
|
146
146
|
"g"
|
|
147
147
|
);
|
|
148
148
|
var TRAILING_WILDCARD = "\uE000";
|
|
149
149
|
var TRAILING_DOUBLESTAR = "\uE001";
|
|
150
150
|
var extractBrackets = (pattern) => {
|
|
151
151
|
const sources = [];
|
|
152
|
-
const hold = (source) => `${
|
|
152
|
+
const hold = (source) => `${PLACEHOLDER2}${sources.push(source) - 1}${PLACEHOLDER2}`;
|
|
153
153
|
const { length } = pattern;
|
|
154
154
|
let out = EMPTY;
|
|
155
155
|
let index = 0;
|
|
@@ -167,8 +167,8 @@ var require_ignore = __commonJS({
|
|
|
167
167
|
);
|
|
168
168
|
}
|
|
169
169
|
index += 2;
|
|
170
|
-
} else if (char ===
|
|
171
|
-
out += hold(`[${
|
|
170
|
+
} else if (char === PLACEHOLDER2) {
|
|
171
|
+
out += hold(`[${PLACEHOLDER2}]`);
|
|
172
172
|
index++;
|
|
173
173
|
} else if (char === "[") {
|
|
174
174
|
const scanned = scanBracket(pattern, index);
|
|
@@ -25491,20 +25491,20 @@ var require_dependencies = __commonJS({
|
|
|
25491
25491
|
const deps = propertyDeps[prop];
|
|
25492
25492
|
if (deps.length === 0)
|
|
25493
25493
|
continue;
|
|
25494
|
-
const
|
|
25494
|
+
const hasProperty2 = (0, code_1.propertyInData)(gen, data, prop, it.opts.ownProperties);
|
|
25495
25495
|
cxt.setParams({
|
|
25496
25496
|
property: prop,
|
|
25497
25497
|
depsCount: deps.length,
|
|
25498
25498
|
deps: deps.join(", ")
|
|
25499
25499
|
});
|
|
25500
25500
|
if (it.allErrors) {
|
|
25501
|
-
gen.if(
|
|
25501
|
+
gen.if(hasProperty2, () => {
|
|
25502
25502
|
for (const depProp of deps) {
|
|
25503
25503
|
(0, code_1.checkReportMissingProp)(cxt, depProp);
|
|
25504
25504
|
}
|
|
25505
25505
|
});
|
|
25506
25506
|
} else {
|
|
25507
|
-
gen.if((0, codegen_1._)`${
|
|
25507
|
+
gen.if((0, codegen_1._)`${hasProperty2} && (${(0, code_1.checkMissingProp)(cxt, deps, missing2)})`);
|
|
25508
25508
|
(0, code_1.reportMissingProp)(cxt, missing2);
|
|
25509
25509
|
gen.else();
|
|
25510
25510
|
}
|
|
@@ -34333,7 +34333,7 @@ var require_public_api = __commonJS({
|
|
|
34333
34333
|
return docs;
|
|
34334
34334
|
return Object.assign([], { empty: true }, composer$1.streamInfo());
|
|
34335
34335
|
}
|
|
34336
|
-
function
|
|
34336
|
+
function parseDocument2(source, options = {}) {
|
|
34337
34337
|
const { lineCounter: lineCounter2, prettyErrors } = parseOptions2(options);
|
|
34338
34338
|
const parser$1 = new parser.Parser(lineCounter2?.addNewLine);
|
|
34339
34339
|
const composer$1 = new composer.Composer(options);
|
|
@@ -34359,7 +34359,7 @@ var require_public_api = __commonJS({
|
|
|
34359
34359
|
} else if (options === void 0 && reviver && typeof reviver === "object") {
|
|
34360
34360
|
options = reviver;
|
|
34361
34361
|
}
|
|
34362
|
-
const doc =
|
|
34362
|
+
const doc = parseDocument2(src, options);
|
|
34363
34363
|
if (!doc)
|
|
34364
34364
|
return null;
|
|
34365
34365
|
doc.warnings.forEach((warning) => log.warn(doc.options.logLevel, warning));
|
|
@@ -34395,7 +34395,7 @@ var require_public_api = __commonJS({
|
|
|
34395
34395
|
}
|
|
34396
34396
|
exports.parse = parse;
|
|
34397
34397
|
exports.parseAllDocuments = parseAllDocuments;
|
|
34398
|
-
exports.parseDocument =
|
|
34398
|
+
exports.parseDocument = parseDocument2;
|
|
34399
34399
|
exports.stringify = stringify;
|
|
34400
34400
|
}
|
|
34401
34401
|
});
|
|
@@ -34459,6 +34459,625 @@ import { createHash } from "node:crypto";
|
|
|
34459
34459
|
import { constants, promises as fs } from "node:fs";
|
|
34460
34460
|
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
34461
34461
|
|
|
34462
|
+
// packages/contracts/dist/framework-build-script.mjs
|
|
34463
|
+
var PINNED_BUN_VERSION = "1.2.22";
|
|
34464
|
+
var FRAMEWORK_VERSION_POLICY = Object.freeze({
|
|
34465
|
+
next: Object.freeze({ floors: Object.freeze(["15.5.27", "16.3.8"]), denied: Object.freeze([]) }),
|
|
34466
|
+
vite: Object.freeze({ floors: Object.freeze(["5.4.6"]), denied: Object.freeze([]) }),
|
|
34467
|
+
"@tanstack/react-start": Object.freeze({
|
|
34468
|
+
floors: Object.freeze(["1.168.60"]),
|
|
34469
|
+
denied: Object.freeze([])
|
|
34470
|
+
})
|
|
34471
|
+
});
|
|
34472
|
+
var STABLE_VERSION = /^(0|[1-9][0-9]{0,5})\.(0|[1-9][0-9]{0,5})\.(0|[1-9][0-9]{0,5})$/u;
|
|
34473
|
+
function parseStableVersion(version2) {
|
|
34474
|
+
if (typeof version2 !== "string")
|
|
34475
|
+
return null;
|
|
34476
|
+
const match = STABLE_VERSION.exec(version2);
|
|
34477
|
+
return match === null ? null : Object.freeze([Number(match[1]), Number(match[2]), Number(match[3])]);
|
|
34478
|
+
}
|
|
34479
|
+
function compareStableVersions(left, right) {
|
|
34480
|
+
return left[0] - right[0] || left[1] - right[1] || left[2] - right[2];
|
|
34481
|
+
}
|
|
34482
|
+
function packagePolicy(packageName, policy) {
|
|
34483
|
+
return typeof packageName === "string" && Object.hasOwn(policy, packageName) ? policy[
|
|
34484
|
+
/** @type {FrameworkVersionPackage} */
|
|
34485
|
+
packageName
|
|
34486
|
+
] : null;
|
|
34487
|
+
}
|
|
34488
|
+
function applicableFloor(entry, version2) {
|
|
34489
|
+
let floor = null;
|
|
34490
|
+
for (const candidate of entry.floors) {
|
|
34491
|
+
const parsed = parseStableVersion(candidate);
|
|
34492
|
+
if (parsed !== null && parsed[0] <= version2[0])
|
|
34493
|
+
floor = parsed;
|
|
34494
|
+
}
|
|
34495
|
+
return floor;
|
|
34496
|
+
}
|
|
34497
|
+
function frameworkVersionVerdict(packageName, version2, policy = FRAMEWORK_VERSION_POLICY) {
|
|
34498
|
+
const entry = packagePolicy(packageName, policy);
|
|
34499
|
+
const parsed = parseStableVersion(version2);
|
|
34500
|
+
if (entry === null || parsed === null)
|
|
34501
|
+
return "invalid_version";
|
|
34502
|
+
const floor = applicableFloor(entry, parsed);
|
|
34503
|
+
if (floor === null || compareStableVersions(parsed, floor) < 0)
|
|
34504
|
+
return "below_security_minimum";
|
|
34505
|
+
for (const [introduced, fixed] of entry.denied) {
|
|
34506
|
+
const from = parseStableVersion(introduced);
|
|
34507
|
+
const to = fixed === null ? null : parseStableVersion(fixed);
|
|
34508
|
+
if (from !== null && compareStableVersions(parsed, from) >= 0 && (to === null || compareStableVersions(parsed, to) < 0))
|
|
34509
|
+
return "known_vulnerable";
|
|
34510
|
+
}
|
|
34511
|
+
return "admitted";
|
|
34512
|
+
}
|
|
34513
|
+
function frameworkVersionMinimum(packageName, version2, policy = FRAMEWORK_VERSION_POLICY) {
|
|
34514
|
+
const entry = packagePolicy(packageName, policy);
|
|
34515
|
+
if (entry === null)
|
|
34516
|
+
throw new TypeError("Framework version package is not governed");
|
|
34517
|
+
const lowest = parseStableVersion(entry.floors[0]);
|
|
34518
|
+
if (lowest === null)
|
|
34519
|
+
throw new TypeError("Framework version policy is invalid");
|
|
34520
|
+
const core = typeof version2 === "string" ? /^[~^]?((?:0|[1-9][0-9]{0,5})\.(?:0|[1-9][0-9]{0,5})\.(?:0|[1-9][0-9]{0,5}))(?:[-+].*)?$/u.exec(version2) : null;
|
|
34521
|
+
const parsed = parseStableVersion(core?.[1]);
|
|
34522
|
+
let minimum = (parsed === null ? null : applicableFloor(entry, parsed)) ?? lowest;
|
|
34523
|
+
let candidate = parsed !== null && compareStableVersions(parsed, minimum) >= 0 ? parsed : minimum;
|
|
34524
|
+
for (let raised = true; raised; ) {
|
|
34525
|
+
raised = false;
|
|
34526
|
+
for (const [introduced, fixed] of entry.denied) {
|
|
34527
|
+
const from = parseStableVersion(introduced);
|
|
34528
|
+
const to = fixed === null ? null : parseStableVersion(fixed);
|
|
34529
|
+
if (from !== null && to !== null && compareStableVersions(candidate, from) >= 0 && compareStableVersions(candidate, to) < 0) {
|
|
34530
|
+
candidate = to;
|
|
34531
|
+
minimum = to;
|
|
34532
|
+
raised = true;
|
|
34533
|
+
}
|
|
34534
|
+
}
|
|
34535
|
+
}
|
|
34536
|
+
return minimum.join(".");
|
|
34537
|
+
}
|
|
34538
|
+
var TYPESCRIPT_BUILD_STAGE = /^tsc(?:[ \t]+(?:--noEmit|-b|--build))?$/u;
|
|
34539
|
+
function isAdmittedFrameworkBuildScript(framework, script) {
|
|
34540
|
+
if (typeof script !== "string")
|
|
34541
|
+
return false;
|
|
34542
|
+
if (framework === "nextjs")
|
|
34543
|
+
return script === "next build" || script === "next build --webpack";
|
|
34544
|
+
if (framework !== "vite" && framework !== "tanstack-start")
|
|
34545
|
+
return false;
|
|
34546
|
+
const stages = script.trim().split(/[ \t]*&&[ \t]*/u);
|
|
34547
|
+
if (stages.length === 1)
|
|
34548
|
+
return stages[0] === "vite build";
|
|
34549
|
+
if (stages.length !== 2)
|
|
34550
|
+
return false;
|
|
34551
|
+
return stages[0] === "vite build" && TYPESCRIPT_BUILD_STAGE.test(stages[1] ?? "") || TYPESCRIPT_BUILD_STAGE.test(stages[0] ?? "") && stages[1] === "vite build";
|
|
34552
|
+
}
|
|
34553
|
+
|
|
34554
|
+
// packages/contracts/dist/customer-auth-admission.js
|
|
34555
|
+
function managedCustomerAuthDatabaseMessage(path2) {
|
|
34556
|
+
return `"${path2}" selects the managed Better Auth bridge, which requires the managed PostgreSQL database. Set database.enabled: true with an admitted migrations path, or set auth.provider: none and keep the application-owned authentication configuration. Commit and push the change, then plan the new commit.`;
|
|
34557
|
+
}
|
|
34558
|
+
function managedCustomerAuthAdmissionIssue(capabilities) {
|
|
34559
|
+
if (capabilities?.auth?.provider !== "better-auth") {
|
|
34560
|
+
return null;
|
|
34561
|
+
}
|
|
34562
|
+
if (capabilities.database?.enabled !== true) {
|
|
34563
|
+
return Object.freeze({
|
|
34564
|
+
code: "managed_auth_database_required",
|
|
34565
|
+
message: managedCustomerAuthDatabaseMessage("ohmyhost.yaml")
|
|
34566
|
+
});
|
|
34567
|
+
}
|
|
34568
|
+
return null;
|
|
34569
|
+
}
|
|
34570
|
+
|
|
34571
|
+
// packages/contracts/dist/framework-admission.js
|
|
34572
|
+
var ADMISSION_DIAGNOSTIC_CODES = Object.freeze({
|
|
34573
|
+
ambiguous_config: "framework_config_ambiguous",
|
|
34574
|
+
ambiguous_lockfile: "lockfile_ambiguous",
|
|
34575
|
+
invalid_package_manager: "package_manager_invalid",
|
|
34576
|
+
missing_lockfile: "lockfile_missing",
|
|
34577
|
+
missing_package_manager: "package_manager_missing",
|
|
34578
|
+
package_manager_lockfile_mismatch: "package_manager_lockfile_mismatch",
|
|
34579
|
+
package_manager_version_unsupported: "package_manager_version_unsupported"
|
|
34580
|
+
});
|
|
34581
|
+
var FrameworkAdmissionError = class extends Error {
|
|
34582
|
+
reason;
|
|
34583
|
+
code = "framework_admission_failed";
|
|
34584
|
+
/** The catalog diagnostic of the refused file, relative to the application root. */
|
|
34585
|
+
diagnostic;
|
|
34586
|
+
constructor(reason, path2) {
|
|
34587
|
+
super("Framework source is not admitted");
|
|
34588
|
+
this.reason = reason;
|
|
34589
|
+
this.name = "FrameworkAdmissionError";
|
|
34590
|
+
this.diagnostic = requiredDiagnostic({ code: ADMISSION_DIAGNOSTIC_CODES[reason], path: path2 });
|
|
34591
|
+
}
|
|
34592
|
+
};
|
|
34593
|
+
var PACKAGE_MANAGER_CONTRACTS = Object.freeze({
|
|
34594
|
+
bun: Object.freeze({
|
|
34595
|
+
lockfiles: Object.freeze(["bun.lock", "bun.lockb"]),
|
|
34596
|
+
installCommand: "bun install --frozen-lockfile --ignore-scripts",
|
|
34597
|
+
buildCommand: "bun run build"
|
|
34598
|
+
}),
|
|
34599
|
+
npm: Object.freeze({
|
|
34600
|
+
lockfiles: Object.freeze(["package-lock.json"]),
|
|
34601
|
+
installCommand: "npm ci --ignore-scripts",
|
|
34602
|
+
buildCommand: "npm run build"
|
|
34603
|
+
}),
|
|
34604
|
+
pnpm: Object.freeze({
|
|
34605
|
+
lockfiles: Object.freeze(["pnpm-lock.yaml"]),
|
|
34606
|
+
installCommand: "pnpm install --frozen-lockfile --ignore-scripts",
|
|
34607
|
+
buildCommand: "pnpm run build"
|
|
34608
|
+
}),
|
|
34609
|
+
yarn: Object.freeze({
|
|
34610
|
+
lockfiles: Object.freeze(["yarn.lock"]),
|
|
34611
|
+
installCommand: "yarn install --immutable --mode=skip-build",
|
|
34612
|
+
buildCommand: "yarn run build"
|
|
34613
|
+
})
|
|
34614
|
+
});
|
|
34615
|
+
var ALL_LOCKFILES = Object.freeze(Object.values(PACKAGE_MANAGER_CONTRACTS).flatMap(({ lockfiles }) => lockfiles));
|
|
34616
|
+
var PACKAGE_MANIFEST = "package.json";
|
|
34617
|
+
var PACKAGE_MANAGER_DECLARATION = /^(bun|npm|pnpm|yarn)@((?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*))$/u;
|
|
34618
|
+
function resolvePackageManager(input) {
|
|
34619
|
+
const presentLockfiles = ALL_LOCKFILES.filter((lockfile2) => input.files.includes(lockfile2));
|
|
34620
|
+
if (presentLockfiles.length === 0) {
|
|
34621
|
+
throw new FrameworkAdmissionError("missing_lockfile", PACKAGE_MANIFEST);
|
|
34622
|
+
}
|
|
34623
|
+
if (presentLockfiles.length !== 1) {
|
|
34624
|
+
throw new FrameworkAdmissionError("ambiguous_lockfile", PACKAGE_MANIFEST);
|
|
34625
|
+
}
|
|
34626
|
+
if (input.packageManager === void 0) {
|
|
34627
|
+
throw new FrameworkAdmissionError("missing_package_manager", PACKAGE_MANIFEST);
|
|
34628
|
+
}
|
|
34629
|
+
const declaration = PACKAGE_MANAGER_DECLARATION.exec(input.packageManager);
|
|
34630
|
+
if (declaration === null) {
|
|
34631
|
+
throw new FrameworkAdmissionError("invalid_package_manager", PACKAGE_MANIFEST);
|
|
34632
|
+
}
|
|
34633
|
+
const manager = declaration[1];
|
|
34634
|
+
const version2 = declaration[2];
|
|
34635
|
+
const contract = PACKAGE_MANAGER_CONTRACTS[manager];
|
|
34636
|
+
const lockfile = presentLockfiles[0];
|
|
34637
|
+
if (!contract.lockfiles.includes(lockfile)) {
|
|
34638
|
+
throw new FrameworkAdmissionError("package_manager_lockfile_mismatch", lockfile);
|
|
34639
|
+
}
|
|
34640
|
+
if (manager === "bun" && version2 !== PINNED_BUN_VERSION) {
|
|
34641
|
+
throw new FrameworkAdmissionError("package_manager_version_unsupported", PACKAGE_MANIFEST);
|
|
34642
|
+
}
|
|
34643
|
+
return Object.freeze({
|
|
34644
|
+
manager,
|
|
34645
|
+
version: version2,
|
|
34646
|
+
lockfile,
|
|
34647
|
+
installCommand: contract.installCommand,
|
|
34648
|
+
buildCommand: contract.buildCommand
|
|
34649
|
+
});
|
|
34650
|
+
}
|
|
34651
|
+
function resolveFrameworkConfig(basename2, files) {
|
|
34652
|
+
const supported = [`${basename2}.js`, `${basename2}.mjs`, `${basename2}.ts`];
|
|
34653
|
+
const matches = supported.filter((candidate) => files.includes(candidate));
|
|
34654
|
+
if (matches.length > 1) {
|
|
34655
|
+
throw new FrameworkAdmissionError("ambiguous_config", matches[0]);
|
|
34656
|
+
}
|
|
34657
|
+
return matches[0] ?? null;
|
|
34658
|
+
}
|
|
34659
|
+
var STABLE_VERSION_SOURCE = "(?:0|[1-9][0-9]{0,5})\\.(?:0|[1-9][0-9]{0,5})\\.(?:0|[1-9][0-9]{0,5})";
|
|
34660
|
+
var VERSION_RANGE = new RegExp(`^([~^])(${STABLE_VERSION_SOURCE})$`, "u");
|
|
34661
|
+
function classifyFrameworkVersion(packageName, declared, resolved = null, policy = FRAMEWORK_VERSION_POLICY) {
|
|
34662
|
+
const judged = resolved ?? declared;
|
|
34663
|
+
const range = resolved === null ? VERSION_RANGE.exec(declared) : null;
|
|
34664
|
+
let status;
|
|
34665
|
+
if (range === null) {
|
|
34666
|
+
status = frameworkVersionVerdict(packageName, judged, policy) === "admitted" ? "admitted" : "unsupported";
|
|
34667
|
+
} else {
|
|
34668
|
+
status = rangeStatus(packageName, range[1] === "^", range[2], policy);
|
|
34669
|
+
}
|
|
34670
|
+
return Object.freeze({
|
|
34671
|
+
package: packageName,
|
|
34672
|
+
status,
|
|
34673
|
+
declared,
|
|
34674
|
+
resolved,
|
|
34675
|
+
minimum: frameworkVersionMinimum(packageName, judged, policy)
|
|
34676
|
+
});
|
|
34677
|
+
}
|
|
34678
|
+
function rangeStatus(packageName, caret, base, policy) {
|
|
34679
|
+
const lower = policyVersion(base);
|
|
34680
|
+
const upper = caret ? [lower[0] + 1, 0, 0] : [lower[0], lower[1] + 1, 0];
|
|
34681
|
+
if (frameworkVersionVerdict(packageName, base, policy) === "admitted" && !policy[packageName].denied.some(([introduced, fixed]) => {
|
|
34682
|
+
const from = parseStableVersion(introduced);
|
|
34683
|
+
const to = fixed === null ? null : parseStableVersion(fixed);
|
|
34684
|
+
return from !== null && compareStableVersions(from, upper) < 0 && (to === null || compareStableVersions(to, lower) > 0);
|
|
34685
|
+
}))
|
|
34686
|
+
return "admitted";
|
|
34687
|
+
const minimum = policyVersion(frameworkVersionMinimum(packageName, base, policy));
|
|
34688
|
+
const candidate = compareStableVersions(minimum, lower) > 0 ? minimum : lower;
|
|
34689
|
+
return compareStableVersions(candidate, upper) < 0 && frameworkVersionVerdict(packageName, candidate.join("."), policy) === "admitted" ? "pending" : "unsupported";
|
|
34690
|
+
}
|
|
34691
|
+
function policyVersion(version2) {
|
|
34692
|
+
const parsed = parseStableVersion(version2);
|
|
34693
|
+
if (parsed === null)
|
|
34694
|
+
throw new TypeError("Framework version policy is invalid");
|
|
34695
|
+
return parsed;
|
|
34696
|
+
}
|
|
34697
|
+
var RESOLVED_VERSION_SOURCE = `${STABLE_VERSION_SOURCE}(?:-[0-9A-Za-z.-]{1,32})?`;
|
|
34698
|
+
var RESOLVED_VERSION_FIELD = anchoredPattern(RESOLVED_VERSION_SOURCE);
|
|
34699
|
+
var DECLARED_VERSION_FIELD = anchoredPattern(`[~^]?${RESOLVED_VERSION_SOURCE}`);
|
|
34700
|
+
var STABLE_VERSION_FIELD = anchoredPattern(STABLE_VERSION_SOURCE);
|
|
34701
|
+
function isResolvedFrameworkVersion(value) {
|
|
34702
|
+
return typeof value === "string" && RESOLVED_VERSION_FIELD.test(value);
|
|
34703
|
+
}
|
|
34704
|
+
function frameworkVersionDiagnostic(admission, lockfile) {
|
|
34705
|
+
if (admission.status !== "unsupported")
|
|
34706
|
+
return null;
|
|
34707
|
+
if (admission.resolved !== null) {
|
|
34708
|
+
if (lockfile === null)
|
|
34709
|
+
throw new TypeError("Framework version lockfile is required");
|
|
34710
|
+
return requiredDiagnostic({
|
|
34711
|
+
code: "framework_version_unsupported",
|
|
34712
|
+
path: lockfile,
|
|
34713
|
+
package: admission.package,
|
|
34714
|
+
version: admission.resolved,
|
|
34715
|
+
minimum: admission.minimum
|
|
34716
|
+
});
|
|
34717
|
+
}
|
|
34718
|
+
return DECLARED_VERSION_FIELD.test(admission.declared) ? requiredDiagnostic({
|
|
34719
|
+
code: "framework_version_unsupported",
|
|
34720
|
+
path: PACKAGE_MANIFEST,
|
|
34721
|
+
package: admission.package,
|
|
34722
|
+
version: admission.declared,
|
|
34723
|
+
minimum: admission.minimum
|
|
34724
|
+
}) : requiredDiagnostic({
|
|
34725
|
+
code: "framework_version_declaration_unsupported",
|
|
34726
|
+
path: PACKAGE_MANIFEST,
|
|
34727
|
+
package: admission.package
|
|
34728
|
+
});
|
|
34729
|
+
}
|
|
34730
|
+
var FRAMEWORK_VERSION_PACKAGES = Object.freeze([
|
|
34731
|
+
"next",
|
|
34732
|
+
"vite",
|
|
34733
|
+
"@tanstack/react-start"
|
|
34734
|
+
]);
|
|
34735
|
+
var FRAMEWORK_DEPENDENCY_PACKAGES = Object.freeze([
|
|
34736
|
+
"next",
|
|
34737
|
+
"react",
|
|
34738
|
+
"react-dom",
|
|
34739
|
+
"vite",
|
|
34740
|
+
"@tanstack/react-start",
|
|
34741
|
+
"@tanstack/react-router",
|
|
34742
|
+
"@vitejs/plugin-react",
|
|
34743
|
+
"@cloudflare/vite-plugin"
|
|
34744
|
+
]);
|
|
34745
|
+
var REACT_PACKAGES = Object.freeze(["react", "react-dom"]);
|
|
34746
|
+
var BUILD_FRAMEWORKS = Object.freeze(["nextjs", "vite-static", "tanstack-start"]);
|
|
34747
|
+
var PUSH_AND_PLAN = "Commit and push the change, then plan the new commit.";
|
|
34748
|
+
var VITE_BUILD_SCRIPT = '"vite build", optionally joined with && to one "tsc", "tsc -b", "tsc --build" or "tsc --noEmit" stage';
|
|
34749
|
+
var FRAMEWORK_CONVERSION_DETAILS = Object.freeze({
|
|
34750
|
+
managed_auth_database_required: {
|
|
34751
|
+
paths: ["repository_config"],
|
|
34752
|
+
template: managedCustomerAuthDatabaseMessage("{{path}}")
|
|
34753
|
+
},
|
|
34754
|
+
database_binding_private: {
|
|
34755
|
+
paths: ["module"],
|
|
34756
|
+
template: `"{{path}}" reads a database connection string (DATABASE_URL, HYPERDRIVE, connectionString or a postgres:// URL); none reaches a customer Worker. Call the database with createPrivateDatabaseClient from "@ohmyhost/customer-runtime/database". ${PUSH_AND_PLAN}`
|
|
34757
|
+
},
|
|
34758
|
+
database_driver_unsupported: {
|
|
34759
|
+
paths: ["module"],
|
|
34760
|
+
template: `"{{path}}" imports a database socket driver (pg, postgres, pg-native or mysql2); customer Workers cannot open sockets. Call the database with createPrivateDatabaseClient from "@ohmyhost/customer-runtime/database". ${PUSH_AND_PLAN}`
|
|
34761
|
+
},
|
|
34762
|
+
source_syntax_invalid: {
|
|
34763
|
+
paths: ["module"],
|
|
34764
|
+
template: 'Invalid JavaScript or TypeScript in "{{path}}". Fix the file, commit and push the change, then plan the new commit.'
|
|
34765
|
+
},
|
|
34766
|
+
worker_module_default_export_required: {
|
|
34767
|
+
paths: ["module"],
|
|
34768
|
+
template: `Worker module "{{path}}" has no default export. Export default { fetch, scheduled } with the handlers ohmyho.st invokes; named exports are never called. ${PUSH_AND_PLAN}`
|
|
34769
|
+
},
|
|
34770
|
+
scheduled_handler_required: {
|
|
34771
|
+
paths: ["module"],
|
|
34772
|
+
template: 'functions.crons is declared but "{{path}}" exports no scheduled handler. Add scheduled(controller, env, ctx) to its default export, commit and push the change, then plan the new commit.'
|
|
34773
|
+
},
|
|
34774
|
+
framework_base_path_unsupported: {
|
|
34775
|
+
paths: ["framework_config"],
|
|
34776
|
+
template: `"{{path}}" sets a Vite base. ohmyho.st serves the app at the root origin: remove base or set base: "/". ${PUSH_AND_PLAN}`
|
|
34777
|
+
},
|
|
34778
|
+
tanstack_prerender_required: {
|
|
34779
|
+
paths: ["framework_config"],
|
|
34780
|
+
template: `"{{path}}" does not call tanstackStart({ prerender: { enabled: true } }), which the static TanStack Start runtime needs; enable prerender, or set runtime.mode: edge with the Cloudflare Vite plugin. ${PUSH_AND_PLAN}`
|
|
34781
|
+
},
|
|
34782
|
+
tanstack_vite_plugins_required: {
|
|
34783
|
+
paths: ["framework_config"],
|
|
34784
|
+
template: `"{{path}}" does not follow the TanStack Start plugin contract: import tanstackStart from "@tanstack/react-start/plugin/vite"; on runtime edge also import cloudflare from "@cloudflare/vite-plugin" and call cloudflare({ viteEnvironment: { name: "ssr" } }) before tanstackStart(); on runtime static do not import the Cloudflare plugin. ${PUSH_AND_PLAN}`
|
|
34785
|
+
},
|
|
34786
|
+
next_cache_components_unsupported: {
|
|
34787
|
+
paths: ["framework_config"],
|
|
34788
|
+
template: `"{{path}}" sets cacheComponents: true, which ohmyho.st does not support; remove it or set it to false. ${PUSH_AND_PLAN}`
|
|
34789
|
+
},
|
|
34790
|
+
framework_version_unsupported: {
|
|
34791
|
+
paths: ["manifest", "lockfile"],
|
|
34792
|
+
fields: {
|
|
34793
|
+
package: FRAMEWORK_VERSION_PACKAGES,
|
|
34794
|
+
version: DECLARED_VERSION_FIELD,
|
|
34795
|
+
minimum: STABLE_VERSION_FIELD
|
|
34796
|
+
},
|
|
34797
|
+
derived: { verb: { from: "path", text: { manifest: "declares", lockfile: "resolves" } } },
|
|
34798
|
+
template: '"{{path}}" {{verb}} {{package}} {{version}}, which ohmyho.st does not admit; use {{package}} {{minimum}} or a later stable release, update package.json and the lockfile, commit and push the change, then plan the new commit.'
|
|
34799
|
+
},
|
|
34800
|
+
framework_version_declaration_unsupported: {
|
|
34801
|
+
paths: ["manifest"],
|
|
34802
|
+
fields: { package: FRAMEWORK_VERSION_PACKAGES },
|
|
34803
|
+
template: '"{{path}}" declares {{package}} in a form ohmyho.st cannot check against its security minimum; declare an exact X.Y.Z version or a ^X.Y.Z or ~X.Y.Z range, update the lockfile, commit and push the change, then plan the new commit.'
|
|
34804
|
+
},
|
|
34805
|
+
framework_dependency_required: {
|
|
34806
|
+
paths: ["manifest"],
|
|
34807
|
+
fields: { package: FRAMEWORK_DEPENDENCY_PACKAGES },
|
|
34808
|
+
template: '"{{path}}" does not declare {{package}} in dependencies, which this framework and runtime need. Add it with the package manager so the lockfile records it, commit and push the change, then plan the new commit.'
|
|
34809
|
+
},
|
|
34810
|
+
react_version_exact_required: {
|
|
34811
|
+
paths: ["manifest"],
|
|
34812
|
+
fields: { package: REACT_PACKAGES },
|
|
34813
|
+
template: '"{{path}}" must declare {{package}} in dependencies at an exact version such as 19.1.0, without ^ or ~, for Next.js. Pin the version the lockfile installs, commit and push the change, then plan the new commit.'
|
|
34814
|
+
},
|
|
34815
|
+
build_script_unsupported: {
|
|
34816
|
+
paths: ["manifest"],
|
|
34817
|
+
fields: { framework: BUILD_FRAMEWORKS },
|
|
34818
|
+
derived: {
|
|
34819
|
+
script: {
|
|
34820
|
+
from: "framework",
|
|
34821
|
+
text: {
|
|
34822
|
+
nextjs: 'exactly "next build" or "next build --webpack"; ohmyho.st adds the OpenNext build itself',
|
|
34823
|
+
"vite-static": VITE_BUILD_SCRIPT,
|
|
34824
|
+
"tanstack-start": VITE_BUILD_SCRIPT
|
|
34825
|
+
}
|
|
34826
|
+
}
|
|
34827
|
+
},
|
|
34828
|
+
template: '"{{path}}" scripts.build is missing or not admitted for the {{framework}} adapter: it must be {{script}}. Move other steps into their own scripts, commit and push the change, then plan the new commit.'
|
|
34829
|
+
},
|
|
34830
|
+
better_auth_version_required: {
|
|
34831
|
+
paths: ["manifest"],
|
|
34832
|
+
template: '"{{path}}" must declare better-auth at exactly 1.7.1 in dependencies because ohmyhost.yaml sets auth.provider: better-auth. Pin that version or set auth.provider: none, commit and push the change, then plan the new commit.'
|
|
34833
|
+
},
|
|
34834
|
+
package_manifest_invalid: {
|
|
34835
|
+
paths: ["manifest"],
|
|
34836
|
+
template: '"{{path}}" is missing or is not one JSON object. Add package.json in the applicationRoot that ohmyhost.yaml names, or fix its syntax so the whole file parses as one object; commit and push the change, then plan the new commit.'
|
|
34837
|
+
},
|
|
34838
|
+
framework_unsupported: {
|
|
34839
|
+
paths: ["manifest"],
|
|
34840
|
+
template: `"{{path}}" declares none of next, vite or @tanstack/react-start. Add the app's framework to dependencies, or set runtime.mode: functions in ohmyhost.yaml and add src/ohmyhost/worker.ts. ${PUSH_AND_PLAN}`
|
|
34841
|
+
},
|
|
34842
|
+
framework_ambiguous: {
|
|
34843
|
+
paths: ["manifest"],
|
|
34844
|
+
template: '"{{path}}" declares next together with vite or @tanstack/react-start, so ohmyho.st cannot choose one framework. Remove the packages of the framework this application does not use, commit and push the change, then plan the new commit.'
|
|
34845
|
+
},
|
|
34846
|
+
lockfile_missing: {
|
|
34847
|
+
paths: ["manifest"],
|
|
34848
|
+
template: '"{{path}}" has no lockfile beside it. Install with the packageManager it names so package-lock.json, pnpm-lock.yaml, yarn.lock or bun.lock is written, commit and push the change, then plan the new commit.'
|
|
34849
|
+
},
|
|
34850
|
+
lockfile_ambiguous: {
|
|
34851
|
+
paths: ["manifest"],
|
|
34852
|
+
template: '"{{path}}" has more than one lockfile beside it. Keep only the lockfile of the packageManager it names and delete the others, commit and push the change, then plan the new commit.'
|
|
34853
|
+
},
|
|
34854
|
+
package_manager_missing: {
|
|
34855
|
+
paths: ["manifest"],
|
|
34856
|
+
template: '"{{path}}" has no packageManager field. Set it to name@X.Y.Z for the npm, pnpm, yarn or bun version that wrote the lockfile, commit and push the change, then plan the new commit.'
|
|
34857
|
+
},
|
|
34858
|
+
package_manager_invalid: {
|
|
34859
|
+
paths: ["manifest"],
|
|
34860
|
+
template: '"{{path}}" sets packageManager to a value ohmyho.st cannot use. Write npm, pnpm, yarn or bun, then @ and an exact X.Y.Z version without a +sha suffix, matching the lockfile; commit and push the change, then plan the new commit.'
|
|
34861
|
+
},
|
|
34862
|
+
package_manager_lockfile_mismatch: {
|
|
34863
|
+
paths: ["lockfile"],
|
|
34864
|
+
template: '"{{path}}" was not written by the packageManager that package.json names. Set packageManager to the manager of this lockfile, or replace the lockfile with one from the named manager; commit and push the change, then plan the new commit.'
|
|
34865
|
+
},
|
|
34866
|
+
package_manager_version_unsupported: {
|
|
34867
|
+
paths: ["manifest"],
|
|
34868
|
+
template: `"{{path}}" pins a Bun version other than ${PINNED_BUN_VERSION}, the only Bun ohmyho.st builds with. Set packageManager to bun@${PINNED_BUN_VERSION}, reinstall to update the lockfile, commit and push the change, then plan the new commit.`
|
|
34869
|
+
},
|
|
34870
|
+
repository_configuration_invalid: {
|
|
34871
|
+
paths: ["repository_config"],
|
|
34872
|
+
template: '"{{path}}" does not match the ohmyho.st configuration schema. Run ohmyhost init --dry-run --json locally to see the exact field and fix, commit and push the change, then plan the new commit.'
|
|
34873
|
+
},
|
|
34874
|
+
runtime_mode_unsupported: {
|
|
34875
|
+
paths: ["repository_config"],
|
|
34876
|
+
fields: { framework: BUILD_FRAMEWORKS },
|
|
34877
|
+
derived: {
|
|
34878
|
+
mode: {
|
|
34879
|
+
from: "framework",
|
|
34880
|
+
text: {
|
|
34881
|
+
nextjs: "runtime.mode: edge",
|
|
34882
|
+
"vite-static": "runtime.mode: edge with src/ohmyhost/companion.ts when database, auth, mail, storage or functions.crons is set, and static otherwise",
|
|
34883
|
+
"tanstack-start": "runtime.mode: edge, or static without functions.crons and storage"
|
|
34884
|
+
}
|
|
34885
|
+
}
|
|
34886
|
+
},
|
|
34887
|
+
template: `"{{path}}" sets a runtime.mode that the {{framework}} adapter cannot run with this configuration; use {{mode}}. ${PUSH_AND_PLAN}`
|
|
34888
|
+
},
|
|
34889
|
+
build_output_unsupported: {
|
|
34890
|
+
paths: ["repository_config"],
|
|
34891
|
+
fields: { framework: BUILD_FRAMEWORKS },
|
|
34892
|
+
derived: {
|
|
34893
|
+
output: {
|
|
34894
|
+
from: "framework",
|
|
34895
|
+
text: {
|
|
34896
|
+
nextjs: ".open-next/assets",
|
|
34897
|
+
"vite-static": "dist",
|
|
34898
|
+
"tanstack-start": "dist/client"
|
|
34899
|
+
}
|
|
34900
|
+
}
|
|
34901
|
+
},
|
|
34902
|
+
template: `"{{path}}" sets a build.output that the {{framework}} adapter does not produce; set build.output: {{output}}. ${PUSH_AND_PLAN}`
|
|
34903
|
+
},
|
|
34904
|
+
build_command_mismatch: {
|
|
34905
|
+
paths: ["repository_config"],
|
|
34906
|
+
template: '"{{path}}" build.install or build.command differs from the commands of the packageManager in package.json (for npm: npm ci --ignore-scripts and npm run build). ohmyhost init --dry-run --json prints the exact values; set them, commit and push the change, then plan the new commit.'
|
|
34907
|
+
},
|
|
34908
|
+
build_option_unsupported: {
|
|
34909
|
+
paths: ["repository_config"],
|
|
34910
|
+
template: '"{{path}}" sets a build option this runtime ignores: build.ssg_cache_max_mib applies only to Next.js, and runtime.mode: functions takes only build.install. Remove the option, commit and push the change, then plan the new commit.'
|
|
34911
|
+
},
|
|
34912
|
+
application_root_missing: {
|
|
34913
|
+
paths: ["repository_config"],
|
|
34914
|
+
template: `"{{path}}" sets applicationRoot to a directory that is missing or empty in this commit. Set it to the directory that holds the app's package.json, or "." for the repository root; commit and push the change, then plan the new commit.`
|
|
34915
|
+
},
|
|
34916
|
+
framework_config_required: {
|
|
34917
|
+
paths: ["framework_config"],
|
|
34918
|
+
template: 'This application has no "{{path}}". Add one framework config with that name and a .js, .mjs or .ts extension that exports its config as default, commit and push the change, then plan the new commit.'
|
|
34919
|
+
},
|
|
34920
|
+
framework_config_ambiguous: {
|
|
34921
|
+
paths: ["framework_config"],
|
|
34922
|
+
template: '"{{path}}" exists beside another variant with the same name and a .js, .mjs or .ts extension. Keep exactly one and delete the others, commit and push the change, then plan the new commit.'
|
|
34923
|
+
},
|
|
34924
|
+
framework_config_too_large: {
|
|
34925
|
+
paths: ["framework_config"],
|
|
34926
|
+
template: '"{{path}}" is larger than the 256 KiB ohmyho.st inspects. Keep the config small and move large data into imported modules, commit and push the change, then plan the new commit.'
|
|
34927
|
+
},
|
|
34928
|
+
framework_config_default_export_required: {
|
|
34929
|
+
paths: ["framework_config"],
|
|
34930
|
+
template: '"{{path}}" does not export its config as default. Use export default (next.config.js may assign module.exports instead), commit and push the change, then plan the new commit.'
|
|
34931
|
+
},
|
|
34932
|
+
next_output_unsupported: {
|
|
34933
|
+
paths: ["framework_config"],
|
|
34934
|
+
template: '"{{path}}" sets output: "export" or output: "standalone", which the Next.js edge runtime on ohmyho.st cannot serve. Remove the output setting, commit and push the change, then plan the new commit.'
|
|
34935
|
+
},
|
|
34936
|
+
next_base_path_unsupported: {
|
|
34937
|
+
paths: ["framework_config"],
|
|
34938
|
+
template: `"{{path}}" sets basePath or assetPrefix. ohmyho.st serves the app at the root origin: remove both or set them to "". ${PUSH_AND_PLAN}`
|
|
34939
|
+
},
|
|
34940
|
+
companion_entry_required: {
|
|
34941
|
+
paths: ["module"],
|
|
34942
|
+
template: '"{{path}}" is missing. A Vite app with runtime.mode: edge or with database, auth, mail, storage or functions.crons needs this Worker module with export default { fetch }. Add it, or use runtime.mode: static without those settings; commit and push the change, then plan the new commit.'
|
|
34943
|
+
},
|
|
34944
|
+
worker_module_required: {
|
|
34945
|
+
paths: ["module"],
|
|
34946
|
+
template: '"{{path}}" is missing. runtime.mode: functions and functions.crons on Next.js or TanStack Start need this Worker module; export default { fetch, scheduled } with the handlers ohmyho.st invokes. Add it, commit and push the change, then plan the new commit.'
|
|
34947
|
+
},
|
|
34948
|
+
worker_module_fetch_handler_required: {
|
|
34949
|
+
paths: ["module"],
|
|
34950
|
+
template: `Worker module "{{path}}" exports no fetch handler; runtime.mode: functions serves every request and the health check through it. Add a fetch handler that receives request, env and ctx to its default export. ${PUSH_AND_PLAN}`
|
|
34951
|
+
}
|
|
34952
|
+
});
|
|
34953
|
+
var DETAIL_DEFINITIONS = FRAMEWORK_CONVERSION_DETAILS;
|
|
34954
|
+
var PLACEHOLDER = /\{\{([a-z]+)\}\}/gu;
|
|
34955
|
+
var PATH_PATTERN = "[A-Za-z0-9_@()[\\]./${}-]{1,240}";
|
|
34956
|
+
var SAFE_PATH = new RegExp(`^${PATH_PATTERN}$`, "u");
|
|
34957
|
+
var MODULE_PATH = /^[A-Za-z0-9_@()[\]./${}-]+\.(?:[cm]?[jt]sx?)$/u;
|
|
34958
|
+
var FRAMEWORK_CONFIG_NAME = /^(?:next|open-next|vite)\.config\.(?:js|mjs|ts)$/u;
|
|
34959
|
+
var MAXIMUM_DETAIL_LENGTH = 2e3;
|
|
34960
|
+
var DETAIL_PATTERNS = Object.freeze(Object.entries(DETAIL_DEFINITIONS).map(([code, definition]) => Object.freeze({
|
|
34961
|
+
code,
|
|
34962
|
+
pattern: detailPattern(definition)
|
|
34963
|
+
})));
|
|
34964
|
+
function anchoredPattern(source) {
|
|
34965
|
+
return new RegExp(`^(?:${source})$`, "u");
|
|
34966
|
+
}
|
|
34967
|
+
function escapePattern(text) {
|
|
34968
|
+
return text.replace(/[\\^$.*+?()[\]{}|]/gu, "\\$&");
|
|
34969
|
+
}
|
|
34970
|
+
function detailPattern(definition) {
|
|
34971
|
+
const placed = /* @__PURE__ */ new Set();
|
|
34972
|
+
let pattern = "^";
|
|
34973
|
+
let offset = 0;
|
|
34974
|
+
for (const match of definition.template.matchAll(PLACEHOLDER)) {
|
|
34975
|
+
const name = match[1];
|
|
34976
|
+
pattern += escapePattern(definition.template.slice(offset, match.index));
|
|
34977
|
+
pattern += placed.has(name) ? `\\k<${name}>` : `(?<${name}>${placeholderPattern(definition, name)})`;
|
|
34978
|
+
placed.add(name);
|
|
34979
|
+
offset = match.index + match[0].length;
|
|
34980
|
+
}
|
|
34981
|
+
return new RegExp(`${pattern}${escapePattern(definition.template.slice(offset))}$`, "u");
|
|
34982
|
+
}
|
|
34983
|
+
function placeholderPattern(definition, name) {
|
|
34984
|
+
if (name === "path")
|
|
34985
|
+
return PATH_PATTERN;
|
|
34986
|
+
const grammar = definition.fields?.[name];
|
|
34987
|
+
if (grammar instanceof RegExp)
|
|
34988
|
+
return grammar.source.slice(1, -1);
|
|
34989
|
+
const values = grammar ?? Object.values(definition.derived?.[name]?.text ?? {});
|
|
34990
|
+
if (values.length === 0)
|
|
34991
|
+
throw new TypeError("Framework conversion detail template is invalid");
|
|
34992
|
+
return `(?:${[...new Set(values)].map(escapePattern).join("|")})`;
|
|
34993
|
+
}
|
|
34994
|
+
function pathClassOf(definition, path2) {
|
|
34995
|
+
if (path2 === "ohmyhost.yaml" && definition.paths.includes("repository_config"))
|
|
34996
|
+
return "repository_config";
|
|
34997
|
+
if (!SAFE_PATH.test(path2) || path2.split("/").some((segment) => segment === "" || segment === "." || segment === ".."))
|
|
34998
|
+
return null;
|
|
34999
|
+
const leaf = path2.slice(path2.lastIndexOf("/") + 1);
|
|
35000
|
+
return definition.paths.find((pathClass) => pathClass === "module" && MODULE_PATH.test(path2) || pathClass === "manifest" && leaf === PACKAGE_MANIFEST || pathClass === "lockfile" && ALL_LOCKFILES.includes(leaf) || pathClass === "framework_config" && FRAMEWORK_CONFIG_NAME.test(leaf)) ?? null;
|
|
35001
|
+
}
|
|
35002
|
+
function fieldMatches(grammar, value) {
|
|
35003
|
+
if (typeof value !== "string")
|
|
35004
|
+
return false;
|
|
35005
|
+
return grammar instanceof RegExp ? grammar.test(value) : grammar.includes(value);
|
|
35006
|
+
}
|
|
35007
|
+
function isFrameworkConversionDiagnosticCode(value) {
|
|
35008
|
+
return typeof value === "string" && Object.hasOwn(DETAIL_DEFINITIONS, value);
|
|
35009
|
+
}
|
|
35010
|
+
function parseFrameworkConversionDiagnostic(value) {
|
|
35011
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
35012
|
+
return null;
|
|
35013
|
+
const record6 = value;
|
|
35014
|
+
const code = Object.hasOwn(record6, "code") ? record6["code"] : void 0;
|
|
35015
|
+
const path2 = Object.hasOwn(record6, "path") ? record6["path"] : void 0;
|
|
35016
|
+
if (!isFrameworkConversionDiagnosticCode(code) || typeof path2 !== "string")
|
|
35017
|
+
return null;
|
|
35018
|
+
const definition = DETAIL_DEFINITIONS[code];
|
|
35019
|
+
const fields = Object.entries(definition.fields ?? {}).map(([name, grammar]) => [name, grammar, Object.hasOwn(record6, name) ? record6[name] : void 0]);
|
|
35020
|
+
if (Object.keys(record6).length !== fields.length + 2 || pathClassOf(definition, path2) === null || fields.some(([, grammar, field]) => !fieldMatches(grammar, field)))
|
|
35021
|
+
return null;
|
|
35022
|
+
return Object.freeze(Object.fromEntries([
|
|
35023
|
+
["code", code],
|
|
35024
|
+
["path", path2],
|
|
35025
|
+
...fields.map(([name, , field]) => [name, field])
|
|
35026
|
+
]));
|
|
35027
|
+
}
|
|
35028
|
+
function requiredDiagnostic(value) {
|
|
35029
|
+
const diagnostic2 = parseFrameworkConversionDiagnostic(value);
|
|
35030
|
+
if (diagnostic2 === null)
|
|
35031
|
+
throw new TypeError("Framework conversion diagnostic is invalid");
|
|
35032
|
+
return diagnostic2;
|
|
35033
|
+
}
|
|
35034
|
+
function renderDetail(diagnostic2) {
|
|
35035
|
+
const definition = DETAIL_DEFINITIONS[diagnostic2.code];
|
|
35036
|
+
const values = diagnostic2;
|
|
35037
|
+
const pathClass = pathClassOf(definition, diagnostic2.path);
|
|
35038
|
+
return definition.template.replace(PLACEHOLDER, (_placeholder, name) => {
|
|
35039
|
+
const derived = definition.derived?.[name];
|
|
35040
|
+
const value = derived === void 0 ? values[name] : derived.text[String(derived.from === "path" ? pathClass : values[derived.from])];
|
|
35041
|
+
if (typeof value !== "string")
|
|
35042
|
+
throw new TypeError("Framework conversion diagnostic is invalid");
|
|
35043
|
+
return value;
|
|
35044
|
+
});
|
|
35045
|
+
}
|
|
35046
|
+
function frameworkConversionDetail(value) {
|
|
35047
|
+
const diagnostic2 = parseFrameworkConversionDiagnostic(value);
|
|
35048
|
+
return diagnostic2 === null ? null : renderDetail(diagnostic2);
|
|
35049
|
+
}
|
|
35050
|
+
function frameworkConversionDiagnosticFromDetail(value) {
|
|
35051
|
+
if (typeof value !== "string" || value.length > MAXIMUM_DETAIL_LENGTH)
|
|
35052
|
+
return null;
|
|
35053
|
+
for (const { code, pattern } of DETAIL_PATTERNS) {
|
|
35054
|
+
const groups = pattern.exec(value)?.groups;
|
|
35055
|
+
if (groups === void 0)
|
|
35056
|
+
continue;
|
|
35057
|
+
const diagnostic2 = parseFrameworkConversionDiagnostic({
|
|
35058
|
+
code,
|
|
35059
|
+
path: groups["path"],
|
|
35060
|
+
...Object.fromEntries(Object.keys(DETAIL_DEFINITIONS[code].fields ?? {}).map((name) => [name, groups[name]]))
|
|
35061
|
+
});
|
|
35062
|
+
if (diagnostic2 !== null && renderDetail(diagnostic2) === value)
|
|
35063
|
+
return diagnostic2;
|
|
35064
|
+
}
|
|
35065
|
+
return null;
|
|
35066
|
+
}
|
|
35067
|
+
function parseFrameworkConversionDetail(value) {
|
|
35068
|
+
const diagnostic2 = frameworkConversionDiagnosticFromDetail(value);
|
|
35069
|
+
return diagnostic2 === null ? null : renderDetail(diagnostic2);
|
|
35070
|
+
}
|
|
35071
|
+
function rootFrameworkConversionDiagnostic(value, applicationRoot) {
|
|
35072
|
+
const diagnostic2 = parseFrameworkConversionDiagnostic(value);
|
|
35073
|
+
if (diagnostic2 === null || applicationRoot === "." || DETAIL_DEFINITIONS[diagnostic2.code].paths.includes("repository_config"))
|
|
35074
|
+
return diagnostic2;
|
|
35075
|
+
return parseFrameworkConversionDiagnostic({
|
|
35076
|
+
...diagnostic2,
|
|
35077
|
+
path: `${applicationRoot}/${diagnostic2.path}`
|
|
35078
|
+
});
|
|
35079
|
+
}
|
|
35080
|
+
|
|
34462
35081
|
// packages/contracts/dist/operation-failure.js
|
|
34463
35082
|
var failures = {
|
|
34464
35083
|
authorization_changed: [
|
|
@@ -34575,7 +35194,7 @@ var failures = {
|
|
|
34575
35194
|
],
|
|
34576
35195
|
auto_deploy_plan_rejected: [
|
|
34577
35196
|
"The GitHub push could not be planned for deployment.",
|
|
34578
|
-
"Read
|
|
35197
|
+
"Read error.detail: it names the file, the rule and the required change. Make that change, commit and push it; the next push is planned again. Without error.detail, plan the pushed commit explicitly with 'ohmyhost plan --project ULID --commit SHA --json' or MCP deployment_plan to read the reason. Replaying this failed operation cannot start a build."
|
|
34579
35198
|
],
|
|
34580
35199
|
promotion_source_stale: [
|
|
34581
35200
|
"The Dev deployment changed before promotion could start.",
|
|
@@ -34596,11 +35215,18 @@ function publicOperationReconciliation(observation) {
|
|
|
34596
35215
|
suggested_action: observation.state === "required" ? "Confirm reconciliation of this same operation; preserve its existing build and resources. Do not submit another deployment." : "Reconciliation is running. Poll this same operation after 60 seconds; do not start another reconciliation or deployment."
|
|
34597
35216
|
});
|
|
34598
35217
|
}
|
|
34599
|
-
function publicOperationFailure(internalCode) {
|
|
35218
|
+
function publicOperationFailure(internalCode, diagnostic2) {
|
|
34600
35219
|
const normalized = internalCode.toLowerCase();
|
|
34601
35220
|
const code = Object.hasOwn(failures, normalized) ? normalized : "operation_failed";
|
|
34602
35221
|
const [message, suggested_action] = failures[code];
|
|
34603
|
-
|
|
35222
|
+
const detail = code === "auto_deploy_plan_rejected" ? frameworkConversionDetail(diagnostic2) : null;
|
|
35223
|
+
return Object.freeze({
|
|
35224
|
+
code,
|
|
35225
|
+
message,
|
|
35226
|
+
retryable: false,
|
|
35227
|
+
suggested_action,
|
|
35228
|
+
...detail === null ? {} : { detail }
|
|
35229
|
+
});
|
|
34604
35230
|
}
|
|
34605
35231
|
|
|
34606
35232
|
// packages/contracts/dist/managed-sources.js
|
|
@@ -35587,242 +36213,6 @@ function invalid2() {
|
|
|
35587
36213
|
throw new TypeError("Project data change plan is invalid");
|
|
35588
36214
|
}
|
|
35589
36215
|
|
|
35590
|
-
// packages/contracts/dist/framework-build-script.mjs
|
|
35591
|
-
var PINNED_BUN_VERSION = "1.2.22";
|
|
35592
|
-
function isAdmittedNextVersion(version2) {
|
|
35593
|
-
if (typeof version2 !== "string")
|
|
35594
|
-
return false;
|
|
35595
|
-
const match = /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/u.exec(version2);
|
|
35596
|
-
if (match === null)
|
|
35597
|
-
return false;
|
|
35598
|
-
const major = Number(match[1]);
|
|
35599
|
-
const minor = Number(match[2]);
|
|
35600
|
-
const patch = Number(match[3]);
|
|
35601
|
-
if (![major, minor, patch].every(Number.isSafeInteger))
|
|
35602
|
-
return false;
|
|
35603
|
-
return major === 15 && minor === 5 && patch >= 26 || major === 16 && minor === 3 && patch === 8;
|
|
35604
|
-
}
|
|
35605
|
-
var TYPESCRIPT_BUILD_STAGE = /^tsc(?:[ \t]+(?:--noEmit|-b|--build))?$/u;
|
|
35606
|
-
function isAdmittedFrameworkBuildScript(framework, script) {
|
|
35607
|
-
if (typeof script !== "string")
|
|
35608
|
-
return false;
|
|
35609
|
-
if (framework === "nextjs")
|
|
35610
|
-
return script === "next build" || script === "next build --webpack";
|
|
35611
|
-
if (framework !== "vite" && framework !== "tanstack-start")
|
|
35612
|
-
return false;
|
|
35613
|
-
const stages = script.trim().split(/[ \t]*&&[ \t]*/u);
|
|
35614
|
-
if (stages.length === 1)
|
|
35615
|
-
return stages[0] === "vite build";
|
|
35616
|
-
if (stages.length !== 2)
|
|
35617
|
-
return false;
|
|
35618
|
-
return stages[0] === "vite build" && TYPESCRIPT_BUILD_STAGE.test(stages[1] ?? "") || TYPESCRIPT_BUILD_STAGE.test(stages[0] ?? "") && stages[1] === "vite build";
|
|
35619
|
-
}
|
|
35620
|
-
|
|
35621
|
-
// packages/contracts/dist/customer-auth-admission.js
|
|
35622
|
-
function managedCustomerAuthDatabaseMessage(path2) {
|
|
35623
|
-
return `"${path2}" selects the managed Better Auth bridge, which requires the managed PostgreSQL database. Set database.enabled: true with an admitted migrations path, or set auth.provider: none and keep the application-owned authentication configuration. Commit and push the change, then plan the new commit.`;
|
|
35624
|
-
}
|
|
35625
|
-
function managedCustomerAuthAdmissionIssue(capabilities) {
|
|
35626
|
-
if (capabilities?.auth?.provider !== "better-auth") {
|
|
35627
|
-
return null;
|
|
35628
|
-
}
|
|
35629
|
-
if (capabilities.database?.enabled !== true) {
|
|
35630
|
-
return Object.freeze({
|
|
35631
|
-
code: "managed_auth_database_required",
|
|
35632
|
-
message: managedCustomerAuthDatabaseMessage("ohmyhost.yaml")
|
|
35633
|
-
});
|
|
35634
|
-
}
|
|
35635
|
-
return null;
|
|
35636
|
-
}
|
|
35637
|
-
|
|
35638
|
-
// packages/contracts/dist/framework-admission.js
|
|
35639
|
-
var FrameworkAdmissionError = class extends Error {
|
|
35640
|
-
reason;
|
|
35641
|
-
code = "framework_admission_failed";
|
|
35642
|
-
constructor(reason) {
|
|
35643
|
-
super("Framework source is not admitted");
|
|
35644
|
-
this.reason = reason;
|
|
35645
|
-
this.name = "FrameworkAdmissionError";
|
|
35646
|
-
}
|
|
35647
|
-
};
|
|
35648
|
-
var PACKAGE_MANAGER_CONTRACTS = Object.freeze({
|
|
35649
|
-
bun: Object.freeze({
|
|
35650
|
-
lockfiles: Object.freeze(["bun.lock", "bun.lockb"]),
|
|
35651
|
-
installCommand: "bun install --frozen-lockfile --ignore-scripts",
|
|
35652
|
-
buildCommand: "bun run build"
|
|
35653
|
-
}),
|
|
35654
|
-
npm: Object.freeze({
|
|
35655
|
-
lockfiles: Object.freeze(["package-lock.json"]),
|
|
35656
|
-
installCommand: "npm ci --ignore-scripts",
|
|
35657
|
-
buildCommand: "npm run build"
|
|
35658
|
-
}),
|
|
35659
|
-
pnpm: Object.freeze({
|
|
35660
|
-
lockfiles: Object.freeze(["pnpm-lock.yaml"]),
|
|
35661
|
-
installCommand: "pnpm install --frozen-lockfile --ignore-scripts",
|
|
35662
|
-
buildCommand: "pnpm run build"
|
|
35663
|
-
}),
|
|
35664
|
-
yarn: Object.freeze({
|
|
35665
|
-
lockfiles: Object.freeze(["yarn.lock"]),
|
|
35666
|
-
installCommand: "yarn install --immutable --mode=skip-build",
|
|
35667
|
-
buildCommand: "yarn run build"
|
|
35668
|
-
})
|
|
35669
|
-
});
|
|
35670
|
-
var ALL_LOCKFILES = Object.freeze(Object.values(PACKAGE_MANAGER_CONTRACTS).flatMap(({ lockfiles }) => lockfiles));
|
|
35671
|
-
var EXACT_VERSION = /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/u;
|
|
35672
|
-
var PACKAGE_MANAGER_DECLARATION = /^(bun|npm|pnpm|yarn)@((?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*))$/u;
|
|
35673
|
-
function resolvePackageManager(input) {
|
|
35674
|
-
const presentLockfiles = ALL_LOCKFILES.filter((lockfile2) => input.files.includes(lockfile2));
|
|
35675
|
-
if (presentLockfiles.length === 0)
|
|
35676
|
-
throw new FrameworkAdmissionError("missing_lockfile");
|
|
35677
|
-
if (presentLockfiles.length !== 1)
|
|
35678
|
-
throw new FrameworkAdmissionError("ambiguous_lockfile");
|
|
35679
|
-
if (input.packageManager === void 0) {
|
|
35680
|
-
throw new FrameworkAdmissionError("missing_package_manager");
|
|
35681
|
-
}
|
|
35682
|
-
const declaration = PACKAGE_MANAGER_DECLARATION.exec(input.packageManager);
|
|
35683
|
-
if (declaration === null)
|
|
35684
|
-
throw new FrameworkAdmissionError("invalid_package_manager");
|
|
35685
|
-
const manager = declaration[1];
|
|
35686
|
-
const version2 = declaration[2];
|
|
35687
|
-
const contract = PACKAGE_MANAGER_CONTRACTS[manager];
|
|
35688
|
-
const lockfile = presentLockfiles[0];
|
|
35689
|
-
if (!contract.lockfiles.includes(lockfile)) {
|
|
35690
|
-
throw new FrameworkAdmissionError("package_manager_lockfile_mismatch");
|
|
35691
|
-
}
|
|
35692
|
-
if (manager === "bun" && version2 !== PINNED_BUN_VERSION) {
|
|
35693
|
-
throw new FrameworkAdmissionError("package_manager_version_unsupported");
|
|
35694
|
-
}
|
|
35695
|
-
return Object.freeze({
|
|
35696
|
-
manager,
|
|
35697
|
-
version: version2,
|
|
35698
|
-
lockfile,
|
|
35699
|
-
installCommand: contract.installCommand,
|
|
35700
|
-
buildCommand: contract.buildCommand
|
|
35701
|
-
});
|
|
35702
|
-
}
|
|
35703
|
-
function resolveFrameworkConfig(basename2, files) {
|
|
35704
|
-
const supported = [`${basename2}.js`, `${basename2}.mjs`, `${basename2}.ts`];
|
|
35705
|
-
const matches = supported.filter((candidate) => files.includes(candidate));
|
|
35706
|
-
if (matches.length > 1)
|
|
35707
|
-
throw new FrameworkAdmissionError("ambiguous_config");
|
|
35708
|
-
return matches[0] ?? null;
|
|
35709
|
-
}
|
|
35710
|
-
var VERIFIED_VERSIONS = Object.freeze({
|
|
35711
|
-
vite: /* @__PURE__ */ new Set(["5.4.21", "8.0.16", "8.2.2"]),
|
|
35712
|
-
"tanstack-start": /* @__PURE__ */ new Set(["1.168.26", "1.168.49"]),
|
|
35713
|
-
nextjs: /* @__PURE__ */ new Set(["15.5.26", "16.3.8"])
|
|
35714
|
-
});
|
|
35715
|
-
function classifyFrameworkVersion(framework, version2) {
|
|
35716
|
-
const parsed = parseVersionSpec(version2);
|
|
35717
|
-
if (parsed === null)
|
|
35718
|
-
return versionAdmission("unsupported", "invalid_version");
|
|
35719
|
-
if (!insideAdmittedWindow(framework, parsed.version)) {
|
|
35720
|
-
return versionAdmission("unsupported", "outside_admitted_window");
|
|
35721
|
-
}
|
|
35722
|
-
if (!parsed.exact) {
|
|
35723
|
-
return versionAdmission("experimental", "range_inside_admitted_window");
|
|
35724
|
-
}
|
|
35725
|
-
return VERIFIED_VERSIONS[framework].has(version2) ? versionAdmission("verified", "verified_fixture") : versionAdmission("experimental", "inside_admitted_window");
|
|
35726
|
-
}
|
|
35727
|
-
function parseVersionSpec(version2) {
|
|
35728
|
-
const exact5 = EXACT_VERSION.exec(version2);
|
|
35729
|
-
const match = exact5 ?? (/^[~^](.*)$/u.exec(version2)?.[1] ? EXACT_VERSION.exec(version2.slice(1)) : null);
|
|
35730
|
-
if (match === null)
|
|
35731
|
-
return null;
|
|
35732
|
-
const tuple = [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
35733
|
-
return tuple.every(Number.isSafeInteger) ? Object.freeze({ exact: exact5 !== null, version: tuple }) : null;
|
|
35734
|
-
}
|
|
35735
|
-
function insideAdmittedWindow(framework, version2) {
|
|
35736
|
-
switch (framework) {
|
|
35737
|
-
case "vite":
|
|
35738
|
-
return compareVersions(version2, [5, 4, 0]) >= 0 && compareVersions(version2, [8, 2, 2]) <= 0;
|
|
35739
|
-
case "tanstack-start":
|
|
35740
|
-
return compareVersions(version2, [1, 168, 26]) >= 0 && compareVersions(version2, [1, 168, 49]) <= 0;
|
|
35741
|
-
case "nextjs":
|
|
35742
|
-
return isAdmittedNextVersion(version2.join("."));
|
|
35743
|
-
}
|
|
35744
|
-
}
|
|
35745
|
-
function compareVersions(left, right) {
|
|
35746
|
-
const majorDifference = left[0] - right[0];
|
|
35747
|
-
if (majorDifference !== 0)
|
|
35748
|
-
return majorDifference;
|
|
35749
|
-
const minorDifference = left[1] - right[1];
|
|
35750
|
-
return minorDifference !== 0 ? minorDifference : left[2] - right[2];
|
|
35751
|
-
}
|
|
35752
|
-
function versionAdmission(classification, reason) {
|
|
35753
|
-
return Object.freeze({ classification, reason });
|
|
35754
|
-
}
|
|
35755
|
-
var PUSH_AND_PLAN = "Commit and push the change, then plan the new commit.";
|
|
35756
|
-
var FRAMEWORK_CONVERSION_DETAILS = Object.freeze({
|
|
35757
|
-
managed_auth_database_required: Object.freeze({
|
|
35758
|
-
render: managedCustomerAuthDatabaseMessage,
|
|
35759
|
-
pattern: /^"([^"]+)" selects the managed Better Auth bridge, which requires the managed PostgreSQL database\. Set database\.enabled: true with an admitted migrations path, or set auth\.provider: none and keep the application-owned authentication configuration\. Commit and push the change, then plan the new commit\.$/u
|
|
35760
|
-
}),
|
|
35761
|
-
database_binding_private: Object.freeze({
|
|
35762
|
-
render: (path2) => `"${path2}" reads a database connection string (DATABASE_URL, HYPERDRIVE, connectionString or a postgres:// URL); none reaches a customer Worker. Call the database with createPrivateDatabaseClient from "@ohmyhost/customer-runtime/database". ${PUSH_AND_PLAN}`,
|
|
35763
|
-
pattern: /^"([^"]+)" reads a database connection string \(DATABASE_URL, HYPERDRIVE, connectionString or a postgres:\/\/ URL\); none reaches a customer Worker\. Call the database with createPrivateDatabaseClient from "@ohmyhost\/customer-runtime\/database"\. Commit and push the change, then plan the new commit\.$/u
|
|
35764
|
-
}),
|
|
35765
|
-
database_driver_unsupported: Object.freeze({
|
|
35766
|
-
render: (path2) => `"${path2}" imports a database socket driver (pg, postgres, pg-native or mysql2); customer Workers cannot open sockets. Call the database with createPrivateDatabaseClient from "@ohmyhost/customer-runtime/database". ${PUSH_AND_PLAN}`,
|
|
35767
|
-
pattern: /^"([^"]+)" imports a database socket driver \(pg, postgres, pg-native or mysql2\); customer Workers cannot open sockets\. Call the database with createPrivateDatabaseClient from "@ohmyhost\/customer-runtime\/database"\. Commit and push the change, then plan the new commit\.$/u
|
|
35768
|
-
}),
|
|
35769
|
-
source_syntax_invalid: Object.freeze({
|
|
35770
|
-
render: (path2) => `Invalid JavaScript or TypeScript in "${path2}". Fix the file, commit and push the change, then plan the new commit.`,
|
|
35771
|
-
pattern: /^Invalid JavaScript or TypeScript in "([^"]+)"\. Fix the file, commit and push the change, then plan the new commit\.$/u
|
|
35772
|
-
}),
|
|
35773
|
-
worker_module_default_export_required: Object.freeze({
|
|
35774
|
-
render: (path2) => `Worker module "${path2}" has no default export. Export default { fetch, scheduled } with the handlers ohmyho.st invokes; named exports are never called. ${PUSH_AND_PLAN}`,
|
|
35775
|
-
pattern: /^Worker module "([^"]+)" has no default export\. Export default \{ fetch, scheduled \} with the handlers ohmyho\.st invokes; named exports are never called\. Commit and push the change, then plan the new commit\.$/u
|
|
35776
|
-
}),
|
|
35777
|
-
scheduled_handler_required: Object.freeze({
|
|
35778
|
-
render: (path2) => `functions.crons is declared but "${path2}" exports no scheduled handler. Add scheduled(controller, env, ctx) to its default export, commit and push the change, then plan the new commit.`,
|
|
35779
|
-
pattern: /^functions\.crons is declared but "([^"]+)" exports no scheduled handler\. Add scheduled\(controller, env, ctx\) to its default export, commit and push the change, then plan the new commit\.$/u
|
|
35780
|
-
}),
|
|
35781
|
-
framework_base_path_unsupported: Object.freeze({
|
|
35782
|
-
render: (path2) => `"${path2}" sets a Vite base. ohmyho.st serves the app at the root origin: remove base or set base: "/". ${PUSH_AND_PLAN}`,
|
|
35783
|
-
pattern: /^"([^"]+)" sets a Vite base\. ohmyho\.st serves the app at the root origin: remove base or set base: "\/"\. Commit and push the change, then plan the new commit\.$/u
|
|
35784
|
-
}),
|
|
35785
|
-
tanstack_prerender_required: Object.freeze({
|
|
35786
|
-
render: (path2) => `"${path2}" does not call tanstackStart({ prerender: { enabled: true } }), which the static TanStack Start runtime needs; enable prerender, or set runtime.mode: edge with the Cloudflare Vite plugin. ${PUSH_AND_PLAN}`,
|
|
35787
|
-
pattern: /^"([^"]+)" does not call tanstackStart\(\{ prerender: \{ enabled: true \} \}\), which the static TanStack Start runtime needs; enable prerender, or set runtime\.mode: edge with the Cloudflare Vite plugin\. Commit and push the change, then plan the new commit\.$/u
|
|
35788
|
-
}),
|
|
35789
|
-
tanstack_vite_plugins_required: Object.freeze({
|
|
35790
|
-
render: (path2) => `"${path2}" does not follow the TanStack Start plugin contract: import tanstackStart from "@tanstack/react-start/plugin/vite"; on runtime edge also import cloudflare from "@cloudflare/vite-plugin" and call cloudflare({ viteEnvironment: { name: "ssr" } }) before tanstackStart(); on runtime static do not import the Cloudflare plugin. ${PUSH_AND_PLAN}`,
|
|
35791
|
-
pattern: /^"([^"]+)" does not follow the TanStack Start plugin contract: import tanstackStart from "@tanstack\/react-start\/plugin\/vite"; on runtime edge also import cloudflare from "@cloudflare\/vite-plugin" and call cloudflare\(\{ viteEnvironment: \{ name: "ssr" \} \}\) before tanstackStart\(\); on runtime static do not import the Cloudflare plugin\. Commit and push the change, then plan the new commit\.$/u
|
|
35792
|
-
}),
|
|
35793
|
-
next_cache_components_unsupported: Object.freeze({
|
|
35794
|
-
render: (path2) => `"${path2}" sets cacheComponents: true, which ohmyho.st does not support; remove it or set it to false. ${PUSH_AND_PLAN}`,
|
|
35795
|
-
pattern: /^"([^"]+)" sets cacheComponents: true, which ohmyho\.st does not support; remove it or set it to false\. Commit and push the change, then plan the new commit\.$/u
|
|
35796
|
-
})
|
|
35797
|
-
});
|
|
35798
|
-
function isFrameworkConversionDiagnosticCode(value) {
|
|
35799
|
-
return typeof value === "string" && Object.hasOwn(FRAMEWORK_CONVERSION_DETAILS, value);
|
|
35800
|
-
}
|
|
35801
|
-
function parseFrameworkConversionDiagnostic(value) {
|
|
35802
|
-
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
35803
|
-
return null;
|
|
35804
|
-
const record6 = value;
|
|
35805
|
-
const path2 = record6["path"];
|
|
35806
|
-
const code = record6["code"];
|
|
35807
|
-
if (Object.keys(record6).length !== 2 || !isFrameworkConversionDiagnosticCode(code) || typeof path2 !== "string" || path2.length > 240 || (code === "managed_auth_database_required" ? path2 !== "ohmyhost.yaml" : !/^[A-Za-z0-9_@()[\]./-]+\.(?:[cm]?[jt]sx?)$/u.test(path2)) || path2.split("/").some((segment) => segment === "" || segment === "." || segment === ".."))
|
|
35808
|
-
return null;
|
|
35809
|
-
return Object.freeze({ code, path: path2 });
|
|
35810
|
-
}
|
|
35811
|
-
function frameworkConversionDetail(value) {
|
|
35812
|
-
const diagnostic = parseFrameworkConversionDiagnostic(value);
|
|
35813
|
-
return diagnostic === null ? null : FRAMEWORK_CONVERSION_DETAILS[diagnostic.code].render(diagnostic.path);
|
|
35814
|
-
}
|
|
35815
|
-
function parseFrameworkConversionDetail(value) {
|
|
35816
|
-
if (typeof value !== "string")
|
|
35817
|
-
return null;
|
|
35818
|
-
for (const [code, detail] of Object.entries(FRAMEWORK_CONVERSION_DETAILS)) {
|
|
35819
|
-
const match = detail.pattern.exec(value);
|
|
35820
|
-
if (match !== null)
|
|
35821
|
-
return frameworkConversionDetail({ code, path: match[1] });
|
|
35822
|
-
}
|
|
35823
|
-
return null;
|
|
35824
|
-
}
|
|
35825
|
-
|
|
35826
36216
|
// packages/contracts/dist/operation-deployment-result.js
|
|
35827
36217
|
function exactRecord(value, keys) {
|
|
35828
36218
|
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
@@ -36288,9 +36678,9 @@ var PROBLEM_POLICY = {
|
|
|
36288
36678
|
framework_conversion_required: {
|
|
36289
36679
|
status: 409,
|
|
36290
36680
|
retryable: false,
|
|
36291
|
-
action: "Read detail
|
|
36681
|
+
action: "Read detail: a catalog sentence names the file, the rule and the required change. Apply it, save or push the corrected version and plan its commit. If detail names no file, run 'ohmyhost init --dry-run --json' at that commit and apply its blockers and the matching ohmyho.st framework Skill; init lists the install, build and server-only checks it cannot run locally under pending. Repeating the unchanged source cannot fix an admission error.",
|
|
36292
36682
|
title: "Framework conversion required",
|
|
36293
|
-
restAction: "Read detail
|
|
36683
|
+
restAction: "Read detail: a catalog sentence names the file, the rule and the required change. Apply it, save or push the corrected version and plan its commit. If detail names no file, run 'ohmyhost init --dry-run --json' at that commit and apply its blockers and the matching ohmyho.st framework Skill; init lists the install, build and server-only checks it cannot run locally under pending. Repeating the unchanged source cannot fix an admission error."
|
|
36294
36684
|
},
|
|
36295
36685
|
repository_configuration_missing: {
|
|
36296
36686
|
status: 409,
|
|
@@ -37216,11 +37606,13 @@ var parseOperation = (value) => {
|
|
|
37216
37606
|
};
|
|
37217
37607
|
};
|
|
37218
37608
|
function parseOperationFailure(value) {
|
|
37219
|
-
const input = exactRecord2(value, ["code", "message", "retryable", "suggested_action"]);
|
|
37220
|
-
|
|
37609
|
+
const input = exactRecord2(value, ["code", "message", "retryable", "suggested_action", "detail"]);
|
|
37610
|
+
const detail = input["detail"];
|
|
37611
|
+
const diagnostic2 = detail === void 0 || input["code"] !== "auto_deploy_plan_rejected" ? null : frameworkConversionDiagnosticFromDetail(detail);
|
|
37612
|
+
if (typeof input["code"] !== "string" || !boundedString(input["message"], 1, 512) || input["retryable"] !== false || !boundedString(input["suggested_action"], 1, 500) || detail !== void 0 && diagnostic2 === null)
|
|
37221
37613
|
throw new ResponseContractError();
|
|
37222
|
-
const safe = publicOperationFailure(input["code"]);
|
|
37223
|
-
if (safe.code !== input["code"])
|
|
37614
|
+
const safe = publicOperationFailure(input["code"], diagnostic2);
|
|
37615
|
+
if (safe.code !== input["code"] || safe.detail !== detail)
|
|
37224
37616
|
throw new ResponseContractError();
|
|
37225
37617
|
return safe;
|
|
37226
37618
|
}
|
|
@@ -40236,7 +40628,7 @@ function exact3(v, keys) {
|
|
|
40236
40628
|
// apps/product-cli/package.json
|
|
40237
40629
|
var package_default = {
|
|
40238
40630
|
name: "@ohmyhost/product-cli",
|
|
40239
|
-
version: "0.1.
|
|
40631
|
+
version: "0.1.32",
|
|
40240
40632
|
private: true,
|
|
40241
40633
|
ohmyhost: {
|
|
40242
40634
|
deployment: "production",
|
|
@@ -46739,6 +47131,9 @@ function formatErrors(errors) {
|
|
|
46739
47131
|
return (errors ?? []).map(({ instancePath, message }) => `${instancePath || "/"} ${message ?? "is invalid"}`).join("; ");
|
|
46740
47132
|
}
|
|
46741
47133
|
|
|
47134
|
+
// packages/contracts/dist/framework-source-rules.js
|
|
47135
|
+
var import_yaml2 = __toESM(require_dist(), 1);
|
|
47136
|
+
|
|
46742
47137
|
// packages/contracts/dist/framework-family.js
|
|
46743
47138
|
function classifyFrameworkFamily(dependencies) {
|
|
46744
47139
|
const next = dependencies.has("next");
|
|
@@ -46753,6 +47148,690 @@ function classifyFrameworkFamily(dependencies) {
|
|
|
46753
47148
|
return vite ? "vite" : "unknown";
|
|
46754
47149
|
}
|
|
46755
47150
|
|
|
47151
|
+
// packages/contracts/dist/framework-source-rules.js
|
|
47152
|
+
var MANAGED_BETTER_AUTH_VERSION = "1.7.1";
|
|
47153
|
+
var MAXIMUM_FRAMEWORK_CONFIG_BYTES = 256 * 1024;
|
|
47154
|
+
var WORKER_MODULE_PATH = "src/ohmyhost/worker.ts";
|
|
47155
|
+
var VITE_COMPANION_ENTRY_PATH = "src/ohmyhost/companion.ts";
|
|
47156
|
+
var PACKAGE_MANIFEST2 = "package.json";
|
|
47157
|
+
var REPOSITORY_CONFIG = "ohmyhost.yaml";
|
|
47158
|
+
var TANSTACK_START_PLUGIN = "@tanstack/react-start/plugin/vite";
|
|
47159
|
+
var CLOUDFLARE_PLUGIN = "@cloudflare/vite-plugin";
|
|
47160
|
+
var EXACT_VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u;
|
|
47161
|
+
var DEPENDENCY_FIELDS = Object.freeze({
|
|
47162
|
+
runtime: Object.freeze(["dependencies"]),
|
|
47163
|
+
build: Object.freeze(["dependencies", "devDependencies"]),
|
|
47164
|
+
any: Object.freeze(["dependencies", "devDependencies", "optionalDependencies"])
|
|
47165
|
+
});
|
|
47166
|
+
var REQUIRED_DEPENDENCIES = Object.freeze({
|
|
47167
|
+
nextjs: Object.freeze([
|
|
47168
|
+
["next", "runtime"],
|
|
47169
|
+
["react", "runtime"],
|
|
47170
|
+
["react-dom", "runtime"]
|
|
47171
|
+
]),
|
|
47172
|
+
"vite-static": Object.freeze([["vite", "build"]]),
|
|
47173
|
+
"tanstack-start": Object.freeze([
|
|
47174
|
+
["@tanstack/react-start", "runtime"],
|
|
47175
|
+
["@tanstack/react-router", "runtime"],
|
|
47176
|
+
["react", "runtime"],
|
|
47177
|
+
["react-dom", "runtime"],
|
|
47178
|
+
["@vitejs/plugin-react", "build"],
|
|
47179
|
+
["vite", "build"]
|
|
47180
|
+
]),
|
|
47181
|
+
functions: Object.freeze([])
|
|
47182
|
+
});
|
|
47183
|
+
var TANSTACK_EDGE_DEPENDENCY = Object.freeze([CLOUDFLARE_PLUGIN, "build"]);
|
|
47184
|
+
var BUILD_SCRIPT_FRAMEWORKS = Object.freeze({
|
|
47185
|
+
nextjs: "nextjs",
|
|
47186
|
+
"vite-static": "vite",
|
|
47187
|
+
"tanstack-start": "tanstack-start"
|
|
47188
|
+
});
|
|
47189
|
+
var BUILD_OUTPUTS = Object.freeze({
|
|
47190
|
+
"vite-static": "dist",
|
|
47191
|
+
"tanstack-start": "dist/client"
|
|
47192
|
+
});
|
|
47193
|
+
var REQUIRED_CONFIG_PATHS = Object.freeze({ nextjs: "next.config.ts", "tanstack-start": "vite.config.ts" });
|
|
47194
|
+
var MAXIMUM_LOCKFILE_LENGTH = 5 * 1024 * 1024;
|
|
47195
|
+
var MAXIMUM_PNPM_LOCKFILE_HEAD = 256 * 1024;
|
|
47196
|
+
var PNPM_LOCKFILE_BODY = /^(?:packages|snapshots):/mu;
|
|
47197
|
+
var PNPM_LOCKFILE_VERSION = /^(?:6|9)\.[0-9]{1,3}$/u;
|
|
47198
|
+
var PNPM_RESOLVED_VERSION = /^([^()]+)(?:\(.*\))?$/u;
|
|
47199
|
+
var DEFAULT_EXPORT_PATTERN = /\bexport\s+default\b/u;
|
|
47200
|
+
var FETCH_HANDLER_PATTERN = /(?:[{,;}]|\basync)\s*fetch\s*(?:\(|:|,|\})/u;
|
|
47201
|
+
var SCHEDULED_HANDLER_PATTERN = /\bscheduled\s*(?:\(|:|,|\})/u;
|
|
47202
|
+
var DATABASE_CONTRACT_PATTERNS = Object.freeze([
|
|
47203
|
+
[
|
|
47204
|
+
/\bHYPERDRIVE\b|\bconnectionString\b|\bDATABASE_URL\b|postgres(?:ql)?:\/\//u,
|
|
47205
|
+
"database_binding_private"
|
|
47206
|
+
],
|
|
47207
|
+
[
|
|
47208
|
+
/\bfrom\s*["'](?:pg|postgres|mysql2|pg-native)["']|\brequire\(\s*["'](?:pg|postgres|pg-native)["']/u,
|
|
47209
|
+
"database_driver_unsupported"
|
|
47210
|
+
]
|
|
47211
|
+
]);
|
|
47212
|
+
function parsePackageManifest(value) {
|
|
47213
|
+
let parsed = value;
|
|
47214
|
+
if (typeof value === "string") {
|
|
47215
|
+
try {
|
|
47216
|
+
parsed = JSON.parse(value);
|
|
47217
|
+
} catch {
|
|
47218
|
+
return null;
|
|
47219
|
+
}
|
|
47220
|
+
}
|
|
47221
|
+
return isRecord4(parsed) ? parsed : null;
|
|
47222
|
+
}
|
|
47223
|
+
function packageManagerDeclaration(manifest2) {
|
|
47224
|
+
const value = manifest2["packageManager"];
|
|
47225
|
+
return typeof value === "string" ? value : void 0;
|
|
47226
|
+
}
|
|
47227
|
+
function declaredDependency(manifest2, name, placement) {
|
|
47228
|
+
for (const field of DEPENDENCY_FIELDS[placement]) {
|
|
47229
|
+
const version2 = ownValue(manifest2[field], name);
|
|
47230
|
+
if (typeof version2 === "string" && version2.length > 0)
|
|
47231
|
+
return version2;
|
|
47232
|
+
}
|
|
47233
|
+
return null;
|
|
47234
|
+
}
|
|
47235
|
+
function declaredDependencyNames(manifest2) {
|
|
47236
|
+
const names = /* @__PURE__ */ new Set();
|
|
47237
|
+
for (const field of DEPENDENCY_FIELDS.any) {
|
|
47238
|
+
const dependencies = manifest2[field];
|
|
47239
|
+
if (!isRecord4(dependencies))
|
|
47240
|
+
continue;
|
|
47241
|
+
for (const [name, version2] of Object.entries(dependencies))
|
|
47242
|
+
if (typeof version2 === "string" && version2.length > 0)
|
|
47243
|
+
names.add(name);
|
|
47244
|
+
}
|
|
47245
|
+
return names;
|
|
47246
|
+
}
|
|
47247
|
+
function viteCompanionRequired(configuration) {
|
|
47248
|
+
return configuration.runtime.mode === "edge" || configuration.database?.enabled === true || configuration.auth?.provider === "better-auth" || configuration.mail?.enabled === true || configuration.storage !== void 0 || (configuration.functions?.crons.length ?? 0) > 0;
|
|
47249
|
+
}
|
|
47250
|
+
function frameworkVersionAdmissions(framework, manifest2, lockfile) {
|
|
47251
|
+
const admissions = [];
|
|
47252
|
+
let locked;
|
|
47253
|
+
for (const [name, placement] of REQUIRED_DEPENDENCIES[framework]) {
|
|
47254
|
+
if (!isFrameworkVersionPackage(name))
|
|
47255
|
+
continue;
|
|
47256
|
+
const declared = declaredDependency(manifest2, name, placement);
|
|
47257
|
+
if (declared === null)
|
|
47258
|
+
continue;
|
|
47259
|
+
locked ??= lockfile === null ? () => null : lockedPackageVersions(lockfile);
|
|
47260
|
+
admissions.push(classifyFrameworkVersion(name, declared, locked(name)));
|
|
47261
|
+
}
|
|
47262
|
+
return Object.freeze(admissions);
|
|
47263
|
+
}
|
|
47264
|
+
function lockedPackageVersions(lockfile) {
|
|
47265
|
+
const { name, text } = lockfile;
|
|
47266
|
+
const npm = name === "package-lock.json";
|
|
47267
|
+
if (!npm && name !== "pnpm-lock.yaml" || typeof text !== "string" || text.length > MAXIMUM_LOCKFILE_LENGTH)
|
|
47268
|
+
return () => null;
|
|
47269
|
+
const document2 = npm ? parseJsonLockfile(text) : parsePnpmLockfileHead(text);
|
|
47270
|
+
return (packageName) => {
|
|
47271
|
+
const version2 = npm ? npmLockedVersion(document2, packageName) : pnpmLockedVersion(document2, packageName);
|
|
47272
|
+
return isResolvedFrameworkVersion(version2) ? version2 : null;
|
|
47273
|
+
};
|
|
47274
|
+
}
|
|
47275
|
+
function frameworkSourceFindings(input) {
|
|
47276
|
+
const versions = frameworkVersionAdmissions(input.framework, input.manifest, input.lockfile);
|
|
47277
|
+
return Object.freeze({
|
|
47278
|
+
findings: Object.freeze([
|
|
47279
|
+
...manifestFindings(input, versions),
|
|
47280
|
+
...configurationFindings(input),
|
|
47281
|
+
...betterAuthFindings(input.configuration, input.manifest),
|
|
47282
|
+
...frameworkConfigFindings(input)
|
|
47283
|
+
]),
|
|
47284
|
+
versions
|
|
47285
|
+
});
|
|
47286
|
+
}
|
|
47287
|
+
function repositorySourceFindings(files) {
|
|
47288
|
+
const configuration = repositoryConfiguration(files.get(REPOSITORY_CONFIG));
|
|
47289
|
+
if (configuration === null)
|
|
47290
|
+
return refused("repository_configuration_invalid", REPOSITORY_CONFIG);
|
|
47291
|
+
const prefix = configuration.applicationRoot === "." ? "" : `${configuration.applicationRoot}/`;
|
|
47292
|
+
const applicationFiles = [...files.keys()].filter((path2) => path2.length > prefix.length && path2.startsWith(prefix)).map((path2) => path2.slice(prefix.length));
|
|
47293
|
+
if (applicationFiles.length === 0)
|
|
47294
|
+
return refused("application_root_missing", REPOSITORY_CONFIG);
|
|
47295
|
+
const text = (path2) => files.get(`${prefix}${path2}`) ?? null;
|
|
47296
|
+
const manifest2 = parsePackageManifest(text(PACKAGE_MANIFEST2));
|
|
47297
|
+
if (manifest2 === null)
|
|
47298
|
+
return refused("package_manifest_invalid", PACKAGE_MANIFEST2);
|
|
47299
|
+
const family = configuration.runtime.mode === "functions" ? "functions" : classifyFrameworkFamily(declaredDependencyNames(manifest2));
|
|
47300
|
+
if (family === "ambiguous")
|
|
47301
|
+
return refused("framework_ambiguous", PACKAGE_MANIFEST2);
|
|
47302
|
+
if (family === "unknown")
|
|
47303
|
+
return refused("framework_unsupported", PACKAGE_MANIFEST2);
|
|
47304
|
+
const framework = family === "vite" ? "vite-static" : family;
|
|
47305
|
+
const findings = [];
|
|
47306
|
+
const packageManager = admitted(findings, () => resolvePackageManager({
|
|
47307
|
+
packageManager: packageManagerDeclaration(manifest2),
|
|
47308
|
+
files: applicationFiles
|
|
47309
|
+
})) ?? null;
|
|
47310
|
+
const configPath = frameworkConfigPath(framework, applicationFiles, findings);
|
|
47311
|
+
const configText = configPath === null ? null : text(configPath);
|
|
47312
|
+
if (configPath !== null && configText === null)
|
|
47313
|
+
findings.push(diagnostic({ code: "source_syntax_invalid", path: configPath }));
|
|
47314
|
+
const lockfileText = packageManager === null ? null : text(packageManager.lockfile);
|
|
47315
|
+
const result = frameworkSourceFindings({
|
|
47316
|
+
framework,
|
|
47317
|
+
files: applicationFiles,
|
|
47318
|
+
configuration,
|
|
47319
|
+
manifest: manifest2,
|
|
47320
|
+
packageManager,
|
|
47321
|
+
lockfile: packageManager === null || lockfileText === null ? null : Object.freeze({ name: packageManager.lockfile, text: lockfileText }),
|
|
47322
|
+
frameworkConfig: configPath === null || configText === null ? null : Object.freeze({ path: configPath, text: configText })
|
|
47323
|
+
});
|
|
47324
|
+
return Object.freeze({
|
|
47325
|
+
framework,
|
|
47326
|
+
findings: Object.freeze([...findings, ...result.findings]),
|
|
47327
|
+
versions: result.versions
|
|
47328
|
+
});
|
|
47329
|
+
}
|
|
47330
|
+
function nextConfigFindings(text, path2) {
|
|
47331
|
+
const lexed = lexFrameworkConfig(text, path2);
|
|
47332
|
+
if ("finding" in lexed)
|
|
47333
|
+
return Object.freeze([lexed.finding]);
|
|
47334
|
+
const { tokens } = lexed;
|
|
47335
|
+
const findings = [];
|
|
47336
|
+
if (hasNonRootProperty(tokens, ["basePath", "assetPrefix"], ["", "/"]))
|
|
47337
|
+
findings.push(diagnostic({ code: "next_base_path_unsupported", path: path2 }));
|
|
47338
|
+
if (hasProperty(tokens, "output", "export") || hasProperty(tokens, "output", "standalone"))
|
|
47339
|
+
findings.push(diagnostic({ code: "next_output_unsupported", path: path2 }));
|
|
47340
|
+
if (hasProperty(tokens, "cacheComponents", "true"))
|
|
47341
|
+
findings.push(diagnostic({ code: "next_cache_components_unsupported", path: path2 }));
|
|
47342
|
+
if (!hasExportDefault(tokens) && !(path2.slice(path2.lastIndexOf("/") + 1) === "next.config.js" && hasModuleExports(tokens)))
|
|
47343
|
+
findings.push(diagnostic({ code: "framework_config_default_export_required", path: path2 }));
|
|
47344
|
+
return Object.freeze(findings);
|
|
47345
|
+
}
|
|
47346
|
+
function viteConfigFindings(text, path2) {
|
|
47347
|
+
const lexed = lexFrameworkConfig(text, path2);
|
|
47348
|
+
if ("finding" in lexed)
|
|
47349
|
+
return Object.freeze([lexed.finding]);
|
|
47350
|
+
return Object.freeze(hasNonRootProperty(lexed.tokens, ["base"], ["/"]) ? [diagnostic({ code: "framework_base_path_unsupported", path: path2 })] : []);
|
|
47351
|
+
}
|
|
47352
|
+
function tanStackViteConfigFindings(text, path2, runtime) {
|
|
47353
|
+
const lexed = lexFrameworkConfig(text, path2);
|
|
47354
|
+
if ("finding" in lexed)
|
|
47355
|
+
return Object.freeze([lexed.finding]);
|
|
47356
|
+
const findings = [];
|
|
47357
|
+
if (hasNonRootProperty(lexed.tokens, ["base"], ["/"]))
|
|
47358
|
+
findings.push(diagnostic({ code: "framework_base_path_unsupported", path: path2 }));
|
|
47359
|
+
if (runtime === null)
|
|
47360
|
+
return Object.freeze(findings);
|
|
47361
|
+
const contract = tanStackPluginContract(lexed.tokens, runtime);
|
|
47362
|
+
if (contract === null)
|
|
47363
|
+
return Object.freeze([diagnostic({ code: "source_syntax_invalid", path: path2 })]);
|
|
47364
|
+
if (!contract.plugins)
|
|
47365
|
+
findings.push(diagnostic({ code: "tanstack_vite_plugins_required", path: path2 }));
|
|
47366
|
+
if (!contract.prerender)
|
|
47367
|
+
findings.push(diagnostic({ code: "tanstack_prerender_required", path: path2 }));
|
|
47368
|
+
return Object.freeze(findings);
|
|
47369
|
+
}
|
|
47370
|
+
function tanStackViteConfigRuntime(text) {
|
|
47371
|
+
const tokens = exceedsConfigBytes(text) ? null : configTokens(text);
|
|
47372
|
+
if (tokens === null)
|
|
47373
|
+
return null;
|
|
47374
|
+
for (const runtime of ["static", "edge"]) {
|
|
47375
|
+
const contract = tanStackPluginContract(tokens, runtime);
|
|
47376
|
+
if (contract?.plugins === true && contract.prerender)
|
|
47377
|
+
return runtime;
|
|
47378
|
+
}
|
|
47379
|
+
return null;
|
|
47380
|
+
}
|
|
47381
|
+
function databaseContractCodes(source) {
|
|
47382
|
+
const code = sourceWithoutComments(source);
|
|
47383
|
+
return Object.freeze(DATABASE_CONTRACT_PATTERNS.filter(([pattern]) => pattern.test(code)).map(([, found]) => found));
|
|
47384
|
+
}
|
|
47385
|
+
function workerModuleFindings(worker) {
|
|
47386
|
+
const { path: path2 } = worker;
|
|
47387
|
+
const code = sourceWithoutComments(worker.source);
|
|
47388
|
+
if (!DEFAULT_EXPORT_PATTERN.test(code))
|
|
47389
|
+
return Object.freeze([diagnostic({ code: "worker_module_default_export_required", path: path2 })]);
|
|
47390
|
+
const findings = [];
|
|
47391
|
+
if (worker.fetch && !FETCH_HANDLER_PATTERN.test(code))
|
|
47392
|
+
findings.push(diagnostic({ code: "worker_module_fetch_handler_required", path: path2 }));
|
|
47393
|
+
if (worker.scheduled && !SCHEDULED_HANDLER_PATTERN.test(code))
|
|
47394
|
+
findings.push(diagnostic({ code: "scheduled_handler_required", path: path2 }));
|
|
47395
|
+
return Object.freeze(findings);
|
|
47396
|
+
}
|
|
47397
|
+
function sourceWithoutComments(source) {
|
|
47398
|
+
let result = "";
|
|
47399
|
+
let quote = null;
|
|
47400
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
47401
|
+
const character = source[index];
|
|
47402
|
+
const next = source[index + 1];
|
|
47403
|
+
if (quote !== null) {
|
|
47404
|
+
result += character;
|
|
47405
|
+
if (character === "\\") {
|
|
47406
|
+
index += 1;
|
|
47407
|
+
result += source[index] ?? "";
|
|
47408
|
+
} else if (character === quote || character === "\n" && quote !== "`")
|
|
47409
|
+
quote = null;
|
|
47410
|
+
continue;
|
|
47411
|
+
}
|
|
47412
|
+
if (character === "'" || character === '"' || character === "`") {
|
|
47413
|
+
quote = character;
|
|
47414
|
+
result += character;
|
|
47415
|
+
continue;
|
|
47416
|
+
}
|
|
47417
|
+
if (character === "/" && next === "/" && source[index - 1] !== "\\") {
|
|
47418
|
+
while (index < source.length && source[index] !== "\n") {
|
|
47419
|
+
result += " ";
|
|
47420
|
+
index += 1;
|
|
47421
|
+
}
|
|
47422
|
+
result += "\n";
|
|
47423
|
+
continue;
|
|
47424
|
+
}
|
|
47425
|
+
if (character === "/" && next === "*" && source[index - 1] !== "\\") {
|
|
47426
|
+
result += " ";
|
|
47427
|
+
index += 2;
|
|
47428
|
+
while (index < source.length && !(source[index] === "*" && source[index + 1] === "/")) {
|
|
47429
|
+
result += source[index] === "\n" ? "\n" : " ";
|
|
47430
|
+
index += 1;
|
|
47431
|
+
}
|
|
47432
|
+
result += " ";
|
|
47433
|
+
index += 1;
|
|
47434
|
+
continue;
|
|
47435
|
+
}
|
|
47436
|
+
result += character;
|
|
47437
|
+
}
|
|
47438
|
+
return result;
|
|
47439
|
+
}
|
|
47440
|
+
function isDatabaseContractModule(path2) {
|
|
47441
|
+
return /\.(?:ts|tsx|js|jsx|mjs|mts)$/u.test(path2) && !/\.d\.[cm]?ts$/u.test(path2);
|
|
47442
|
+
}
|
|
47443
|
+
function manifestFindings(input, versions) {
|
|
47444
|
+
const { configuration, framework, manifest: manifest2 } = input;
|
|
47445
|
+
const findings = [];
|
|
47446
|
+
if (framework !== "functions" && !isAdmittedFrameworkBuildScript(BUILD_SCRIPT_FRAMEWORKS[framework], ownValue(manifest2["scripts"], "build")))
|
|
47447
|
+
findings.push(diagnostic({ code: "build_script_unsupported", path: PACKAGE_MANIFEST2, framework }));
|
|
47448
|
+
const required = framework === "tanstack-start" && configuration.runtime.mode === "edge" ? [...REQUIRED_DEPENDENCIES[framework], TANSTACK_EDGE_DEPENDENCY] : REQUIRED_DEPENDENCIES[framework];
|
|
47449
|
+
for (const [name, placement] of required)
|
|
47450
|
+
if (declaredDependency(manifest2, name, placement) === null)
|
|
47451
|
+
findings.push(diagnostic({
|
|
47452
|
+
code: "framework_dependency_required",
|
|
47453
|
+
path: PACKAGE_MANIFEST2,
|
|
47454
|
+
package: name
|
|
47455
|
+
}));
|
|
47456
|
+
for (const admission of versions) {
|
|
47457
|
+
const finding = frameworkVersionDiagnostic(admission, input.lockfile?.name ?? null);
|
|
47458
|
+
if (finding !== null)
|
|
47459
|
+
findings.push(finding);
|
|
47460
|
+
}
|
|
47461
|
+
if (framework === "nextjs")
|
|
47462
|
+
for (const name of ["react", "react-dom"]) {
|
|
47463
|
+
const version2 = declaredDependency(manifest2, name, "runtime");
|
|
47464
|
+
if (version2 !== null && !EXACT_VERSION.test(version2))
|
|
47465
|
+
findings.push(diagnostic({
|
|
47466
|
+
code: "react_version_exact_required",
|
|
47467
|
+
path: PACKAGE_MANIFEST2,
|
|
47468
|
+
package: name
|
|
47469
|
+
}));
|
|
47470
|
+
}
|
|
47471
|
+
return findings;
|
|
47472
|
+
}
|
|
47473
|
+
function configurationFindings(input) {
|
|
47474
|
+
const { configuration, files, framework, packageManager } = input;
|
|
47475
|
+
const build = "command" in configuration.build ? configuration.build : null;
|
|
47476
|
+
const crons = (configuration.functions?.crons.length ?? 0) > 0;
|
|
47477
|
+
const findings = [];
|
|
47478
|
+
if (framework !== "functions" && !runtimeModeAdmitted(framework, configuration, crons))
|
|
47479
|
+
findings.push(diagnostic({ code: "runtime_mode_unsupported", path: REPOSITORY_CONFIG, framework }));
|
|
47480
|
+
if (build !== null && framework !== "nextjs" && build.ssg_cache_max_mib !== void 0)
|
|
47481
|
+
findings.push(diagnostic({ code: "build_option_unsupported", path: REPOSITORY_CONFIG }));
|
|
47482
|
+
const output = BUILD_OUTPUTS[framework];
|
|
47483
|
+
if (build !== null && output !== void 0 && build.output !== output)
|
|
47484
|
+
findings.push(diagnostic({ code: "build_output_unsupported", path: REPOSITORY_CONFIG, framework }));
|
|
47485
|
+
if (packageManager !== null && framework !== "nextjs" && (configuration.build.install !== packageManager.installCommand || build !== null && build.command !== packageManager.buildCommand))
|
|
47486
|
+
findings.push(diagnostic({ code: "build_command_mismatch", path: REPOSITORY_CONFIG }));
|
|
47487
|
+
if (framework === "vite-static" && viteCompanionRequired(configuration) && !files.includes(VITE_COMPANION_ENTRY_PATH))
|
|
47488
|
+
findings.push(diagnostic({ code: "companion_entry_required", path: VITE_COMPANION_ENTRY_PATH }));
|
|
47489
|
+
if ((framework === "functions" || crons && (framework === "nextjs" || framework === "tanstack-start" && configuration.runtime.mode === "edge")) && !files.includes(WORKER_MODULE_PATH))
|
|
47490
|
+
findings.push(diagnostic({ code: "worker_module_required", path: WORKER_MODULE_PATH }));
|
|
47491
|
+
if (managedCustomerAuthAdmissionIssue(configuration) !== null)
|
|
47492
|
+
findings.push(diagnostic({ code: "managed_auth_database_required", path: REPOSITORY_CONFIG }));
|
|
47493
|
+
return findings;
|
|
47494
|
+
}
|
|
47495
|
+
function runtimeModeAdmitted(framework, configuration, crons) {
|
|
47496
|
+
const mode = configuration.runtime.mode;
|
|
47497
|
+
if (framework === "nextjs")
|
|
47498
|
+
return mode === "edge";
|
|
47499
|
+
if (framework === "vite-static")
|
|
47500
|
+
return mode === (viteCompanionRequired(configuration) ? "edge" : "static");
|
|
47501
|
+
return mode === "edge" || mode === "static" && !crons && configuration.storage === void 0;
|
|
47502
|
+
}
|
|
47503
|
+
function betterAuthFindings(configuration, manifest2) {
|
|
47504
|
+
return configuration.auth?.provider === "better-auth" && declaredDependency(manifest2, "better-auth", "any") !== MANAGED_BETTER_AUTH_VERSION ? [diagnostic({ code: "better_auth_version_required", path: PACKAGE_MANIFEST2 })] : [];
|
|
47505
|
+
}
|
|
47506
|
+
function frameworkConfigFindings(input) {
|
|
47507
|
+
const { frameworkConfig, framework } = input;
|
|
47508
|
+
if (frameworkConfig === null || framework === "functions")
|
|
47509
|
+
return [];
|
|
47510
|
+
const { path: path2, text } = frameworkConfig;
|
|
47511
|
+
if (framework === "nextjs")
|
|
47512
|
+
return nextConfigFindings(text, path2);
|
|
47513
|
+
if (framework === "vite-static")
|
|
47514
|
+
return viteConfigFindings(text, path2);
|
|
47515
|
+
const mode = input.configuration.runtime.mode;
|
|
47516
|
+
return tanStackViteConfigFindings(text, path2, mode === "edge" || mode === "static" ? mode : null);
|
|
47517
|
+
}
|
|
47518
|
+
function repositoryConfiguration(text) {
|
|
47519
|
+
if (typeof text !== "string")
|
|
47520
|
+
return null;
|
|
47521
|
+
try {
|
|
47522
|
+
return parseOhmyhostConfigYaml(text);
|
|
47523
|
+
} catch (error) {
|
|
47524
|
+
if (error instanceof TypeError)
|
|
47525
|
+
return null;
|
|
47526
|
+
throw error;
|
|
47527
|
+
}
|
|
47528
|
+
}
|
|
47529
|
+
function frameworkConfigPath(framework, files, findings) {
|
|
47530
|
+
if (framework === "functions")
|
|
47531
|
+
return null;
|
|
47532
|
+
const path2 = admitted(findings, () => resolveFrameworkConfig(framework === "nextjs" ? "next.config" : "vite.config", files));
|
|
47533
|
+
if (framework === "nextjs")
|
|
47534
|
+
admitted(findings, () => resolveFrameworkConfig("open-next.config", files));
|
|
47535
|
+
const required = REQUIRED_CONFIG_PATHS[framework];
|
|
47536
|
+
if (path2 === null && required !== void 0)
|
|
47537
|
+
findings.push(diagnostic({ code: "framework_config_required", path: required }));
|
|
47538
|
+
return path2 ?? null;
|
|
47539
|
+
}
|
|
47540
|
+
function admitted(findings, admit) {
|
|
47541
|
+
try {
|
|
47542
|
+
return admit();
|
|
47543
|
+
} catch (error) {
|
|
47544
|
+
if (!(error instanceof FrameworkAdmissionError))
|
|
47545
|
+
throw error;
|
|
47546
|
+
findings.push(error.diagnostic);
|
|
47547
|
+
return void 0;
|
|
47548
|
+
}
|
|
47549
|
+
}
|
|
47550
|
+
function refused(code, path2) {
|
|
47551
|
+
return Object.freeze({
|
|
47552
|
+
framework: null,
|
|
47553
|
+
findings: Object.freeze([diagnostic({ code, path: path2 })]),
|
|
47554
|
+
versions: Object.freeze([])
|
|
47555
|
+
});
|
|
47556
|
+
}
|
|
47557
|
+
function diagnostic(value) {
|
|
47558
|
+
const parsed = parseFrameworkConversionDiagnostic(value);
|
|
47559
|
+
if (parsed === null)
|
|
47560
|
+
throw new TypeError("Framework source diagnostic is invalid");
|
|
47561
|
+
return parsed;
|
|
47562
|
+
}
|
|
47563
|
+
function isFrameworkVersionPackage(name) {
|
|
47564
|
+
return Object.hasOwn(FRAMEWORK_VERSION_POLICY, name);
|
|
47565
|
+
}
|
|
47566
|
+
function parseJsonLockfile(text) {
|
|
47567
|
+
try {
|
|
47568
|
+
return JSON.parse(text);
|
|
47569
|
+
} catch {
|
|
47570
|
+
return null;
|
|
47571
|
+
}
|
|
47572
|
+
}
|
|
47573
|
+
function parsePnpmLockfileHead(text) {
|
|
47574
|
+
const end = text.search(PNPM_LOCKFILE_BODY);
|
|
47575
|
+
const head = end === -1 ? text : text.slice(0, end);
|
|
47576
|
+
if (head.length > MAXIMUM_PNPM_LOCKFILE_HEAD)
|
|
47577
|
+
return null;
|
|
47578
|
+
try {
|
|
47579
|
+
const document2 = (0, import_yaml2.parseDocument)(head, {
|
|
47580
|
+
customTags: [],
|
|
47581
|
+
logLevel: "error",
|
|
47582
|
+
merge: false,
|
|
47583
|
+
prettyErrors: false,
|
|
47584
|
+
resolveKnownTags: false,
|
|
47585
|
+
schema: "core",
|
|
47586
|
+
strict: true,
|
|
47587
|
+
stringKeys: true,
|
|
47588
|
+
// The parser compares every key with every earlier one; pnpm refuses repeated keys itself.
|
|
47589
|
+
uniqueKeys: false,
|
|
47590
|
+
version: "1.2"
|
|
47591
|
+
});
|
|
47592
|
+
return document2.errors.length === 0 && document2.warnings.length === 0 ? document2.toJS({ maxAliasCount: 0 }) : null;
|
|
47593
|
+
} catch {
|
|
47594
|
+
return null;
|
|
47595
|
+
}
|
|
47596
|
+
}
|
|
47597
|
+
function npmLockedVersion(document2, packageName) {
|
|
47598
|
+
if (!isRecord4(document2) || document2["lockfileVersion"] !== 2 && document2["lockfileVersion"] !== 3)
|
|
47599
|
+
return null;
|
|
47600
|
+
const entry = ownValue(document2["packages"], `node_modules/${packageName}`);
|
|
47601
|
+
return isRecord4(entry) && !Object.hasOwn(entry, "link") && (entry["name"] === void 0 || entry["name"] === packageName) ? entry["version"] : null;
|
|
47602
|
+
}
|
|
47603
|
+
function pnpmLockedVersion(document2, packageName) {
|
|
47604
|
+
if (!isRecord4(document2))
|
|
47605
|
+
return null;
|
|
47606
|
+
const lockfileVersion = document2["lockfileVersion"];
|
|
47607
|
+
if (typeof lockfileVersion !== "string" || !PNPM_LOCKFILE_VERSION.test(lockfileVersion))
|
|
47608
|
+
return null;
|
|
47609
|
+
const importers = document2["importers"];
|
|
47610
|
+
const root = importers === void 0 && lockfileVersion.startsWith("6.") ? document2 : ownValue(importers, ".");
|
|
47611
|
+
if (!isRecord4(root))
|
|
47612
|
+
return null;
|
|
47613
|
+
for (const group of ["dependencies", "devDependencies", "optionalDependencies"]) {
|
|
47614
|
+
const dependencies = root[group];
|
|
47615
|
+
if (!isRecord4(dependencies) || !Object.hasOwn(dependencies, packageName))
|
|
47616
|
+
continue;
|
|
47617
|
+
const entry = dependencies[packageName];
|
|
47618
|
+
return isRecord4(entry) && typeof entry["version"] === "string" ? PNPM_RESOLVED_VERSION.exec(entry["version"])?.[1] ?? null : null;
|
|
47619
|
+
}
|
|
47620
|
+
return null;
|
|
47621
|
+
}
|
|
47622
|
+
function ownValue(record6, key) {
|
|
47623
|
+
return isRecord4(record6) && Object.hasOwn(record6, key) ? record6[key] : void 0;
|
|
47624
|
+
}
|
|
47625
|
+
function isRecord4(value) {
|
|
47626
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
47627
|
+
}
|
|
47628
|
+
var PUNCTUATION = "{}[]():,.;?=-!";
|
|
47629
|
+
function lexFrameworkConfig(text, path2) {
|
|
47630
|
+
const tooLarge = diagnostic({ code: "framework_config_too_large", path: path2 });
|
|
47631
|
+
if (exceedsConfigBytes(text))
|
|
47632
|
+
return { finding: tooLarge };
|
|
47633
|
+
const tokens = configTokens(text);
|
|
47634
|
+
return tokens === null ? { finding: diagnostic({ code: "source_syntax_invalid", path: path2 }) } : { tokens };
|
|
47635
|
+
}
|
|
47636
|
+
function exceedsConfigBytes(text) {
|
|
47637
|
+
return text.length > MAXIMUM_FRAMEWORK_CONFIG_BYTES || new TextEncoder().encode(text).byteLength > MAXIMUM_FRAMEWORK_CONFIG_BYTES;
|
|
47638
|
+
}
|
|
47639
|
+
function configTokens(source) {
|
|
47640
|
+
const tokens = [];
|
|
47641
|
+
let index = 0;
|
|
47642
|
+
while (index < source.length) {
|
|
47643
|
+
const character = source[index];
|
|
47644
|
+
const next = source[index + 1];
|
|
47645
|
+
if (character === "/" && next === "/") {
|
|
47646
|
+
const end = source.indexOf("\n", index + 2);
|
|
47647
|
+
index = end === -1 ? source.length : end + 1;
|
|
47648
|
+
} else if (character === "/" && next === "*") {
|
|
47649
|
+
const end = source.indexOf("*/", index + 2);
|
|
47650
|
+
if (end === -1)
|
|
47651
|
+
return null;
|
|
47652
|
+
index = end + 2;
|
|
47653
|
+
} else if (character === "'" || character === '"' || character === "`") {
|
|
47654
|
+
const string = readString(source, index, character);
|
|
47655
|
+
if (string === null)
|
|
47656
|
+
return null;
|
|
47657
|
+
tokens.push(string.token);
|
|
47658
|
+
index = string.nextIndex;
|
|
47659
|
+
} else if (isIdentifierStart(character) || isDigit(character)) {
|
|
47660
|
+
const start = index;
|
|
47661
|
+
const part = isDigit(character) ? isDigit : isIdentifierPart;
|
|
47662
|
+
index += 1;
|
|
47663
|
+
while (index < source.length && part(source[index]))
|
|
47664
|
+
index += 1;
|
|
47665
|
+
tokens.push({
|
|
47666
|
+
kind: isDigit(character) ? "number" : "identifier",
|
|
47667
|
+
value: source.slice(start, index)
|
|
47668
|
+
});
|
|
47669
|
+
} else {
|
|
47670
|
+
if (PUNCTUATION.includes(character))
|
|
47671
|
+
tokens.push({ kind: "punctuation", value: character });
|
|
47672
|
+
index += 1;
|
|
47673
|
+
}
|
|
47674
|
+
}
|
|
47675
|
+
return tokens;
|
|
47676
|
+
}
|
|
47677
|
+
function readString(source, index, quote) {
|
|
47678
|
+
let cursor = index + 1;
|
|
47679
|
+
let value = "";
|
|
47680
|
+
while (cursor < source.length) {
|
|
47681
|
+
const character = source[cursor];
|
|
47682
|
+
if (character === quote)
|
|
47683
|
+
return { token: { kind: "string", value }, nextIndex: cursor + 1 };
|
|
47684
|
+
if (character === "\\") {
|
|
47685
|
+
const escaped = source[cursor + 1];
|
|
47686
|
+
if (escaped === void 0)
|
|
47687
|
+
return null;
|
|
47688
|
+
value += escaped;
|
|
47689
|
+
cursor += 2;
|
|
47690
|
+
} else {
|
|
47691
|
+
value += character;
|
|
47692
|
+
cursor += 1;
|
|
47693
|
+
}
|
|
47694
|
+
}
|
|
47695
|
+
return null;
|
|
47696
|
+
}
|
|
47697
|
+
function isIdentifierStart(character) {
|
|
47698
|
+
return character >= "A" && character <= "Z" || character >= "a" && character <= "z" || character === "_" || character === "$";
|
|
47699
|
+
}
|
|
47700
|
+
function isIdentifierPart(character) {
|
|
47701
|
+
return isIdentifierStart(character) || isDigit(character);
|
|
47702
|
+
}
|
|
47703
|
+
function isDigit(character) {
|
|
47704
|
+
return character >= "0" && character <= "9";
|
|
47705
|
+
}
|
|
47706
|
+
function hasNonRootProperty(tokens, names, rootStrings) {
|
|
47707
|
+
let objectDepth = 0;
|
|
47708
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
47709
|
+
const token = tokens[index];
|
|
47710
|
+
if (token.value === "{" || token.value === "}") {
|
|
47711
|
+
objectDepth += token.value === "{" ? 1 : -1;
|
|
47712
|
+
continue;
|
|
47713
|
+
}
|
|
47714
|
+
const before = tokens[index - 1]?.value;
|
|
47715
|
+
const directKey = before === "{" || before === ",";
|
|
47716
|
+
const computedKey = before === "[" && tokens[index + 1]?.value === "]" && (tokens[index - 2]?.value === "{" || tokens[index - 2]?.value === ",");
|
|
47717
|
+
if (objectDepth < 1 || !names.includes(token.value) || !directKey && !computedKey)
|
|
47718
|
+
continue;
|
|
47719
|
+
const separator = tokens[index + (computedKey ? 2 : 1)]?.value;
|
|
47720
|
+
const value = tokens[index + (computedKey ? 3 : 2)];
|
|
47721
|
+
const root = separator === ":" && (value?.kind === "string" && rootStrings.includes(value.value) || value?.kind === "identifier" && value.value === "undefined");
|
|
47722
|
+
if (!root)
|
|
47723
|
+
return true;
|
|
47724
|
+
}
|
|
47725
|
+
return false;
|
|
47726
|
+
}
|
|
47727
|
+
function hasProperty(tokens, name, value) {
|
|
47728
|
+
return tokens.some((token, index) => token.value === name && tokens[index + 1]?.value === ":" && tokens[index + 2]?.value === value);
|
|
47729
|
+
}
|
|
47730
|
+
function hasExportDefault(tokens) {
|
|
47731
|
+
return tokens.some((token, index) => token.value === "export" && tokens[index + 1]?.value === "default");
|
|
47732
|
+
}
|
|
47733
|
+
function hasModuleExports(tokens) {
|
|
47734
|
+
return tokens.some((token, index) => {
|
|
47735
|
+
if (token.kind !== "identifier" || token.value !== "module")
|
|
47736
|
+
return false;
|
|
47737
|
+
const property = tokens[index + 2];
|
|
47738
|
+
const direct = tokens[index + 1]?.value === "." && property?.kind === "identifier" && property.value === "exports";
|
|
47739
|
+
const computed = tokens[index + 1]?.value === "[" && property?.kind === "string" && property.value === "exports" && tokens[index + 3]?.value === "]";
|
|
47740
|
+
const assignment = index + (computed ? 4 : 3);
|
|
47741
|
+
return (direct || computed) && tokens[assignment]?.value === "=" && tokens[assignment + 1]?.value !== "=";
|
|
47742
|
+
});
|
|
47743
|
+
}
|
|
47744
|
+
function tanStackPluginContract(tokens, runtime) {
|
|
47745
|
+
const closing = closingBraces(tokens);
|
|
47746
|
+
const imported = hasNamedImport(tokens, "tanstackStart", TANSTACK_START_PLUGIN);
|
|
47747
|
+
if (runtime === "static") {
|
|
47748
|
+
const calls2 = callOptions(tokens, closing, "tanstackStart");
|
|
47749
|
+
return calls2 === null ? null : {
|
|
47750
|
+
plugins: imported && !hasModuleImport(tokens, CLOUDFLARE_PLUGIN),
|
|
47751
|
+
prerender: calls2.some(([start, end]) => nestedPropertyValue(tokens, closing, start, end, "prerender", "enabled") === "true")
|
|
47752
|
+
};
|
|
47753
|
+
}
|
|
47754
|
+
const calls = callOptions(tokens, closing, "cloudflare");
|
|
47755
|
+
return calls === null ? null : {
|
|
47756
|
+
plugins: imported && hasNamedImport(tokens, "cloudflare", CLOUDFLARE_PLUGIN) && calls.some(([start, end]) => nestedPropertyValue(tokens, closing, start, end, "viteEnvironment", "name") === "ssr") && callIndex(tokens, "cloudflare") < callIndex(tokens, "tanstackStart"),
|
|
47757
|
+
prerender: true
|
|
47758
|
+
};
|
|
47759
|
+
}
|
|
47760
|
+
function closingBraces(tokens) {
|
|
47761
|
+
const closing = tokens.map(() => -1);
|
|
47762
|
+
const open5 = [];
|
|
47763
|
+
tokens.forEach((token, index) => {
|
|
47764
|
+
if (token.value === "{")
|
|
47765
|
+
open5.push(index);
|
|
47766
|
+
if (token.value !== "}")
|
|
47767
|
+
return;
|
|
47768
|
+
const start = open5.pop();
|
|
47769
|
+
if (start !== void 0)
|
|
47770
|
+
closing[start] = index;
|
|
47771
|
+
});
|
|
47772
|
+
return closing;
|
|
47773
|
+
}
|
|
47774
|
+
function callOptions(tokens, closing, name) {
|
|
47775
|
+
const calls = [];
|
|
47776
|
+
for (let index = 0; index < tokens.length - 2; index += 1) {
|
|
47777
|
+
if (tokens[index]?.value !== name || tokens[index + 1]?.value !== "(" || tokens[index + 2]?.value !== "{")
|
|
47778
|
+
continue;
|
|
47779
|
+
const end = closing[index + 2] ?? -1;
|
|
47780
|
+
if (end === -1)
|
|
47781
|
+
return null;
|
|
47782
|
+
calls.push([index + 2, end]);
|
|
47783
|
+
}
|
|
47784
|
+
return calls;
|
|
47785
|
+
}
|
|
47786
|
+
function nestedPropertyValue(tokens, closing, start, end, outer, inner) {
|
|
47787
|
+
const object3 = directProperty(tokens, closing, start, end, outer);
|
|
47788
|
+
if (object3?.value !== "{")
|
|
47789
|
+
return null;
|
|
47790
|
+
return directProperty(tokens, closing, object3.start, object3.end, inner)?.value ?? null;
|
|
47791
|
+
}
|
|
47792
|
+
function directProperty(tokens, closing, start, end, name) {
|
|
47793
|
+
let depth = 0;
|
|
47794
|
+
for (let index = start + 1; index < end; index += 1) {
|
|
47795
|
+
const value = tokens[index]?.value;
|
|
47796
|
+
if (value === "{") {
|
|
47797
|
+
index = Math.max(index, closing[index] ?? -1);
|
|
47798
|
+
continue;
|
|
47799
|
+
}
|
|
47800
|
+
if (value === "[" || value === "(")
|
|
47801
|
+
depth += 1;
|
|
47802
|
+
if (value === "]" || value === ")")
|
|
47803
|
+
depth -= 1;
|
|
47804
|
+
if (depth !== 0 || value !== name || tokens[index + 1]?.value !== ":")
|
|
47805
|
+
continue;
|
|
47806
|
+
const candidate = tokens[index + 2]?.value ?? "";
|
|
47807
|
+
const close = candidate === "{" ? closing[index + 2] ?? -1 : index + 2;
|
|
47808
|
+
return { start: index + 2, end: close, value: candidate };
|
|
47809
|
+
}
|
|
47810
|
+
return null;
|
|
47811
|
+
}
|
|
47812
|
+
function callIndex(tokens, name) {
|
|
47813
|
+
return tokens.findIndex((token, index) => token.value === name && tokens[index + 1]?.value === "(");
|
|
47814
|
+
}
|
|
47815
|
+
function hasNamedImport(tokens, name, moduleName) {
|
|
47816
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
47817
|
+
if (tokens[index]?.value !== "import")
|
|
47818
|
+
continue;
|
|
47819
|
+
let from = index + 1;
|
|
47820
|
+
while (from < tokens.length && tokens[from]?.value !== "from")
|
|
47821
|
+
from += 1;
|
|
47822
|
+
if (from === tokens.length)
|
|
47823
|
+
return false;
|
|
47824
|
+
const source = tokens[from + 1];
|
|
47825
|
+
if (source?.kind === "string" && source.value === moduleName && tokens.slice(index + 1, from).some((token) => token.value === name))
|
|
47826
|
+
return true;
|
|
47827
|
+
index = from;
|
|
47828
|
+
}
|
|
47829
|
+
return false;
|
|
47830
|
+
}
|
|
47831
|
+
function hasModuleImport(tokens, moduleName) {
|
|
47832
|
+
return tokens.some((token, index) => token.value === "from" && tokens[index + 1]?.kind === "string" && tokens[index + 1]?.value === moduleName);
|
|
47833
|
+
}
|
|
47834
|
+
|
|
46756
47835
|
// packages/contracts/dist/database-migration-errors.js
|
|
46757
47836
|
var DatabaseMigrationAdmissionError = class extends Error {
|
|
46758
47837
|
reason;
|
|
@@ -47117,28 +48196,31 @@ function admitStatement(tokens, sql) {
|
|
|
47117
48196
|
// apps/product-cli/dist/repository-init.js
|
|
47118
48197
|
var MAXIMUM_PACKAGE_BYTES = 1024 * 1024;
|
|
47119
48198
|
var MAXIMUM_CONFIGURATION_BYTES = 64 * 1024;
|
|
48199
|
+
var MAXIMUM_INSPECTED_FILE_BYTES = 5 * 1024 * 1024;
|
|
47120
48200
|
var MAXIMUM_REPOSITORY_FILES = 2e4;
|
|
47121
|
-
var SUPPORTED_BETTER_AUTH_VERSION = "1.7.1";
|
|
47122
48201
|
var PROJECT_PATTERN = /^[a-z](?:[a-z0-9-]*[a-z0-9])?$/u;
|
|
47123
48202
|
var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
47124
48203
|
".git",
|
|
47125
48204
|
".next",
|
|
48205
|
+
".open-next",
|
|
47126
48206
|
".supabase",
|
|
47127
48207
|
".turbo",
|
|
47128
48208
|
".vercel",
|
|
48209
|
+
".wrangler",
|
|
47129
48210
|
"coverage",
|
|
47130
48211
|
"dist",
|
|
47131
48212
|
"node_modules"
|
|
47132
48213
|
]);
|
|
47133
|
-
var
|
|
48214
|
+
var UTF8 = new TextDecoder("utf-8", { fatal: true });
|
|
47134
48215
|
var MAXIMUM_SOURCE_BYTES = 2 * 1024 * 1024;
|
|
47135
48216
|
var SOURCE_MODULE_PATTERN = /\.(?:[cm]?[jt]s|[jt]sx)$/u;
|
|
47136
48217
|
var STORAGE_CLIENT_CALL_PATTERN = /\bcreatePrivateStorageClient\s*\(/u;
|
|
47137
|
-
var
|
|
47138
|
-
|
|
47139
|
-
|
|
48218
|
+
var UNNAMED_MODULE_MESSAGES = Object.freeze({
|
|
48219
|
+
database_binding_private: 'The module at path reads a database connection string (DATABASE_URL, HYPERDRIVE, connectionString or a postgres:// URL); none reaches a customer Worker. Call the database with createPrivateDatabaseClient from "@ohmyhost/customer-runtime/database". Commit and push the change, then plan the new commit.',
|
|
48220
|
+
database_driver_unsupported: 'The module at path imports a database socket driver (pg, postgres, pg-native or mysql2); customer Workers cannot open sockets. Call the database with createPrivateDatabaseClient from "@ohmyhost/customer-runtime/database". Commit and push the change, then plan the new commit.'
|
|
48221
|
+
});
|
|
47140
48222
|
function callsStorageClient(source) {
|
|
47141
|
-
return STORAGE_CLIENT_CALL_PATTERN.test(source
|
|
48223
|
+
return STORAGE_CLIENT_CALL_PATTERN.test(sourceWithoutComments(source));
|
|
47142
48224
|
}
|
|
47143
48225
|
var STORAGE_RUNTIME_BINDINGS = Object.freeze([
|
|
47144
48226
|
"OHMYHOST_ENVIRONMENT_ID",
|
|
@@ -47148,14 +48230,13 @@ var STORAGE_RUNTIME_BINDINGS = Object.freeze([
|
|
|
47148
48230
|
"OHMYHOST_STORAGE_KEY"
|
|
47149
48231
|
]);
|
|
47150
48232
|
var CUSTOMER_RUNTIME_SOURCE = `npm:@amerged/ohmyhost-runtime@${package_default.version}`;
|
|
47151
|
-
var
|
|
47152
|
-
|
|
47153
|
-
|
|
47154
|
-
|
|
47155
|
-
|
|
47156
|
-
|
|
47157
|
-
|
|
47158
|
-
}
|
|
48233
|
+
var PENDING_CHECKS = Object.freeze({
|
|
48234
|
+
installed_version: "compatibility.frameworks lists a version range that no lockfile ohmyho.st reads (package-lock.json v2/v3 or pnpm-lock.yaml v6/v9) resolves. Planning admits it; after the frozen install, the build checks the installed version against the same security minimum.",
|
|
48235
|
+
frozen_install: "The build installs dependencies from the committed lockfile with the frozen install of the packageManager in package.json, without lifecycle scripts; init installs nothing.",
|
|
48236
|
+
framework_build: "The build itself (scripts.build with the ohmyho.st adapter build, or the Worker bundle of the functions runtime) runs only on ohmyho.st; a clean init does not prove that it succeeds.",
|
|
48237
|
+
reachable_runtime_sources: "Planning walks the runtime sources the build reaches for Workers-incompatible imports, database socket drivers and syntax errors; init does not walk them.",
|
|
48238
|
+
project_state: "Planning also checks the project itself, such as its region against storage.jurisdiction, shared data, the mail sender domain and credits; init runs offline without it."
|
|
48239
|
+
});
|
|
47159
48240
|
var RepositoryInitError = class extends Error {
|
|
47160
48241
|
code;
|
|
47161
48242
|
constructor(code, message) {
|
|
@@ -47186,18 +48267,15 @@ async function initializeRepository(input) {
|
|
|
47186
48267
|
const applicationDirectory = resolve5(root, selection.applicationRoot);
|
|
47187
48268
|
const applicationFiles = applicationRelativeFiles(files, selection.applicationRoot);
|
|
47188
48269
|
const packageJson = await readPackageJson(applicationDirectory);
|
|
47189
|
-
const
|
|
47190
|
-
const
|
|
47191
|
-
const
|
|
48270
|
+
const manifest2 = packageJson.manifest;
|
|
48271
|
+
const dependencies = declaredDependencyNames(manifest2);
|
|
48272
|
+
const existing2 = await readExistingConfiguration(root, files, selection.applicationRoot);
|
|
48273
|
+
const existingConfiguration = existing2?.configuration ?? null;
|
|
47192
48274
|
const framework = detectRepositoryFramework(dependencies, applicationFiles, existingConfiguration);
|
|
47193
|
-
const packageManagerAdmission = packageManagerAdmissionFor(
|
|
47194
|
-
const packageManager = packageManagerInventory(packageManagerAdmission,
|
|
47195
|
-
const
|
|
47196
|
-
const
|
|
47197
|
-
const nextStandaloneOutput = await detectNextStandaloneOutput(applicationDirectory, framework, frameworkConfigs.next);
|
|
47198
|
-
const tanStackRuntime = await detectTanStackRuntime(applicationDirectory, applicationFiles, framework, packageJson, frameworkConfigs.vite);
|
|
47199
|
-
const project = projectName(input.project, packageJson);
|
|
47200
|
-
const repositoryAdmissionBlockers = repositoryBlockers(packageJson, framework, nextStandaloneOutput, tanStackRuntime, applicationFiles, packageManagerAdmission, frameworkConfigs, compatibility, existingConfiguration);
|
|
48275
|
+
const packageManagerAdmission = packageManagerAdmissionFor(manifest2, applicationFiles);
|
|
48276
|
+
const packageManager = packageManagerInventory(packageManagerAdmission, manifest2, applicationFiles);
|
|
48277
|
+
const frameworkConfig = await readFrameworkConfig(applicationDirectory, applicationFiles, framework);
|
|
48278
|
+
const project = projectName(input.project, manifest2);
|
|
47201
48279
|
const supabaseMigrationCount = applicationFiles.filter((path2) => /^supabase\/migrations\/[^/]+\.sql$/u.test(path2)).length;
|
|
47202
48280
|
const postgresMigrationRoot = existingConfiguration?.database?.migrations ?? "postgres/migrations";
|
|
47203
48281
|
const postgresMigrationCount = applicationFiles.filter((path2) => path2.startsWith(`${postgresMigrationRoot}/`) && path2.endsWith(".sql")).length;
|
|
@@ -47245,8 +48323,8 @@ async function initializeRepository(input) {
|
|
|
47245
48323
|
const targetBetterAuth = existingConfiguration?.auth?.provider === "better-auth";
|
|
47246
48324
|
const targetMail = existingConfiguration?.mail?.enabled === true;
|
|
47247
48325
|
const targetStorage = r2 || supabaseStorage;
|
|
47248
|
-
const
|
|
47249
|
-
const companionRequired = framework === "vite" && (existingConfiguration === null ? databaseProvider !== "none" || targetBetterAuth || targetMail || targetStorage || edgeFunctionNames.size > 0 || applicationFiles.includes(
|
|
48326
|
+
const tanStackRuntime = framework !== "tanstack-start" ? null : targetStorage ? "edge" : proposedTanStackRuntime(frameworkConfig, dependencies);
|
|
48327
|
+
const companionRequired = framework === "vite" && (existingConfiguration === null ? databaseProvider !== "none" || targetBetterAuth || targetMail || targetStorage || edgeFunctionNames.size > 0 || applicationFiles.includes(VITE_COMPANION_ENTRY_PATH) : viteCompanionRequired(existingConfiguration));
|
|
47250
48328
|
const generatedConfigurationYaml = renderConfiguration({
|
|
47251
48329
|
project,
|
|
47252
48330
|
framework,
|
|
@@ -47261,7 +48339,7 @@ async function initializeRepository(input) {
|
|
|
47261
48339
|
tanStackRuntime,
|
|
47262
48340
|
applicationRoot: selection.applicationRoot
|
|
47263
48341
|
});
|
|
47264
|
-
const configurationYaml =
|
|
48342
|
+
const configurationYaml = existing2?.yaml ?? generatedConfigurationYaml;
|
|
47265
48343
|
const capabilities = Object.freeze({
|
|
47266
48344
|
database: selectedDatabaseProvider !== "none",
|
|
47267
48345
|
auth: targetBetterAuth,
|
|
@@ -47270,16 +48348,19 @@ async function initializeRepository(input) {
|
|
|
47270
48348
|
});
|
|
47271
48349
|
const companion = companionRequired ? viteCompanionScaffold(applicationFiles, capabilities) : null;
|
|
47272
48350
|
const companionSourcePresent = companion !== null && applicationFiles.includes(companion.sourceEntryPoint);
|
|
47273
|
-
const
|
|
47274
|
-
|
|
47275
|
-
|
|
47276
|
-
|
|
47277
|
-
|
|
47278
|
-
|
|
47279
|
-
|
|
47280
|
-
|
|
47281
|
-
|
|
47282
|
-
|
|
48351
|
+
const { blockers, versions } = existing2 !== null && existing2.blocker !== null ? { blockers: [existing2.blocker], versions: [] } : await judgeSource({
|
|
48352
|
+
framework,
|
|
48353
|
+
config: existingConfiguration,
|
|
48354
|
+
applicationDirectory,
|
|
48355
|
+
files: applicationFiles,
|
|
48356
|
+
applicationRoot: selection.applicationRoot,
|
|
48357
|
+
provider: selectedDatabaseProvider,
|
|
48358
|
+
texts: sourceTexts(files, selection.applicationRoot, configurationYaml, [
|
|
48359
|
+
["package.json", packageJson.text],
|
|
48360
|
+
...lockfileTexts(await readResolvingLockfile(applicationDirectory, packageManagerAdmission)),
|
|
48361
|
+
...frameworkConfig === null ? [] : [[frameworkConfig.path, frameworkConfig.text]]
|
|
48362
|
+
])
|
|
48363
|
+
});
|
|
47283
48364
|
const requirements = requirementsFor(inventory, companion !== null && !companionSourcePresent, mailDetected, targetBetterAuth);
|
|
47284
48365
|
const configurationSha256 = createHash4("sha256").update(configurationYaml).digest("hex");
|
|
47285
48366
|
if (!input.dryRun) {
|
|
@@ -47296,8 +48377,17 @@ async function initializeRepository(input) {
|
|
|
47296
48377
|
applicationRoot: selection.applicationRoot,
|
|
47297
48378
|
applicationRootSource: selection.source,
|
|
47298
48379
|
inventory,
|
|
47299
|
-
compatibility
|
|
48380
|
+
compatibility: Object.freeze({
|
|
48381
|
+
frameworks: Object.freeze(versions.map((version2) => Object.freeze({
|
|
48382
|
+
package: version2.package,
|
|
48383
|
+
declared: version2.declared,
|
|
48384
|
+
resolved: version2.resolved,
|
|
48385
|
+
status: version2.status,
|
|
48386
|
+
minimum: version2.minimum
|
|
48387
|
+
})))
|
|
48388
|
+
}),
|
|
47300
48389
|
blockers: Object.freeze(blockers),
|
|
48390
|
+
pending: pendingChecks(versions),
|
|
47301
48391
|
requirements: Object.freeze(requirements),
|
|
47302
48392
|
companion,
|
|
47303
48393
|
worker: framework === "functions" ? Object.freeze({
|
|
@@ -47309,6 +48399,20 @@ async function initializeRepository(input) {
|
|
|
47309
48399
|
runtime: Object.values(capabilities).some(Boolean) ? runtimeContract(capabilities) : null
|
|
47310
48400
|
});
|
|
47311
48401
|
}
|
|
48402
|
+
async function judgeSource(context) {
|
|
48403
|
+
const source = repositorySourceFindings(context.texts);
|
|
48404
|
+
return {
|
|
48405
|
+
blockers: [
|
|
48406
|
+
...migrationFilenameBlockers(context, context.provider),
|
|
48407
|
+
...source.findings.map((finding) => requiredCatalogBlocker(finding, context.applicationRoot)),
|
|
48408
|
+
...await workerModuleBlockers(context),
|
|
48409
|
+
...await databaseContractBlockers(context, context.provider),
|
|
48410
|
+
...await storageClientBlockers(context),
|
|
48411
|
+
...await migrationSqlBlockers(context, context.provider)
|
|
48412
|
+
],
|
|
48413
|
+
versions: source.versions
|
|
48414
|
+
};
|
|
48415
|
+
}
|
|
47312
48416
|
function detectRepositoryFramework(dependencies, files, existingConfiguration) {
|
|
47313
48417
|
const detected = detectFramework(dependencies);
|
|
47314
48418
|
if (existingConfiguration?.runtime.mode === "functions")
|
|
@@ -47317,41 +48421,47 @@ function detectRepositoryFramework(dependencies, files, existingConfiguration) {
|
|
|
47317
48421
|
return "functions";
|
|
47318
48422
|
return detected;
|
|
47319
48423
|
}
|
|
47320
|
-
|
|
47321
|
-
|
|
47322
|
-
|
|
48424
|
+
function sourceTexts(files, applicationRoot, configurationYaml, applicationTexts) {
|
|
48425
|
+
const texts = new Map(files.map((path2) => [path2, null]));
|
|
48426
|
+
texts.set("ohmyhost.yaml", configurationYaml);
|
|
48427
|
+
for (const [path2, text] of applicationTexts)
|
|
48428
|
+
texts.set(repositoryPath(applicationRoot, path2), text);
|
|
48429
|
+
return texts;
|
|
48430
|
+
}
|
|
48431
|
+
function lockfileTexts(lockfile) {
|
|
48432
|
+
return lockfile === null ? [] : [[lockfile.name, lockfile.text]];
|
|
48433
|
+
}
|
|
48434
|
+
function repositoryPath(applicationRoot, path2) {
|
|
48435
|
+
return applicationRoot === "." ? path2 : `${applicationRoot}/${path2}`;
|
|
48436
|
+
}
|
|
48437
|
+
function catalogBlocker(diagnostic2, applicationRoot) {
|
|
48438
|
+
const rooted = rootFrameworkConversionDiagnostic(diagnostic2, applicationRoot);
|
|
48439
|
+
const message = frameworkConversionDetail(rooted);
|
|
48440
|
+
return rooted === null || message === null ? null : Object.freeze({ code: rooted.code, path: rooted.path, message });
|
|
48441
|
+
}
|
|
48442
|
+
function requiredCatalogBlocker(diagnostic2, applicationRoot) {
|
|
48443
|
+
const blocker = catalogBlocker(diagnostic2, applicationRoot);
|
|
48444
|
+
if (blocker === null)
|
|
48445
|
+
throw new TypeError("Repository init diagnostic is invalid");
|
|
48446
|
+
return blocker;
|
|
48447
|
+
}
|
|
48448
|
+
async function workerModuleBlockers(context) {
|
|
48449
|
+
const { config, framework, files } = context;
|
|
47323
48450
|
const mode = config?.runtime.mode;
|
|
47324
|
-
const
|
|
47325
|
-
|
|
47326
|
-
|
|
47327
|
-
|
|
47328
|
-
|
|
47329
|
-
|
|
47330
|
-
}
|
|
47331
|
-
];
|
|
47332
|
-
}
|
|
47333
|
-
const modulePath = framework === "vite" ? "src/ohmyhost/companion.ts" : WORKER_MODULE_PATH;
|
|
47334
|
-
const source = files.includes(modulePath) ? await readFile3(resolve5(applicationDirectory, modulePath), "utf8") : null;
|
|
47335
|
-
if (framework !== "functions" && source !== null && !DEFAULT_EXPORT_PATTERN.test(source)) {
|
|
47336
|
-
return [defaultExportBlocker(modulePath)];
|
|
47337
|
-
}
|
|
47338
|
-
if (source === null || !SCHEDULED_HANDLER_PATTERN.test(source)) {
|
|
47339
|
-
return [
|
|
47340
|
-
{
|
|
47341
|
-
code: "scheduled_handler_missing",
|
|
47342
|
-
message: `Keep a scheduled handler in the default export of ${modulePath} to run the declared crons.`
|
|
47343
|
-
}
|
|
47344
|
-
];
|
|
47345
|
-
}
|
|
47346
|
-
return [];
|
|
48451
|
+
const scheduled = (config?.functions?.crons.length ?? 0) > 0 && (mode === "edge" && (framework === "vite" || framework === "nextjs" || framework === "tanstack-start") || mode === "functions" && framework === "functions");
|
|
48452
|
+
const path2 = framework === "vite" ? VITE_COMPANION_ENTRY_PATH : WORKER_MODULE_PATH;
|
|
48453
|
+
if (framework !== "functions" && !scheduled || !files.includes(path2))
|
|
48454
|
+
return [];
|
|
48455
|
+
const source = await readFile3(resolve5(context.applicationDirectory, path2), "utf8");
|
|
48456
|
+
return workerModuleFindings({ path: path2, source, fetch: framework === "functions", scheduled }).map((finding) => requiredCatalogBlocker(finding, context.applicationRoot));
|
|
47347
48457
|
}
|
|
47348
|
-
async function storageClientBlockers(
|
|
47349
|
-
if (config?.storage === void 0)
|
|
48458
|
+
async function storageClientBlockers(context) {
|
|
48459
|
+
if (context.config?.storage === void 0)
|
|
47350
48460
|
return [];
|
|
47351
|
-
for (const path2 of files) {
|
|
48461
|
+
for (const path2 of context.files) {
|
|
47352
48462
|
if (!SOURCE_MODULE_PATTERN.test(path2))
|
|
47353
48463
|
continue;
|
|
47354
|
-
const absolute = resolve5(applicationDirectory, path2);
|
|
48464
|
+
const absolute = resolve5(context.applicationDirectory, path2);
|
|
47355
48465
|
const information = await stat2(absolute);
|
|
47356
48466
|
if (!information.isFile() || information.size > MAXIMUM_SOURCE_BYTES)
|
|
47357
48467
|
continue;
|
|
@@ -47361,52 +48471,41 @@ async function storageClientBlockers(config, applicationDirectory, files) {
|
|
|
47361
48471
|
return [
|
|
47362
48472
|
{
|
|
47363
48473
|
code: "storage_client_missing",
|
|
48474
|
+
path: null,
|
|
47364
48475
|
message: 'ohmyhost.yaml declares storage, but no application source calls createPrivateStorageClient. Import it from "@ohmyhost/customer-runtime/storage" and build it in server code from OHMYHOST_STORAGE_GATEWAY, OHMYHOST_STORAGE_GATEWAY_URL, OHMYHOST_PROJECT_ID, OHMYHOST_ENVIRONMENT_ID and OHMYHOST_STORAGE_KEY, then reserve an upload, PUT the bytes to the returned signed URL, complete it, and serve reads with createSignedRead. There is no FILES bucket binding.'
|
|
47365
48476
|
}
|
|
47366
48477
|
];
|
|
47367
48478
|
}
|
|
47368
|
-
async function databaseContractBlockers(
|
|
47369
|
-
if (
|
|
48479
|
+
async function databaseContractBlockers(context, provider) {
|
|
48480
|
+
if (provider === "none")
|
|
47370
48481
|
return [];
|
|
47371
48482
|
const blockers = [];
|
|
47372
|
-
for (const path2 of files) {
|
|
47373
|
-
if (!
|
|
48483
|
+
for (const path2 of context.files) {
|
|
48484
|
+
if (!isDatabaseContractModule(path2))
|
|
47374
48485
|
continue;
|
|
47375
|
-
const absolute = resolve5(applicationDirectory, path2);
|
|
48486
|
+
const absolute = resolve5(context.applicationDirectory, path2);
|
|
47376
48487
|
const information = await stat2(absolute);
|
|
47377
48488
|
if (!information.isFile() || information.size > MAXIMUM_SOURCE_BYTES)
|
|
47378
48489
|
continue;
|
|
47379
|
-
const
|
|
47380
|
-
|
|
47381
|
-
|
|
47382
|
-
|
|
47383
|
-
message:
|
|
47384
|
-
});
|
|
47385
|
-
}
|
|
47386
|
-
if (DATABASE_SOCKET_DRIVER_PATTERN.test(source)) {
|
|
47387
|
-
blockers.push({
|
|
47388
|
-
code: "database_driver_unsupported",
|
|
47389
|
-
message: `${path2} imports a PostgreSQL socket driver. Outbound connect() is disabled for customer Workers, so the driver can never open a connection. Call the database with createPrivateDatabaseClient from "@ohmyhost/customer-runtime/database".`
|
|
47390
|
-
});
|
|
47391
|
-
}
|
|
48490
|
+
for (const code of databaseContractCodes(await readFile3(absolute, "utf8")))
|
|
48491
|
+
blockers.push(catalogBlocker({ code, path: path2 }, context.applicationRoot) ?? Object.freeze({
|
|
48492
|
+
code,
|
|
48493
|
+
path: repositoryPath(context.applicationRoot, path2),
|
|
48494
|
+
message: UNNAMED_MODULE_MESSAGES[code]
|
|
48495
|
+
}));
|
|
47392
48496
|
}
|
|
47393
48497
|
return blockers;
|
|
47394
48498
|
}
|
|
47395
|
-
|
|
47396
|
-
if (framework !== "functions" || !files.includes(WORKER_MODULE_PATH))
|
|
47397
|
-
return [];
|
|
47398
|
-
const source = await readFile3(resolve5(applicationDirectory, WORKER_MODULE_PATH), "utf8");
|
|
47399
|
-
return DEFAULT_EXPORT_PATTERN.test(source) ? [] : [defaultExportBlocker(WORKER_MODULE_PATH)];
|
|
47400
|
-
}
|
|
47401
|
-
function migrationFilenameBlockers(files, configuredPath, provider) {
|
|
48499
|
+
function migrationFilenameBlockers(context, provider) {
|
|
47402
48500
|
if (provider !== "postgresql")
|
|
47403
48501
|
return [];
|
|
47404
|
-
const root =
|
|
48502
|
+
const root = context.config?.database?.migrations ?? "postgres/migrations";
|
|
47405
48503
|
const prefix = `${root}/`;
|
|
47406
|
-
const invalid6 = files.filter((path2) => path2.startsWith(prefix)).map((path2) => path2.slice(prefix.length)).find((path2) => !/^(?:[A-Za-z0-9][A-Za-z0-9._-]*\/)*\d{14}_[a-z0-9][a-z0-9_-]*\.sql$/u.test(path2));
|
|
48504
|
+
const invalid6 = context.files.filter((path2) => path2.startsWith(prefix)).map((path2) => path2.slice(prefix.length)).find((path2) => !/^(?:[A-Za-z0-9][A-Za-z0-9._-]*\/)*\d{14}_[a-z0-9][a-z0-9_-]*\.sql$/u.test(path2));
|
|
47407
48505
|
return invalid6 === void 0 ? [] : [
|
|
47408
48506
|
{
|
|
47409
48507
|
code: "migration_filename_noncanonical",
|
|
48508
|
+
path: repositoryPath(context.applicationRoot, `${prefix}${invalid6}`),
|
|
47410
48509
|
message: `Migration file '${invalid6}' in ${root} is not named YYYYMMDDHHMMSS_name.sql (14 digits, an underscore, then lowercase letters, digits, _ or -). Every file in that directory must match, README and .gitkeep included: rename or move it, keeping the run order.`
|
|
47411
48510
|
}
|
|
47412
48511
|
];
|
|
@@ -47417,18 +48516,18 @@ var MIGRATION_REJECTION = {
|
|
|
47417
48516
|
unsupported: "It has a statement other than CREATE TABLE, TYPE, DOMAIN, SEQUENCE, VIEW, MATERIALIZED VIEW, TRIGGER, INDEX or FUNCTION, ALTER TABLE ... ADD, COMMENT ON, CREATE SCHEMA IF NOT EXISTS auth|extensions|private or CREATE EXTENSION IF NOT EXISTS pgcrypto|btree_gist|unaccent; or a function not in LANGUAGE sql or plpgsql, SECURITY DEFINER without SET search_path, or the word ohmyhost, comments included.",
|
|
47418
48517
|
invalid: "It is empty, not UTF-8 with LF line endings, has a BOM or NUL byte or an unterminated quote or comment, or exceeds 256 KiB (at most 128 files and 2 MiB in total)."
|
|
47419
48518
|
};
|
|
47420
|
-
async function migrationSqlBlockers(
|
|
48519
|
+
async function migrationSqlBlockers(context, provider) {
|
|
47421
48520
|
if (provider !== "postgresql")
|
|
47422
48521
|
return [];
|
|
47423
|
-
const root =
|
|
48522
|
+
const root = context.config?.database?.migrations ?? "postgres/migrations";
|
|
47424
48523
|
const prefix = `${root}/`;
|
|
47425
|
-
const migrationPaths = files.filter((path2) => path2.startsWith(prefix) && path2.endsWith(".sql")).map((path2) => path2.slice(prefix.length));
|
|
48524
|
+
const migrationPaths = context.files.filter((path2) => path2.startsWith(prefix) && path2.endsWith(".sql")).map((path2) => path2.slice(prefix.length));
|
|
47426
48525
|
if (migrationPaths.some((path2) => !/^(?:[A-Za-z0-9][A-Za-z0-9._-]*\/)*\d{14}_[a-z0-9][a-z0-9_-]*\.sql$/u.test(path2))) {
|
|
47427
48526
|
return [];
|
|
47428
48527
|
}
|
|
47429
48528
|
const migrations = await Promise.all(migrationPaths.map(async (path2) => Object.freeze({
|
|
47430
48529
|
path: path2,
|
|
47431
|
-
body: new Uint8Array(await readFile3(resolve5(applicationDirectory, root, path2)))
|
|
48530
|
+
body: new Uint8Array(await readFile3(resolve5(context.applicationDirectory, root, path2)))
|
|
47432
48531
|
})));
|
|
47433
48532
|
const blockers = [];
|
|
47434
48533
|
for (const migration of migrations) {
|
|
@@ -47439,6 +48538,7 @@ async function migrationSqlBlockers(applicationDirectory, files, configuredPath,
|
|
|
47439
48538
|
throw error;
|
|
47440
48539
|
blockers.push({
|
|
47441
48540
|
code: "migration_sql_not_admitted",
|
|
48541
|
+
path: repositoryPath(context.applicationRoot, `${prefix}${migration.path}`),
|
|
47442
48542
|
message: `PostgreSQL migration '${migration.path}' is not admitted (${error.reason}): ${MIGRATION_REJECTION[error.reason]} Migrations are expand-only. Fix the file before its first deployment; never edit a migration a deployment already applied.`
|
|
47443
48543
|
});
|
|
47444
48544
|
}
|
|
@@ -47454,11 +48554,22 @@ async function migrationSqlBlockers(applicationDirectory, files, configuredPath,
|
|
|
47454
48554
|
return [
|
|
47455
48555
|
{
|
|
47456
48556
|
code: "migration_sql_not_admitted",
|
|
48557
|
+
path: null,
|
|
47457
48558
|
message: `The migration set is not admitted (${error.reason}): ${MIGRATION_REJECTION[error.reason]}`
|
|
47458
48559
|
}
|
|
47459
48560
|
];
|
|
47460
48561
|
}
|
|
47461
48562
|
}
|
|
48563
|
+
function pendingChecks(versions) {
|
|
48564
|
+
const codes = [
|
|
48565
|
+
...versions.some(({ status }) => status === "pending") ? ["installed_version"] : [],
|
|
48566
|
+
"frozen_install",
|
|
48567
|
+
"framework_build",
|
|
48568
|
+
"reachable_runtime_sources",
|
|
48569
|
+
"project_state"
|
|
48570
|
+
];
|
|
48571
|
+
return Object.freeze(codes.map((code) => Object.freeze({ code, message: PENDING_CHECKS[code] })));
|
|
48572
|
+
}
|
|
47462
48573
|
async function readExistingConfiguration(repositoryRoot, repositoryFiles, applicationRoot) {
|
|
47463
48574
|
if (!repositoryFiles.includes("ohmyhost.yaml"))
|
|
47464
48575
|
return null;
|
|
@@ -47467,17 +48578,30 @@ async function readExistingConfiguration(repositoryRoot, repositoryFiles, applic
|
|
|
47467
48578
|
if (!information.isFile() || information.size > MAXIMUM_CONFIGURATION_BYTES) {
|
|
47468
48579
|
throw new RepositoryInitError("repository_configuration_invalid", "ohmyhost.yaml must be one regular file of at most 64 KiB");
|
|
47469
48580
|
}
|
|
47470
|
-
const
|
|
48581
|
+
const bytes = await readFile3(configurationPath);
|
|
48582
|
+
const text = utf8(bytes);
|
|
48583
|
+
const yaml = text ?? new TextDecoder().decode(bytes);
|
|
47471
48584
|
let parsed;
|
|
47472
48585
|
try {
|
|
47473
|
-
|
|
48586
|
+
if (text === null)
|
|
48587
|
+
throw new TypeError("ohmyhost.yaml is not valid UTF-8");
|
|
48588
|
+
parsed = parseOhmyhostConfigYaml(text);
|
|
47474
48589
|
} catch (error) {
|
|
47475
|
-
|
|
48590
|
+
const reason = (error instanceof TypeError ? error.message : "ohmyhost.yaml is invalid").replace(/\.$/u, "");
|
|
48591
|
+
return Object.freeze({
|
|
48592
|
+
yaml,
|
|
48593
|
+
configuration: null,
|
|
48594
|
+
blocker: Object.freeze({
|
|
48595
|
+
code: "repository_configuration_invalid",
|
|
48596
|
+
path: "ohmyhost.yaml",
|
|
48597
|
+
message: `${reason}. Fix that in ohmyhost.yaml and run init again; init never rewrites an existing ohmyhost.yaml, so do not delete it.`
|
|
48598
|
+
})
|
|
48599
|
+
});
|
|
47476
48600
|
}
|
|
47477
48601
|
if (parsed.applicationRoot !== applicationRoot) {
|
|
47478
48602
|
throw new RepositoryInitError("repository_configuration_root_mismatch", `ohmyhost.yaml sets applicationRoot to '${parsed.applicationRoot}', but init selected '${applicationRoot}'. Run 'ohmyhost init --root ${parsed.applicationRoot} --dry-run --json', or correct applicationRoot.`);
|
|
47479
48603
|
}
|
|
47480
|
-
return configuration;
|
|
48604
|
+
return Object.freeze({ yaml, configuration: parsed, blocker: null });
|
|
47481
48605
|
}
|
|
47482
48606
|
var APPLICATION_LOCKFILES = Object.freeze([
|
|
47483
48607
|
"bun.lock",
|
|
@@ -47555,7 +48679,7 @@ async function isApplicationCandidate(repositoryRoot, repositoryFiles, applicati
|
|
|
47555
48679
|
const packageJson = await tryReadPackageJson(applicationDirectory);
|
|
47556
48680
|
if (packageJson === null)
|
|
47557
48681
|
return false;
|
|
47558
|
-
const framework = detectFramework(
|
|
48682
|
+
const framework = detectFramework(declaredDependencyNames(packageJson.manifest));
|
|
47559
48683
|
return framework !== "unknown" || files.includes(WORKER_MODULE_PATH);
|
|
47560
48684
|
}
|
|
47561
48685
|
async function hasDirectApplicationShape(repositoryRoot, repositoryFiles, applicationRoot) {
|
|
@@ -47567,10 +48691,10 @@ async function hasDirectApplicationShape(repositoryRoot, repositoryFiles, applic
|
|
|
47567
48691
|
const packageJson = await tryReadPackageJson(applicationDirectory);
|
|
47568
48692
|
if (packageJson === null)
|
|
47569
48693
|
return false;
|
|
47570
|
-
const framework = detectFramework(
|
|
48694
|
+
const framework = detectFramework(declaredDependencyNames(packageJson.manifest));
|
|
47571
48695
|
if (framework === "unknown")
|
|
47572
48696
|
return files.includes(WORKER_MODULE_PATH);
|
|
47573
|
-
return hasDirectSafeBuildCommand(packageJson, framework);
|
|
48697
|
+
return hasDirectSafeBuildCommand(packageJson.manifest, framework);
|
|
47574
48698
|
}
|
|
47575
48699
|
async function isRealDirectory(path2) {
|
|
47576
48700
|
try {
|
|
@@ -47593,18 +48717,12 @@ async function tryReadPackageJson(root) {
|
|
|
47593
48717
|
return null;
|
|
47594
48718
|
}
|
|
47595
48719
|
}
|
|
47596
|
-
function hasDirectSafeBuildCommand(
|
|
47597
|
-
const scripts =
|
|
47598
|
-
if (!
|
|
48720
|
+
function hasDirectSafeBuildCommand(manifest2, framework) {
|
|
48721
|
+
const scripts = manifest2["scripts"];
|
|
48722
|
+
if (!isRecord5(scripts) || typeof scripts["build"] !== "string")
|
|
47599
48723
|
return false;
|
|
47600
48724
|
return (framework === "nextjs" || framework === "vite" || framework === "tanstack-start") && isAdmittedFrameworkBuildScript(framework, scripts["build"]);
|
|
47601
48725
|
}
|
|
47602
|
-
function buildScriptMessage(framework, command) {
|
|
47603
|
-
const quoted = command.slice(0, 200);
|
|
47604
|
-
if (framework === "nextjs")
|
|
47605
|
-
return `package.json scripts.build must be exactly "next build" or "next build --webpack", not "${quoted}". Move other steps into their own scripts, then commit and push.`;
|
|
47606
|
-
return `package.json scripts.build must be "vite build", optionally joined with && to exactly one "tsc", "tsc -b", "tsc --build" or "tsc --noEmit" stage before or after it, not "${quoted}". Move other steps into their own scripts, then commit and push.`;
|
|
47607
|
-
}
|
|
47608
48726
|
async function assertRepositoryRoot(root) {
|
|
47609
48727
|
let information;
|
|
47610
48728
|
try {
|
|
@@ -47676,105 +48794,95 @@ async function readPackageJson(root) {
|
|
|
47676
48794
|
if (!information.isFile() || information.size > MAXIMUM_PACKAGE_BYTES) {
|
|
47677
48795
|
throw new RepositoryInitError("package_manifest_invalid", "package.json is invalid");
|
|
47678
48796
|
}
|
|
48797
|
+
const text = utf8(await readFile3(path2));
|
|
48798
|
+
const manifest2 = text === null ? null : parsePackageManifest(text);
|
|
48799
|
+
if (text === null || manifest2 === null) {
|
|
48800
|
+
throw new RepositoryInitError("package_manifest_invalid", "package.json is invalid");
|
|
48801
|
+
}
|
|
48802
|
+
return Object.freeze({ text, manifest: manifest2 });
|
|
48803
|
+
}
|
|
48804
|
+
function utf8(bytes) {
|
|
47679
48805
|
try {
|
|
47680
|
-
|
|
47681
|
-
if (!isRecord4(value))
|
|
47682
|
-
throw new TypeError();
|
|
47683
|
-
return value;
|
|
48806
|
+
return UTF8.decode(bytes);
|
|
47684
48807
|
} catch {
|
|
47685
|
-
|
|
48808
|
+
return null;
|
|
47686
48809
|
}
|
|
47687
48810
|
}
|
|
47688
|
-
function
|
|
47689
|
-
const
|
|
47690
|
-
|
|
47691
|
-
const
|
|
47692
|
-
|
|
47693
|
-
|
|
47694
|
-
|
|
47695
|
-
|
|
47696
|
-
|
|
47697
|
-
}
|
|
48811
|
+
async function readLeadingBytes(path2) {
|
|
48812
|
+
const handle = await open4(path2, "r");
|
|
48813
|
+
try {
|
|
48814
|
+
const { size } = await handle.stat();
|
|
48815
|
+
const bytes = new Uint8Array(Math.min(size, MAXIMUM_INSPECTED_FILE_BYTES));
|
|
48816
|
+
const { bytesRead } = await handle.read(bytes, 0, bytes.byteLength, 0);
|
|
48817
|
+
return Object.freeze({ bytes: bytes.subarray(0, bytesRead), complete: bytesRead === size });
|
|
48818
|
+
} finally {
|
|
48819
|
+
await handle.close();
|
|
47698
48820
|
}
|
|
47699
|
-
|
|
48821
|
+
}
|
|
48822
|
+
async function readResolvingLockfile(applicationDirectory, admission) {
|
|
48823
|
+
if (admission instanceof FrameworkAdmissionError || admission.lockfile !== "package-lock.json" && admission.lockfile !== "pnpm-lock.yaml")
|
|
48824
|
+
return null;
|
|
48825
|
+
const { bytes, complete } = await readLeadingBytes(resolve5(applicationDirectory, admission.lockfile));
|
|
48826
|
+
const text = complete ? utf8(bytes) : null;
|
|
48827
|
+
return text === null ? null : Object.freeze({ name: admission.lockfile, text });
|
|
48828
|
+
}
|
|
48829
|
+
async function readFrameworkConfig(applicationDirectory, files, framework) {
|
|
48830
|
+
if (framework !== "nextjs" && framework !== "vite" && framework !== "tanstack-start")
|
|
48831
|
+
return null;
|
|
48832
|
+
let path2;
|
|
48833
|
+
try {
|
|
48834
|
+
path2 = resolveFrameworkConfig(framework === "nextjs" ? "next.config" : "vite.config", files);
|
|
48835
|
+
} catch (error) {
|
|
48836
|
+
if (error instanceof FrameworkAdmissionError)
|
|
48837
|
+
return null;
|
|
48838
|
+
throw error;
|
|
48839
|
+
}
|
|
48840
|
+
if (path2 === null)
|
|
48841
|
+
return null;
|
|
48842
|
+
const { bytes, complete } = await readLeadingBytes(resolve5(applicationDirectory, path2));
|
|
48843
|
+
return Object.freeze({ path: path2, text: complete ? utf8(bytes) : new TextDecoder().decode(bytes) });
|
|
48844
|
+
}
|
|
48845
|
+
function proposedTanStackRuntime(config, dependencies) {
|
|
48846
|
+
const followed = config === null || config.text === null ? null : tanStackViteConfigRuntime(config.text);
|
|
48847
|
+
return followed ?? (dependencies.has("@cloudflare/vite-plugin") ? "edge" : "static");
|
|
47700
48848
|
}
|
|
47701
48849
|
function detectFramework(dependencies) {
|
|
47702
48850
|
const classification = classifyFrameworkFamily(dependencies);
|
|
47703
48851
|
return classification === "ambiguous" ? "unknown" : classification;
|
|
47704
48852
|
}
|
|
47705
|
-
function packageManagerAdmissionFor(
|
|
48853
|
+
function packageManagerAdmissionFor(manifest2, files) {
|
|
47706
48854
|
try {
|
|
47707
|
-
return resolvePackageManager({
|
|
47708
|
-
packageManager: typeof packageJson["packageManager"] === "string" ? packageJson["packageManager"] : void 0,
|
|
47709
|
-
files
|
|
47710
|
-
});
|
|
48855
|
+
return resolvePackageManager({ packageManager: packageManagerDeclaration(manifest2), files });
|
|
47711
48856
|
} catch (error) {
|
|
47712
48857
|
if (error instanceof FrameworkAdmissionError)
|
|
47713
48858
|
return error;
|
|
47714
48859
|
throw error;
|
|
47715
48860
|
}
|
|
47716
48861
|
}
|
|
47717
|
-
function packageManagerInventory(admission,
|
|
48862
|
+
function packageManagerInventory(admission, manifest2, files) {
|
|
47718
48863
|
if (!(admission instanceof FrameworkAdmissionError)) {
|
|
47719
48864
|
return {
|
|
47720
48865
|
name: admission.manager,
|
|
47721
48866
|
version: admission.version,
|
|
47722
|
-
lockfile: admission.lockfile
|
|
48867
|
+
lockfile: admission.lockfile,
|
|
48868
|
+
installCommand: admission.installCommand,
|
|
48869
|
+
buildCommand: admission.buildCommand
|
|
47723
48870
|
};
|
|
47724
48871
|
}
|
|
47725
|
-
const declaration =
|
|
47726
|
-
const match =
|
|
48872
|
+
const declaration = packageManagerDeclaration(manifest2);
|
|
48873
|
+
const match = declaration === void 0 ? null : /^(bun|npm|pnpm|yarn)@([^\s]+)$/u.exec(declaration);
|
|
47727
48874
|
const lockfile = APPLICATION_LOCKFILES.find((candidate) => files.includes(candidate)) ?? null;
|
|
47728
48875
|
const inferredName = lockfile === "package-lock.json" ? "npm" : lockfile === "pnpm-lock.yaml" ? "pnpm" : lockfile === "yarn.lock" ? "yarn" : lockfile === "bun.lock" || lockfile === "bun.lockb" ? "bun" : "unknown";
|
|
47729
48876
|
return {
|
|
47730
48877
|
name: match === null ? inferredName : match[1],
|
|
47731
48878
|
version: match?.[2] ?? null,
|
|
47732
|
-
lockfile
|
|
47733
|
-
|
|
47734
|
-
|
|
47735
|
-
function frameworkConfigurations(files) {
|
|
47736
|
-
const ambiguous = [];
|
|
47737
|
-
const resolve7 = (basename2) => {
|
|
47738
|
-
try {
|
|
47739
|
-
return resolveFrameworkConfig(basename2, files);
|
|
47740
|
-
} catch (error) {
|
|
47741
|
-
if (!(error instanceof FrameworkAdmissionError) || error.reason !== "ambiguous_config") {
|
|
47742
|
-
throw error;
|
|
47743
|
-
}
|
|
47744
|
-
ambiguous.push(basename2.slice(0, -".config".length));
|
|
47745
|
-
return null;
|
|
47746
|
-
}
|
|
48879
|
+
lockfile,
|
|
48880
|
+
installCommand: null,
|
|
48881
|
+
buildCommand: null
|
|
47747
48882
|
};
|
|
47748
|
-
return Object.freeze({
|
|
47749
|
-
next: resolve7("next.config"),
|
|
47750
|
-
openNext: resolve7("open-next.config"),
|
|
47751
|
-
vite: resolve7("vite.config"),
|
|
47752
|
-
ambiguous: Object.freeze(ambiguous)
|
|
47753
|
-
});
|
|
47754
48883
|
}
|
|
47755
|
-
|
|
47756
|
-
|
|
47757
|
-
"tanstack-start": "@tanstack/react-start",
|
|
47758
|
-
vite: "vite"
|
|
47759
|
-
};
|
|
47760
|
-
function frameworkCompatibility(packageJson, framework) {
|
|
47761
|
-
const targets = framework === "tanstack-start" ? [
|
|
47762
|
-
{ framework: "tanstack-start", packageName: FRAMEWORK_PACKAGE["tanstack-start"] },
|
|
47763
|
-
{ framework: "vite", packageName: FRAMEWORK_PACKAGE.vite }
|
|
47764
|
-
] : framework === "nextjs" ? [{ framework: "nextjs", packageName: FRAMEWORK_PACKAGE.nextjs }] : framework === "vite" ? [{ framework: "vite", packageName: FRAMEWORK_PACKAGE.vite }] : [];
|
|
47765
|
-
const frameworks = targets.map(({ framework: targetFramework, packageName }) => {
|
|
47766
|
-
const version2 = dependencyVersion(packageJson, packageName) ?? "";
|
|
47767
|
-
return Object.freeze({
|
|
47768
|
-
framework: targetFramework,
|
|
47769
|
-
version: version2,
|
|
47770
|
-
...classifyFrameworkVersion(targetFramework, version2)
|
|
47771
|
-
});
|
|
47772
|
-
});
|
|
47773
|
-
const classification = frameworks.some((entry) => entry.classification === "unsupported") ? "unsupported" : frameworks.some((entry) => entry.classification === "experimental") ? "experimental" : frameworks.length > 0 || framework === "functions" ? "verified" : "unsupported";
|
|
47774
|
-
return Object.freeze({ classification, frameworks: Object.freeze(frameworks) });
|
|
47775
|
-
}
|
|
47776
|
-
function projectName(explicit, packageJson) {
|
|
47777
|
-
const packageName = packageJson["name"];
|
|
48884
|
+
function projectName(explicit, manifest2) {
|
|
48885
|
+
const packageName = manifest2["name"];
|
|
47778
48886
|
const inferred = typeof packageName === "string" ? (packageName.split("/").at(-1) ?? "").toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "") : packageName;
|
|
47779
48887
|
const value = explicit ?? inferred;
|
|
47780
48888
|
if (typeof value !== "string" || value.length > 63 || !PROJECT_PATTERN.test(value)) {
|
|
@@ -47782,117 +48890,6 @@ function projectName(explicit, packageJson) {
|
|
|
47782
48890
|
}
|
|
47783
48891
|
return value;
|
|
47784
48892
|
}
|
|
47785
|
-
function repositoryBlockers(packageJson, framework, nextStandaloneOutput, tanStackRuntime, files, packageManagerAdmission, frameworkConfigs, compatibility, existingConfiguration) {
|
|
47786
|
-
const blockers = [];
|
|
47787
|
-
if (framework !== "nextjs" && existingConfiguration !== null && "command" in existingConfiguration.build && existingConfiguration.build.ssg_cache_max_mib !== void 0) {
|
|
47788
|
-
blockers.push({
|
|
47789
|
-
code: "framework_unsupported",
|
|
47790
|
-
message: "build.ssg_cache_max_mib is supported only by Next.js; remove it from this application's configuration."
|
|
47791
|
-
});
|
|
47792
|
-
}
|
|
47793
|
-
if (framework === "unknown") {
|
|
47794
|
-
blockers.push({
|
|
47795
|
-
code: "framework_unsupported",
|
|
47796
|
-
message: "Declare exactly one supported Vite, TanStack Start, or Next.js framework, or add src/ohmyhost/worker.ts for the functions runtime."
|
|
47797
|
-
});
|
|
47798
|
-
}
|
|
47799
|
-
if (framework === "functions" && !files.includes(WORKER_MODULE_PATH)) {
|
|
47800
|
-
blockers.push({
|
|
47801
|
-
code: "worker_module_missing",
|
|
47802
|
-
message: `The functions runtime needs ${WORKER_MODULE_PATH} exporting fetch and optional scheduled handlers.`
|
|
47803
|
-
});
|
|
47804
|
-
}
|
|
47805
|
-
if (tanStackRuntime === "ambiguous") {
|
|
47806
|
-
blockers.push({
|
|
47807
|
-
code: "framework_ambiguous",
|
|
47808
|
-
message: 'TanStack Start needs one vite.config.js, .mjs or .ts that selects a mode. Static: tanstackStart({ prerender: { enabled: true } }) from "@tanstack/react-start/plugin/vite" and no "@cloudflare/vite-plugin". Server: cloudflare({ viteEnvironment: { name: "ssr" } }) from "@cloudflare/vite-plugin" listed before tanstackStart() in plugins, with @cloudflare/vite-plugin, @tanstack/react-router, @tanstack/react-start, react, react-dom, @vitejs/plugin-react and vite in package.json.'
|
|
47809
|
-
});
|
|
47810
|
-
}
|
|
47811
|
-
const relevantAmbiguousConfigs = frameworkConfigs.ambiguous.filter((config) => framework === "nextjs" ? config === "next" || config === "open-next" : config === "vite");
|
|
47812
|
-
for (const config of relevantAmbiguousConfigs) {
|
|
47813
|
-
blockers.push({
|
|
47814
|
-
code: "framework_ambiguous",
|
|
47815
|
-
message: `Keep exactly one supported ${config}.config.js, ${config}.config.mjs, or ${config}.config.ts file.`
|
|
47816
|
-
});
|
|
47817
|
-
}
|
|
47818
|
-
if (packageManagerAdmission instanceof FrameworkAdmissionError && packageManagerAdmission.reason === "ambiguous_lockfile") {
|
|
47819
|
-
blockers.push({
|
|
47820
|
-
code: "package_manager_ambiguous",
|
|
47821
|
-
message: "Keep exactly one supported lockfile in the application root."
|
|
47822
|
-
});
|
|
47823
|
-
} else if (packageManagerAdmission instanceof FrameworkAdmissionError) {
|
|
47824
|
-
blockers.push({
|
|
47825
|
-
code: "package_manager_unpinned",
|
|
47826
|
-
message: packageManagerAdmission.reason === "package_manager_version_unsupported" ? `This build image runs Bun ${PINNED_BUN_VERSION}; pin packageManager to bun@${PINNED_BUN_VERSION} and commit its matching lockfile. Repeating the unchanged version cannot work.` : "Declare one exact npm, pnpm, Yarn, or Bun packageManager version and commit its matching lockfile."
|
|
47827
|
-
});
|
|
47828
|
-
} else if (existingConfiguration !== null && (existingConfiguration.build.install !== packageManagerAdmission.installCommand || "command" in existingConfiguration.build && existingConfiguration.build.command !== packageManagerAdmission.buildCommand)) {
|
|
47829
|
-
blockers.push({
|
|
47830
|
-
code: "build_command_unsupported",
|
|
47831
|
-
message: `ohmyhost.yaml build.install must be exactly "${packageManagerAdmission.installCommand}"${"command" in existingConfiguration.build ? ` and build.command "${packageManagerAdmission.buildCommand}"` : ""} for the packageManager in package.json. Set them, then commit and push.`
|
|
47832
|
-
});
|
|
47833
|
-
}
|
|
47834
|
-
if (compatibility.classification === "unsupported" && compatibility.frameworks.length > 0) {
|
|
47835
|
-
const unsupported = compatibility.frameworks.filter((entry) => entry.classification === "unsupported").map((entry) => `${FRAMEWORK_PACKAGE[entry.framework]}@${entry.version || "missing"}`).join(", ");
|
|
47836
|
-
blockers.push({
|
|
47837
|
-
code: "framework_version_unsupported",
|
|
47838
|
-
message: `${unsupported} is outside the versions ohmyho.st builds: next 15.5.26+ (15.5.x) or 16.3.8, vite 5.4.0\u20138.2.2, @tanstack/react-start 1.168.26\u20131.168.49, as an exact version or a ^ or ~ range whose base version is inside. Pin a version in that range in package.json, update the lockfile, commit and push.`
|
|
47839
|
-
});
|
|
47840
|
-
}
|
|
47841
|
-
const scripts = packageJson["scripts"];
|
|
47842
|
-
const scriptRecord = isRecord4(scripts) ? scripts : {};
|
|
47843
|
-
if (framework === "functions") {
|
|
47844
|
-
} else if (!isRecord4(scripts) || typeof scripts["build"] !== "string" || scripts["build"].length === 0) {
|
|
47845
|
-
blockers.push({ code: "build_script_missing", message: "Declare a non-empty build script." });
|
|
47846
|
-
} else if (!hasDirectSafeBuildCommand(packageJson, framework)) {
|
|
47847
|
-
blockers.push({
|
|
47848
|
-
code: "build_command_unsupported",
|
|
47849
|
-
message: buildScriptMessage(framework, scripts["build"])
|
|
47850
|
-
});
|
|
47851
|
-
}
|
|
47852
|
-
const betterAuthVersion = dependencyVersion(packageJson, "better-auth");
|
|
47853
|
-
const managedAuthIssue = managedCustomerAuthAdmissionIssue(existingConfiguration);
|
|
47854
|
-
if (managedAuthIssue !== null)
|
|
47855
|
-
blockers.push(managedAuthIssue);
|
|
47856
|
-
if (existingConfiguration?.auth?.provider === "better-auth" && betterAuthVersion !== null && betterAuthVersion !== SUPPORTED_BETTER_AUTH_VERSION) {
|
|
47857
|
-
blockers.push({
|
|
47858
|
-
code: "better_auth_version_unsupported",
|
|
47859
|
-
message: "Pin better-auth to exactly 1.7.1 before enabling portable customer auth."
|
|
47860
|
-
});
|
|
47861
|
-
}
|
|
47862
|
-
if (framework === "nextjs") {
|
|
47863
|
-
const dependencies = isRecord4(packageJson["dependencies"]) ? packageJson["dependencies"] : {};
|
|
47864
|
-
const nextBuild = scriptRecord["build"] === "next build" || scriptRecord["build"] === "next build --webpack";
|
|
47865
|
-
const nextPackageConfigured = nextBuild && compatibility.frameworks[0]?.classification !== "unsupported" && exactVersion(dependencies["react"]) && exactVersion(dependencies["react-dom"]) && frameworkConfigs.next !== null;
|
|
47866
|
-
const nextConfigured = nextPackageConfigured && frameworkConfigs.ambiguous.every((config) => config !== "open-next");
|
|
47867
|
-
if (nextStandaloneOutput) {
|
|
47868
|
-
blockers.push({
|
|
47869
|
-
code: "workers_runtime_incompatible",
|
|
47870
|
-
message: "Next.js output: standalone does not function on Workers. Remove the output setting from the Next.js config, commit the change, then create a new plan."
|
|
47871
|
-
});
|
|
47872
|
-
}
|
|
47873
|
-
if (!nextConfigured) {
|
|
47874
|
-
blockers.push({
|
|
47875
|
-
code: "next_adapter_unconfigured",
|
|
47876
|
-
message: 'Next.js needs scripts.build exactly "next build" or "next build --webpack", react and react-dom at exact versions such as "19.1.0" (no ^ or ~), next within 15.5.26+ (15.5.x) or 16.3.8, and exactly one next.config.js, .mjs or .ts, with at most one open-next.config file. ohmyho.st adds the OpenNext build itself; do not make it the build script.'
|
|
47877
|
-
});
|
|
47878
|
-
}
|
|
47879
|
-
}
|
|
47880
|
-
return blockers.sort((left, right) => left.code.localeCompare(right.code));
|
|
47881
|
-
}
|
|
47882
|
-
function exactVersion(value) {
|
|
47883
|
-
return typeof value === "string" && /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u.test(value);
|
|
47884
|
-
}
|
|
47885
|
-
function dependencyVersion(packageJson, name) {
|
|
47886
|
-
for (const field of ["dependencies", "devDependencies", "optionalDependencies"]) {
|
|
47887
|
-
const dependencies = packageJson[field];
|
|
47888
|
-
if (!isRecord4(dependencies))
|
|
47889
|
-
continue;
|
|
47890
|
-
const version2 = dependencies[name];
|
|
47891
|
-
if (typeof version2 === "string" && version2.length > 0)
|
|
47892
|
-
return version2;
|
|
47893
|
-
}
|
|
47894
|
-
return null;
|
|
47895
|
-
}
|
|
47896
48893
|
function renderConfiguration(input) {
|
|
47897
48894
|
const descriptor = packageManagerDescriptorFromInventory(input.packageManager);
|
|
47898
48895
|
const install = descriptor?.installCommand ?? "pnpm install --frozen-lockfile --ignore-scripts";
|
|
@@ -47949,66 +48946,6 @@ function packageManagerDescriptorFromInventory(packageManager) {
|
|
|
47949
48946
|
return null;
|
|
47950
48947
|
}
|
|
47951
48948
|
}
|
|
47952
|
-
async function detectNextStandaloneOutput(root, framework, nextConfigPath) {
|
|
47953
|
-
if (framework !== "nextjs" || nextConfigPath === null)
|
|
47954
|
-
return false;
|
|
47955
|
-
const path2 = resolve5(root, nextConfigPath);
|
|
47956
|
-
const information = await stat2(path2);
|
|
47957
|
-
if (!information.isFile() || information.size > MAXIMUM_CONFIGURATION_BYTES)
|
|
47958
|
-
return false;
|
|
47959
|
-
return declaresStandaloneOutput(await readFile3(path2, "utf8"));
|
|
47960
|
-
}
|
|
47961
|
-
function declaresStandaloneOutput(source) {
|
|
47962
|
-
const tokens = sourceTokens(source);
|
|
47963
|
-
return tokens.some((token, index) => token === "output" && tokens[index + 1] === ":" && tokens[index + 2] === "standalone");
|
|
47964
|
-
}
|
|
47965
|
-
function sourceTokens(source) {
|
|
47966
|
-
const tokens = [];
|
|
47967
|
-
let index = 0;
|
|
47968
|
-
while (index < source.length) {
|
|
47969
|
-
const character = source[index];
|
|
47970
|
-
const next = source[index + 1];
|
|
47971
|
-
if (character === "/" && next === "/") {
|
|
47972
|
-
const end = source.indexOf("\n", index + 2);
|
|
47973
|
-
index = end === -1 ? source.length : end + 1;
|
|
47974
|
-
continue;
|
|
47975
|
-
}
|
|
47976
|
-
if (character === "/" && next === "*") {
|
|
47977
|
-
const end = source.indexOf("*/", index + 2);
|
|
47978
|
-
if (end === -1)
|
|
47979
|
-
return Object.freeze([]);
|
|
47980
|
-
index = end + 2;
|
|
47981
|
-
continue;
|
|
47982
|
-
}
|
|
47983
|
-
if (character === "'" || character === '"' || character === "`") {
|
|
47984
|
-
let value = "";
|
|
47985
|
-
index += 1;
|
|
47986
|
-
while (index < source.length && source[index] !== character) {
|
|
47987
|
-
if (source[index] === "\\" && source[index + 1] !== void 0)
|
|
47988
|
-
index += 1;
|
|
47989
|
-
value += source[index] ?? "";
|
|
47990
|
-
index += 1;
|
|
47991
|
-
}
|
|
47992
|
-
if (source[index] !== character)
|
|
47993
|
-
return Object.freeze([]);
|
|
47994
|
-
tokens.push(value);
|
|
47995
|
-
index += 1;
|
|
47996
|
-
continue;
|
|
47997
|
-
}
|
|
47998
|
-
if (character !== void 0 && /[A-Za-z_$]/u.test(character)) {
|
|
47999
|
-
const start = index;
|
|
48000
|
-
index += 1;
|
|
48001
|
-
while (/[A-Za-z0-9_$]/u.test(source[index] ?? ""))
|
|
48002
|
-
index += 1;
|
|
48003
|
-
tokens.push(source.slice(start, index));
|
|
48004
|
-
continue;
|
|
48005
|
-
}
|
|
48006
|
-
if (character !== void 0 && "{}[]():,.".includes(character))
|
|
48007
|
-
tokens.push(character);
|
|
48008
|
-
index += 1;
|
|
48009
|
-
}
|
|
48010
|
-
return Object.freeze(tokens);
|
|
48011
|
-
}
|
|
48012
48949
|
function databaseMigrationPath(provider) {
|
|
48013
48950
|
if (provider === "supabase")
|
|
48014
48951
|
return "postgres/migrations";
|
|
@@ -48016,99 +48953,6 @@ function databaseMigrationPath(provider) {
|
|
|
48016
48953
|
return "postgres/migrations";
|
|
48017
48954
|
return null;
|
|
48018
48955
|
}
|
|
48019
|
-
async function detectTanStackRuntime(root, files, framework, packageJson, viteConfigPath) {
|
|
48020
|
-
if (framework !== "tanstack-start")
|
|
48021
|
-
return null;
|
|
48022
|
-
if (viteConfigPath === null)
|
|
48023
|
-
return "ambiguous";
|
|
48024
|
-
const path2 = resolve5(root, viteConfigPath);
|
|
48025
|
-
const information = await stat2(path2);
|
|
48026
|
-
if (!information.isFile() || information.size > MAXIMUM_PACKAGE_BYTES)
|
|
48027
|
-
return "ambiguous";
|
|
48028
|
-
const source = await readFile3(path2, "utf8");
|
|
48029
|
-
const tokens = sourceTokens(source);
|
|
48030
|
-
if (isCompleteTanStackStatic(tokens))
|
|
48031
|
-
return "static";
|
|
48032
|
-
if (isCompleteTanStackEdgePackage(packageJson) && isCompleteTanStackEdgeConfig(tokens)) {
|
|
48033
|
-
return "edge";
|
|
48034
|
-
}
|
|
48035
|
-
return "ambiguous";
|
|
48036
|
-
}
|
|
48037
|
-
function isCompleteTanStackStatic(tokens) {
|
|
48038
|
-
return hasNamedSourceImport(tokens, "tanstackStart", "@tanstack/react-start/plugin/vite") && !hasSourceModuleImport(tokens, "@cloudflare/vite-plugin") && hasNestedObjectValue(tokens, "tanstackStart", "prerender", "enabled", "true");
|
|
48039
|
-
}
|
|
48040
|
-
function isCompleteTanStackEdgePackage(packageJson) {
|
|
48041
|
-
return dependencyVersion(packageJson, "@cloudflare/vite-plugin") !== null && ["@tanstack/react-router", "@tanstack/react-start", "react", "react-dom"].every((name) => dependencyVersion(packageJson, name) !== null) && ["@vitejs/plugin-react", "vite"].every((name) => dependencyVersion(packageJson, name) !== null);
|
|
48042
|
-
}
|
|
48043
|
-
function isCompleteTanStackEdgeConfig(tokens) {
|
|
48044
|
-
const cloudflareCall = sourceCallIndex(tokens, "cloudflare");
|
|
48045
|
-
const tanStackCall = sourceCallIndex(tokens, "tanstackStart");
|
|
48046
|
-
return hasNamedSourceImport(tokens, "cloudflare", "@cloudflare/vite-plugin") && hasNamedSourceImport(tokens, "tanstackStart", "@tanstack/react-start/plugin/vite") && cloudflareCall !== -1 && tanStackCall !== -1 && cloudflareCall < tanStackCall && hasNestedObjectValue(tokens, "cloudflare", "viteEnvironment", "name", "ssr");
|
|
48047
|
-
}
|
|
48048
|
-
function hasNamedSourceImport(tokens, name, moduleName) {
|
|
48049
|
-
for (let index = 0; index < tokens.length; index += 1) {
|
|
48050
|
-
if (tokens[index] !== "import")
|
|
48051
|
-
continue;
|
|
48052
|
-
const fromIndex = tokens.findIndex((token, candidate) => candidate > index && token === "from");
|
|
48053
|
-
if (fromIndex === -1)
|
|
48054
|
-
return false;
|
|
48055
|
-
if (tokens[fromIndex + 1] === moduleName && tokens.slice(index + 1, fromIndex).includes(name)) {
|
|
48056
|
-
return true;
|
|
48057
|
-
}
|
|
48058
|
-
index = fromIndex;
|
|
48059
|
-
}
|
|
48060
|
-
return false;
|
|
48061
|
-
}
|
|
48062
|
-
function hasSourceModuleImport(tokens, moduleName) {
|
|
48063
|
-
return tokens.some((token, index) => token === "from" && tokens[index + 1] === moduleName);
|
|
48064
|
-
}
|
|
48065
|
-
function sourceCallIndex(tokens, name) {
|
|
48066
|
-
return tokens.findIndex((token, index) => token === name && tokens[index + 1] === "(");
|
|
48067
|
-
}
|
|
48068
|
-
function hasNestedObjectValue(tokens, call, outerProperty, innerProperty, expectedValue) {
|
|
48069
|
-
const callIndex = sourceCallIndex(tokens, call);
|
|
48070
|
-
if (callIndex === -1 || tokens[callIndex + 2] !== "{")
|
|
48071
|
-
return false;
|
|
48072
|
-
const callEnd = matchingSourceDelimiter(tokens, callIndex + 2, "{", "}");
|
|
48073
|
-
const outer = directSourceObjectProperty(tokens, callIndex + 2, callEnd, outerProperty);
|
|
48074
|
-
if (outer === null || outer.value !== "object")
|
|
48075
|
-
return false;
|
|
48076
|
-
return directSourceObjectProperty(tokens, outer.start, outer.end, innerProperty)?.value === expectedValue;
|
|
48077
|
-
}
|
|
48078
|
-
function directSourceObjectProperty(tokens, start, end, name) {
|
|
48079
|
-
let depth = 0;
|
|
48080
|
-
for (let index = start + 1; index < end; index += 1) {
|
|
48081
|
-
const value = tokens[index];
|
|
48082
|
-
if (value === "{" || value === "[" || value === "(")
|
|
48083
|
-
depth += 1;
|
|
48084
|
-
if (value === "}" || value === "]" || value === ")")
|
|
48085
|
-
depth -= 1;
|
|
48086
|
-
if (depth !== 0 || value !== name || tokens[index + 1] !== ":")
|
|
48087
|
-
continue;
|
|
48088
|
-
if (tokens[index + 2] === "{") {
|
|
48089
|
-
const candidateEnd = matchingSourceDelimiter(tokens, index + 2, "{", "}");
|
|
48090
|
-
return Object.freeze({ start: index + 2, end: candidateEnd, value: "object" });
|
|
48091
|
-
}
|
|
48092
|
-
return Object.freeze({
|
|
48093
|
-
start: index + 2,
|
|
48094
|
-
end: index + 2,
|
|
48095
|
-
value: tokens[index + 2] ?? ""
|
|
48096
|
-
});
|
|
48097
|
-
}
|
|
48098
|
-
return null;
|
|
48099
|
-
}
|
|
48100
|
-
function matchingSourceDelimiter(tokens, start, open5, close) {
|
|
48101
|
-
let depth = 0;
|
|
48102
|
-
for (let index = start; index < tokens.length; index += 1) {
|
|
48103
|
-
if (tokens[index] === open5)
|
|
48104
|
-
depth += 1;
|
|
48105
|
-
if (tokens[index] === close)
|
|
48106
|
-
depth -= 1;
|
|
48107
|
-
if (depth === 0)
|
|
48108
|
-
return index;
|
|
48109
|
-
}
|
|
48110
|
-
return -1;
|
|
48111
|
-
}
|
|
48112
48956
|
function requirementsFor(inventory, viteCompanion, mailConversionRequired, managedAuthSelected) {
|
|
48113
48957
|
const requirements = /* @__PURE__ */ new Set();
|
|
48114
48958
|
if (inventory.framework === "nextjs")
|
|
@@ -48149,7 +48993,7 @@ function runtimeContract(capabilities) {
|
|
|
48149
48993
|
...capabilities.mail ? ["OHMYHOST_MAIL_KEY"] : []
|
|
48150
48994
|
].sort()),
|
|
48151
48995
|
packages: Object.freeze({
|
|
48152
|
-
...capabilities.auth ? { betterAuth:
|
|
48996
|
+
...capabilities.auth ? { betterAuth: MANAGED_BETTER_AUTH_VERSION } : {},
|
|
48153
48997
|
// A database project was told to install pg, which cannot open a socket from a Worker at all,
|
|
48154
48998
|
// and was never offered the package that actually holds the client it needs.
|
|
48155
48999
|
...capabilities.database || capabilities.storage || capabilities.mail ? { customerRuntime: CUSTOMER_RUNTIME_SOURCE } : {}
|
|
@@ -48157,7 +49001,7 @@ function runtimeContract(capabilities) {
|
|
|
48157
49001
|
});
|
|
48158
49002
|
}
|
|
48159
49003
|
function viteCompanionScaffold(files, capabilities) {
|
|
48160
|
-
const sourceEntryPoint =
|
|
49004
|
+
const sourceEntryPoint = VITE_COMPANION_ENTRY_PATH;
|
|
48161
49005
|
const contract = runtimeContract(capabilities);
|
|
48162
49006
|
return Object.freeze({
|
|
48163
49007
|
schemaVersion: "ohmyhost.vite-api-companion/v1",
|
|
@@ -48189,7 +49033,7 @@ async function createConfiguration(root, configuration) {
|
|
|
48189
49033
|
await handle?.close();
|
|
48190
49034
|
}
|
|
48191
49035
|
}
|
|
48192
|
-
function
|
|
49036
|
+
function isRecord5(value) {
|
|
48193
49037
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
48194
49038
|
}
|
|
48195
49039
|
function isNodeError(value) {
|
|
@@ -48951,6 +49795,7 @@ var runOfflineInit = async (command, io, diagnostics) => {
|
|
|
48951
49795
|
inventory: result.inventory,
|
|
48952
49796
|
compatibility: result.compatibility,
|
|
48953
49797
|
blockers: result.blockers,
|
|
49798
|
+
pending: result.pending,
|
|
48954
49799
|
requirements: result.requirements,
|
|
48955
49800
|
companion: result.companion,
|
|
48956
49801
|
// The functions runtime reports its own bindings and packages; dropping it left a
|