@amerged/ohmyhost-cli 0.1.37 → 0.1.38
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/THIRD_PARTY_NOTICES.md +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-IHXNADOQ.js → chunk-EWKJGJQU.js} +240 -34
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/apps/product-cli/package.json +2 -2
- package/src/apps/product-cli/src/repository-init.ts +181 -9
- package/src/packages/contracts/package.json +1 -1
- package/src/packages/contracts/src/framework-admission-corpus.ts +15 -18
- package/src/packages/contracts/src/framework-build-script.mjs +10 -3
- package/src/packages/contracts/src/index.ts +3 -0
- package/src/packages/sdk-ts/package.json +2 -2
- package/src/packages/workos-auth-contracts/package.json +1 -1
package/README.md
CHANGED
package/THIRD_PARTY_NOTICES.md
CHANGED
package/dist/bin.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createRequire as __ohmyhostCreateRequire } from "node:module"; const require = __ohmyhostCreateRequire(import.meta.url);
|
|
3
3
|
import {
|
|
4
4
|
runProductionCli
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-EWKJGJQU.js";
|
|
6
6
|
import "./chunk-7D2RFLWM.js";
|
|
7
7
|
|
|
8
8
|
// apps/product-cli/dist/bin.js
|
|
@@ -22796,9 +22796,9 @@ var require_data = __commonJS({
|
|
|
22796
22796
|
}
|
|
22797
22797
|
});
|
|
22798
22798
|
|
|
22799
|
-
// node_modules/.pnpm/fast-uri@3.1.
|
|
22799
|
+
// node_modules/.pnpm/fast-uri@3.1.9/node_modules/fast-uri/lib/utils.js
|
|
22800
22800
|
var require_utils = __commonJS({
|
|
22801
|
-
"node_modules/.pnpm/fast-uri@3.1.
|
|
22801
|
+
"node_modules/.pnpm/fast-uri@3.1.9/node_modules/fast-uri/lib/utils.js"(exports, module) {
|
|
22802
22802
|
"use strict";
|
|
22803
22803
|
var isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu);
|
|
22804
22804
|
var isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);
|
|
@@ -23030,6 +23030,37 @@ var require_utils = __commonJS({
|
|
|
23030
23030
|
}
|
|
23031
23031
|
return output.join("");
|
|
23032
23032
|
}
|
|
23033
|
+
function removeDotSegmentsRelative(path2) {
|
|
23034
|
+
if (path2 === "" || path2[0] === "/") {
|
|
23035
|
+
return removeDotSegments(path2);
|
|
23036
|
+
}
|
|
23037
|
+
const segments = path2.split("/");
|
|
23038
|
+
const last = segments.length - 1;
|
|
23039
|
+
const output = [];
|
|
23040
|
+
let trailingDirectory = false;
|
|
23041
|
+
for (let i = 0; i <= last; i++) {
|
|
23042
|
+
const segment = segments[i];
|
|
23043
|
+
if (segment === "." || segment === "..") {
|
|
23044
|
+
if (segment === "..") {
|
|
23045
|
+
if (output.length !== 0 && output[output.length - 1] !== "..") {
|
|
23046
|
+
output.pop();
|
|
23047
|
+
} else {
|
|
23048
|
+
output.push("..");
|
|
23049
|
+
}
|
|
23050
|
+
}
|
|
23051
|
+
trailingDirectory = i === last;
|
|
23052
|
+
} else {
|
|
23053
|
+
output.push(segment);
|
|
23054
|
+
}
|
|
23055
|
+
}
|
|
23056
|
+
if (trailingDirectory && output[output.length - 1] !== "..") {
|
|
23057
|
+
output.push("");
|
|
23058
|
+
}
|
|
23059
|
+
if (output.length === 0 || output[0] === "" || output[0].includes(":")) {
|
|
23060
|
+
output.unshift(".");
|
|
23061
|
+
}
|
|
23062
|
+
return output.join("/");
|
|
23063
|
+
}
|
|
23033
23064
|
var HOST_DELIMS = { "@": "%40", "/": "%2F", "?": "%3F", "#": "%23", ":": "%3A" };
|
|
23034
23065
|
var HOST_DELIM_RE = /[@/?#:]/g;
|
|
23035
23066
|
var HOST_DELIM_NO_COLON_RE = /[@/?#]/g;
|
|
@@ -23290,6 +23321,7 @@ var require_utils = __commonJS({
|
|
|
23290
23321
|
encodeFragment,
|
|
23291
23322
|
escapePreservingEscapes,
|
|
23292
23323
|
removeDotSegments,
|
|
23324
|
+
removeDotSegmentsRelative,
|
|
23293
23325
|
isIPv4,
|
|
23294
23326
|
isUUID,
|
|
23295
23327
|
normalizeIPv6,
|
|
@@ -23298,9 +23330,9 @@ var require_utils = __commonJS({
|
|
|
23298
23330
|
}
|
|
23299
23331
|
});
|
|
23300
23332
|
|
|
23301
|
-
// node_modules/.pnpm/fast-uri@3.1.
|
|
23333
|
+
// node_modules/.pnpm/fast-uri@3.1.9/node_modules/fast-uri/lib/schemes.js
|
|
23302
23334
|
var require_schemes = __commonJS({
|
|
23303
|
-
"node_modules/.pnpm/fast-uri@3.1.
|
|
23335
|
+
"node_modules/.pnpm/fast-uri@3.1.9/node_modules/fast-uri/lib/schemes.js"(exports, module) {
|
|
23304
23336
|
"use strict";
|
|
23305
23337
|
var { isUUID } = require_utils();
|
|
23306
23338
|
var URN_REG = /^([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-./:;=@]|%[\da-f]{2})+)$/iu;
|
|
@@ -23509,20 +23541,22 @@ var require_schemes = __commonJS({
|
|
|
23509
23541
|
}
|
|
23510
23542
|
});
|
|
23511
23543
|
|
|
23512
|
-
// node_modules/.pnpm/fast-uri@3.1.
|
|
23544
|
+
// node_modules/.pnpm/fast-uri@3.1.9/node_modules/fast-uri/index.js
|
|
23513
23545
|
var require_fast_uri = __commonJS({
|
|
23514
|
-
"node_modules/.pnpm/fast-uri@3.1.
|
|
23546
|
+
"node_modules/.pnpm/fast-uri@3.1.9/node_modules/fast-uri/index.js"(exports, module) {
|
|
23515
23547
|
"use strict";
|
|
23516
|
-
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, serializePathEncoding, normalizeQueryFragmentEncoding, encodeQuery, encodeFragment, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
|
|
23548
|
+
var { normalizeIPv6, removeDotSegments, removeDotSegmentsRelative, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, serializePathEncoding, normalizeQueryFragmentEncoding, encodeQuery, encodeFragment, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
|
|
23517
23549
|
var { SCHEMES, getSchemeHandler } = require_schemes();
|
|
23518
23550
|
var VALID_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*$/u;
|
|
23519
23551
|
var MALFORMED_SCHEME_ERROR = "URI scheme is malformed.";
|
|
23520
|
-
|
|
23521
|
-
|
|
23522
|
-
|
|
23552
|
+
var MALFORMED_HOST_ERROR = "URI host is malformed.";
|
|
23553
|
+
var FORBIDDEN_DOMAIN_HOST_CHARACTER = /[\0-\x20"#/:<>?@[\\\]^`{|}\x7F]/u;
|
|
23554
|
+
function validateScheme(scheme) {
|
|
23555
|
+
const stringScheme = String(scheme);
|
|
23556
|
+
if (!VALID_SCHEME.test(stringScheme)) {
|
|
23523
23557
|
throw new TypeError(MALFORMED_SCHEME_ERROR);
|
|
23524
23558
|
}
|
|
23525
|
-
return
|
|
23559
|
+
return stringScheme;
|
|
23526
23560
|
}
|
|
23527
23561
|
function normalize(uri, options) {
|
|
23528
23562
|
if (typeof uri === "string") {
|
|
@@ -23559,6 +23593,9 @@ var require_fast_uri = __commonJS({
|
|
|
23559
23593
|
const resolvedSchemeHandler = getSchemeHandler(options && options.scheme || resolved.scheme);
|
|
23560
23594
|
const resolvedHost = resolved.host;
|
|
23561
23595
|
const resolvedHostIsIP = resolvedHost !== void 0 && resolvedHost !== "" && (isIPv4(resolvedHost) || normalizeIPv6(resolvedHost).isIPV6);
|
|
23596
|
+
if (hasForbiddenDomainHost(resolved, options || {}, resolvedSchemeHandler, resolvedHostIsIP)) {
|
|
23597
|
+
throw new Error(MALFORMED_HOST_ERROR);
|
|
23598
|
+
}
|
|
23562
23599
|
canonicalizeHost(resolved, options || {}, resolvedSchemeHandler, resolvedHostIsIP);
|
|
23563
23600
|
const encodedASCIIHost = resolvedHost && resolvedHost.indexOf("%") !== -1 && !new RegExp("\\P{ASCII}", "u").test(resolvedHost);
|
|
23564
23601
|
if (resolved.error && !encodedASCIIHost) {
|
|
@@ -23600,14 +23637,15 @@ var require_fast_uri = __commonJS({
|
|
|
23600
23637
|
if (relative2.path[0] === "/") {
|
|
23601
23638
|
target.path = removeDotSegments(relative2.path);
|
|
23602
23639
|
} else {
|
|
23603
|
-
|
|
23640
|
+
const baseHasAuthority = base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0;
|
|
23641
|
+
if (baseHasAuthority && !base.path) {
|
|
23604
23642
|
target.path = "/" + relative2.path;
|
|
23605
23643
|
} else if (!base.path) {
|
|
23606
23644
|
target.path = relative2.path;
|
|
23607
23645
|
} else {
|
|
23608
23646
|
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative2.path;
|
|
23609
23647
|
}
|
|
23610
|
-
target.path = removeDotSegments(target.path);
|
|
23648
|
+
target.path = base.scheme === void 0 && !baseHasAuthority && target.path[0] !== "/" ? removeDotSegmentsRelative(target.path) : removeDotSegments(target.path);
|
|
23611
23649
|
}
|
|
23612
23650
|
target.query = relative2.query;
|
|
23613
23651
|
}
|
|
@@ -23645,12 +23683,13 @@ var require_fast_uri = __commonJS({
|
|
|
23645
23683
|
const options = Object.assign({}, opts);
|
|
23646
23684
|
const uriTokens = [];
|
|
23647
23685
|
if (component.scheme) {
|
|
23648
|
-
component.scheme =
|
|
23686
|
+
component.scheme = validateScheme(component.scheme);
|
|
23649
23687
|
}
|
|
23650
23688
|
const schemeHandler = getSchemeHandler(options.scheme || component.scheme);
|
|
23651
23689
|
if (schemeHandler && schemeHandler.serialize) schemeHandler.serialize(component, options);
|
|
23652
23690
|
const hasAuthority = component.userinfo !== void 0 || component.host !== void 0 || component.port !== void 0;
|
|
23653
|
-
const
|
|
23691
|
+
const schemelessReference = (component.scheme === void 0 || options.reference === "suffix") && !hasAuthority;
|
|
23692
|
+
const pathNoScheme = !options.skipEscape && schemelessReference;
|
|
23654
23693
|
if (component.path !== void 0) {
|
|
23655
23694
|
if (!options.skipEscape) {
|
|
23656
23695
|
component.path = serializePathEncoding(component.path, pathNoScheme);
|
|
@@ -23659,7 +23698,7 @@ var require_fast_uri = __commonJS({
|
|
|
23659
23698
|
}
|
|
23660
23699
|
}
|
|
23661
23700
|
if (options.reference !== "suffix" && component.scheme) {
|
|
23662
|
-
component.scheme =
|
|
23701
|
+
component.scheme = validateScheme(component.scheme);
|
|
23663
23702
|
uriTokens.push(component.scheme, ":");
|
|
23664
23703
|
}
|
|
23665
23704
|
const authority = recomposeAuthority(component);
|
|
@@ -23675,9 +23714,9 @@ var require_fast_uri = __commonJS({
|
|
|
23675
23714
|
if (component.path !== void 0) {
|
|
23676
23715
|
let s = component.path;
|
|
23677
23716
|
if (!options.absolutePath && (!schemeHandler || !schemeHandler.absolutePath)) {
|
|
23678
|
-
s = removeDotSegments(s);
|
|
23717
|
+
s = schemelessReference && s[0] !== "/" ? removeDotSegmentsRelative(s) : removeDotSegments(s);
|
|
23679
23718
|
}
|
|
23680
|
-
if (
|
|
23719
|
+
if (schemelessReference) {
|
|
23681
23720
|
s = serializePathEncoding(s, true);
|
|
23682
23721
|
}
|
|
23683
23722
|
if (authority === void 0 && s[0] === "/" && s[1] === "/") {
|
|
@@ -23724,16 +23763,34 @@ var require_fast_uri = __commonJS({
|
|
|
23724
23763
|
return hasMalformedPercentEncoding(matches[3]) || host2 !== void 0 && !isIPLiteral(host2) && hasMalformedPercentEncoding(host2) || hasMalformedPercentEncoding(matches[6]) || hasMalformedPercentEncoding(matches[7]) || hasMalformedPercentEncoding(matches[8]);
|
|
23725
23764
|
}
|
|
23726
23765
|
function canonicalizeHost(parsed, options, schemeHandler, isIP) {
|
|
23766
|
+
if (hasForbiddenDomainHost(parsed, options, schemeHandler, isIP)) {
|
|
23767
|
+
parsed.error = parsed.error || MALFORMED_HOST_ERROR;
|
|
23768
|
+
return true;
|
|
23769
|
+
}
|
|
23727
23770
|
if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
|
|
23771
|
+
let url;
|
|
23728
23772
|
try {
|
|
23729
|
-
|
|
23773
|
+
url = new URL("http://" + parsed.host);
|
|
23730
23774
|
} catch (e) {
|
|
23731
23775
|
parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
|
|
23732
23776
|
return true;
|
|
23733
23777
|
}
|
|
23778
|
+
if (!isHostOnlyURL(url)) {
|
|
23779
|
+
parsed.error = parsed.error || MALFORMED_HOST_ERROR;
|
|
23780
|
+
return true;
|
|
23781
|
+
}
|
|
23782
|
+
parsed.host = url.hostname;
|
|
23734
23783
|
}
|
|
23735
23784
|
return false;
|
|
23736
23785
|
}
|
|
23786
|
+
function hasForbiddenDomainHost(parsed, options, schemeHandler, isIP) {
|
|
23787
|
+
return Boolean(
|
|
23788
|
+
parsed.host && isIP === false && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && FORBIDDEN_DOMAIN_HOST_CHARACTER.test(parsed.host)
|
|
23789
|
+
);
|
|
23790
|
+
}
|
|
23791
|
+
function isHostOnlyURL(url) {
|
|
23792
|
+
return url.username === "" && url.password === "" && url.port === "" && url.pathname === "/" && url.search === "" && url.hash === "" && url.hostname !== "";
|
|
23793
|
+
}
|
|
23737
23794
|
function parseWithStatus(uri, opts) {
|
|
23738
23795
|
const options = Object.assign({}, opts);
|
|
23739
23796
|
const parsed = {
|
|
@@ -23788,9 +23845,8 @@ var require_fast_uri = __commonJS({
|
|
|
23788
23845
|
parsed.query = matches[7];
|
|
23789
23846
|
parsed.fragment = matches[8];
|
|
23790
23847
|
if (parsed.scheme !== void 0) {
|
|
23791
|
-
|
|
23792
|
-
|
|
23793
|
-
parsed.scheme = decodedScheme.toLowerCase();
|
|
23848
|
+
if (VALID_SCHEME.test(parsed.scheme)) {
|
|
23849
|
+
parsed.scheme = parsed.scheme.toLowerCase();
|
|
23794
23850
|
} else {
|
|
23795
23851
|
parsed.error = parsed.error || MALFORMED_SCHEME_ERROR;
|
|
23796
23852
|
malformedScheme = true;
|
|
@@ -23818,7 +23874,7 @@ var require_fast_uri = __commonJS({
|
|
|
23818
23874
|
malformedIPLiteral = hasIPLiteralBracket && (!bracketedIPLiteral || ipv6result.error === true);
|
|
23819
23875
|
parsed.host = isIP ? ipv6result.host : ipv6result.host.toLowerCase();
|
|
23820
23876
|
if (malformedIPLiteral) {
|
|
23821
|
-
parsed.error = parsed.error ||
|
|
23877
|
+
parsed.error = parsed.error || MALFORMED_HOST_ERROR;
|
|
23822
23878
|
malformedAuthorityOrPort = true;
|
|
23823
23879
|
}
|
|
23824
23880
|
} else {
|
|
@@ -23846,7 +23902,7 @@ var require_fast_uri = __commonJS({
|
|
|
23846
23902
|
if (!isIP) {
|
|
23847
23903
|
host2 = normalizePercentEncoding(host2.toLowerCase());
|
|
23848
23904
|
}
|
|
23849
|
-
parsed.host = reescapeHostDelimiters(host2, isIP);
|
|
23905
|
+
parsed.host = isIP || options.scheme !== "null" ? reescapeHostDelimiters(host2, isIP) : host2;
|
|
23850
23906
|
}
|
|
23851
23907
|
if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
|
|
23852
23908
|
if (parsed.path) {
|
|
@@ -34467,7 +34523,7 @@ var FRAMEWORK_VERSION_POLICY = Object.freeze({
|
|
|
34467
34523
|
floors: Object.freeze(["14.4.0"]),
|
|
34468
34524
|
denied: Object.freeze([])
|
|
34469
34525
|
}),
|
|
34470
|
-
next: Object.freeze({ floors: Object.freeze(["
|
|
34526
|
+
next: Object.freeze({ floors: Object.freeze(["16.4.1"]), denied: Object.freeze([]) }),
|
|
34471
34527
|
vite: Object.freeze({ floors: Object.freeze(["5.4.6"]), denied: Object.freeze([]) }),
|
|
34472
34528
|
"@tanstack/react-start": Object.freeze({
|
|
34473
34529
|
floors: Object.freeze(["1.168.60"]),
|
|
@@ -40867,7 +40923,7 @@ function exact3(v, keys) {
|
|
|
40867
40923
|
// apps/product-cli/package.json
|
|
40868
40924
|
var package_default = {
|
|
40869
40925
|
name: "@ohmyhost/product-cli",
|
|
40870
|
-
version: "0.1.
|
|
40926
|
+
version: "0.1.38",
|
|
40871
40927
|
private: true,
|
|
40872
40928
|
ohmyhost: {
|
|
40873
40929
|
deployment: "production",
|
|
@@ -40899,7 +40955,7 @@ var package_default = {
|
|
|
40899
40955
|
},
|
|
40900
40956
|
scripts: {
|
|
40901
40957
|
build: "tsc --project tsconfig.build.json",
|
|
40902
|
-
typecheck: "tsc --project tsconfig.json --noEmit",
|
|
40958
|
+
typecheck: "tsc --project tsconfig.json --noEmit --incremental --tsBuildInfoFile typecheck.tsbuildinfo",
|
|
40903
40959
|
test: "vitest run --root ../.. apps/product-cli/test"
|
|
40904
40960
|
},
|
|
40905
40961
|
dependencies: {
|
|
@@ -48066,7 +48122,7 @@ function normalizeEgressOrigins(value) {
|
|
|
48066
48122
|
}
|
|
48067
48123
|
function forbiddenEgressHostname(hostname) {
|
|
48068
48124
|
const normalized = hostname.toLowerCase();
|
|
48069
|
-
return normalized === "localhost" || normalized.endsWith(".localhost") || normalized.endsWith(".local") || normalized.endsWith(".internal") || /^\d{1,3}(?:\.\d{1,3}){3}$/u.test(normalized) || normalized.startsWith("[") || normalized.endsWith("]");
|
|
48125
|
+
return /^ohmyho-(?:mail|storage)-gateway-[a-z0-9][a-z0-9-]*\.[a-z0-9][a-z0-9-]*\.workers\.dev\.?$/u.test(normalized) || normalized === "localhost" || normalized.endsWith(".localhost") || normalized.endsWith(".local") || normalized.endsWith(".internal") || /^\d{1,3}(?:\.\d{1,3}){3}$/u.test(normalized) || normalized.startsWith("[") || normalized.endsWith("]");
|
|
48070
48126
|
}
|
|
48071
48127
|
function parseOhmyhostConfigYaml(source) {
|
|
48072
48128
|
if (typeof source !== "string") {
|
|
@@ -49197,6 +49253,8 @@ var UTF8 = new TextDecoder("utf-8", { fatal: true });
|
|
|
49197
49253
|
var MAXIMUM_SOURCE_BYTES = 2 * 1024 * 1024;
|
|
49198
49254
|
var SOURCE_MODULE_PATTERN = /\.(?:[cm]?[jt]s|[jt]sx)$/u;
|
|
49199
49255
|
var STORAGE_CLIENT_CALL_PATTERN = /\bcreatePrivateStorageClient\s*\(/u;
|
|
49256
|
+
var GATEWAY_BINDING_NAME = /^OHMYHOST_(?:MAIL|STORAGE)_GATEWAY$/u;
|
|
49257
|
+
var GATEWAY_WORD_TOKEN = /[\p{ID_Start}_$][\p{ID_Continue}$]*/uy;
|
|
49200
49258
|
var UNNAMED_MODULE_MESSAGES = Object.freeze({
|
|
49201
49259
|
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.',
|
|
49202
49260
|
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.'
|
|
@@ -49207,10 +49265,14 @@ function callsStorageClient(source) {
|
|
|
49207
49265
|
var STORAGE_RUNTIME_BINDINGS = Object.freeze([
|
|
49208
49266
|
"OHMYHOST_ENVIRONMENT_ID",
|
|
49209
49267
|
"OHMYHOST_PROJECT_ID",
|
|
49210
|
-
"OHMYHOST_STORAGE_GATEWAY",
|
|
49211
49268
|
"OHMYHOST_STORAGE_GATEWAY_URL",
|
|
49212
49269
|
"OHMYHOST_STORAGE_KEY"
|
|
49213
49270
|
]);
|
|
49271
|
+
var MAIL_RUNTIME_BINDINGS = Object.freeze([
|
|
49272
|
+
"OHMYHOST_MAIL_GATEWAY_URL",
|
|
49273
|
+
"OHMYHOST_MAIL_KEY",
|
|
49274
|
+
"OHMYHOST_PROJECT_ID"
|
|
49275
|
+
]);
|
|
49214
49276
|
var CUSTOMER_RUNTIME_SOURCE = `npm:@amerged/ohmyhost-runtime@${package_default.version}`;
|
|
49215
49277
|
var PENDING_CHECKS = Object.freeze({
|
|
49216
49278
|
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.",
|
|
@@ -49389,6 +49451,7 @@ async function judgeSource(context) {
|
|
|
49389
49451
|
...source.findings.map((finding) => requiredCatalogBlocker(finding, context.applicationRoot)),
|
|
49390
49452
|
...await workerModuleBlockers(context),
|
|
49391
49453
|
...await databaseContractBlockers(context, context.provider),
|
|
49454
|
+
...await gatewayBindingBlockers(context),
|
|
49392
49455
|
...await storageClientBlockers(context),
|
|
49393
49456
|
...await migrationSqlBlockers(context, context.provider)
|
|
49394
49457
|
],
|
|
@@ -49437,6 +49500,147 @@ async function workerModuleBlockers(context) {
|
|
|
49437
49500
|
const source = await readFile3(resolve5(context.applicationDirectory, path2), "utf8");
|
|
49438
49501
|
return workerModuleFindings({ path: path2, source, fetch: framework === "functions", scheduled }).map((finding) => requiredCatalogBlocker(finding, context.applicationRoot));
|
|
49439
49502
|
}
|
|
49503
|
+
function gatewaySourceTokens(source) {
|
|
49504
|
+
const tokens = [];
|
|
49505
|
+
let cursor = 0;
|
|
49506
|
+
const quoted = (quote) => {
|
|
49507
|
+
const start = cursor++;
|
|
49508
|
+
while (cursor < source.length) {
|
|
49509
|
+
const character = source[cursor++];
|
|
49510
|
+
if (character === "\\")
|
|
49511
|
+
cursor++;
|
|
49512
|
+
else if (character === quote)
|
|
49513
|
+
break;
|
|
49514
|
+
}
|
|
49515
|
+
tokens.push(source.slice(start, cursor));
|
|
49516
|
+
};
|
|
49517
|
+
const code = (interpolation) => {
|
|
49518
|
+
let braces = 0;
|
|
49519
|
+
while (cursor < source.length) {
|
|
49520
|
+
const character = source[cursor];
|
|
49521
|
+
if (interpolation && character === "}" && braces === 0) {
|
|
49522
|
+
cursor++;
|
|
49523
|
+
return;
|
|
49524
|
+
}
|
|
49525
|
+
if (character === '"' || character === "'") {
|
|
49526
|
+
quoted(character);
|
|
49527
|
+
continue;
|
|
49528
|
+
}
|
|
49529
|
+
if (character === "`") {
|
|
49530
|
+
cursor++;
|
|
49531
|
+
while (cursor < source.length) {
|
|
49532
|
+
const literal = source[cursor++];
|
|
49533
|
+
if (literal === "\\")
|
|
49534
|
+
cursor++;
|
|
49535
|
+
else if (literal === "`")
|
|
49536
|
+
break;
|
|
49537
|
+
else if (literal === "$" && source[cursor] === "{") {
|
|
49538
|
+
cursor++;
|
|
49539
|
+
tokens.push("(");
|
|
49540
|
+
code(true);
|
|
49541
|
+
tokens.push(")");
|
|
49542
|
+
}
|
|
49543
|
+
}
|
|
49544
|
+
continue;
|
|
49545
|
+
}
|
|
49546
|
+
if (/\s/u.test(character)) {
|
|
49547
|
+
cursor++;
|
|
49548
|
+
continue;
|
|
49549
|
+
}
|
|
49550
|
+
if (character === "{")
|
|
49551
|
+
braces++;
|
|
49552
|
+
if (character === "}")
|
|
49553
|
+
braces--;
|
|
49554
|
+
GATEWAY_WORD_TOKEN.lastIndex = cursor;
|
|
49555
|
+
const word = GATEWAY_WORD_TOKEN.exec(source)?.[0];
|
|
49556
|
+
const token = word ?? (["?.", "=>"].includes(source.slice(cursor, cursor + 2)) ? source.slice(cursor, cursor + 2) : character);
|
|
49557
|
+
tokens.push(token);
|
|
49558
|
+
cursor += token.length;
|
|
49559
|
+
}
|
|
49560
|
+
};
|
|
49561
|
+
code(false);
|
|
49562
|
+
return tokens;
|
|
49563
|
+
}
|
|
49564
|
+
function usesGatewayBinding(source) {
|
|
49565
|
+
const tokens = gatewaySourceTokens(sourceWithoutComments(source));
|
|
49566
|
+
const objects = [];
|
|
49567
|
+
let declaration;
|
|
49568
|
+
let declarationDepth = 0;
|
|
49569
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
49570
|
+
const token = tokens[index];
|
|
49571
|
+
const previous = tokens[index - 1];
|
|
49572
|
+
const next = tokens[index + 1];
|
|
49573
|
+
if (declaration !== void 0) {
|
|
49574
|
+
if (declarationDepth === 0 && (token === ";" || /^(?:export|const|let|var|function|class|type|interface)$/u.test(token))) {
|
|
49575
|
+
declaration = void 0;
|
|
49576
|
+
if (token === ";")
|
|
49577
|
+
continue;
|
|
49578
|
+
} else {
|
|
49579
|
+
if (token === "{" || token === "(" || token === "[")
|
|
49580
|
+
declarationDepth++;
|
|
49581
|
+
if (token === "}" || token === ")" || token === "]") {
|
|
49582
|
+
declarationDepth--;
|
|
49583
|
+
if (declaration !== "type" && declarationDepth === 0)
|
|
49584
|
+
declaration = void 0;
|
|
49585
|
+
}
|
|
49586
|
+
continue;
|
|
49587
|
+
}
|
|
49588
|
+
}
|
|
49589
|
+
if ((token === "type" || token === "interface") && next !== void 0 && /^[\p{ID_Start}_$][\p{ID_Continue}$]*$/u.test(next) && previous !== "." && previous !== "?.") {
|
|
49590
|
+
declaration = token;
|
|
49591
|
+
continue;
|
|
49592
|
+
}
|
|
49593
|
+
if (token === "{" && previous === ":" && /^(?:const|let|var)$/u.test(tokens[index - 3] ?? "")) {
|
|
49594
|
+
declaration = "annotation";
|
|
49595
|
+
declarationDepth = 1;
|
|
49596
|
+
continue;
|
|
49597
|
+
}
|
|
49598
|
+
if (token === "{")
|
|
49599
|
+
objects.push(false);
|
|
49600
|
+
if (token === "}") {
|
|
49601
|
+
const gateway = objects.pop();
|
|
49602
|
+
let after = index + 1;
|
|
49603
|
+
if (tokens[after] === ":") {
|
|
49604
|
+
after++;
|
|
49605
|
+
while (after < tokens.length && !/^(?:=|;|\{|\})$/u.test(tokens[after]))
|
|
49606
|
+
after++;
|
|
49607
|
+
}
|
|
49608
|
+
if (gateway && tokens[after] === "=")
|
|
49609
|
+
return true;
|
|
49610
|
+
}
|
|
49611
|
+
const quoted = token.startsWith('"') || token.startsWith("'");
|
|
49612
|
+
const name = quoted ? token.slice(1, -1) : token;
|
|
49613
|
+
if (!GATEWAY_BINDING_NAME.test(name))
|
|
49614
|
+
continue;
|
|
49615
|
+
if (!quoted && (previous === "." || previous === "?."))
|
|
49616
|
+
return true;
|
|
49617
|
+
const receiver = tokens[index - (tokens[index - 2] === "?." ? 3 : 2)] ?? "";
|
|
49618
|
+
if (quoted && previous === "[" && next === "]" && (/^[\p{ID_Start}_$][\p{ID_Continue}$]*$/u.test(receiver) || receiver === ")" || receiver === "]") && !/^(?:return|throw|yield|case|new|typeof|void|delete|await|in|of)$/u.test(receiver))
|
|
49619
|
+
return true;
|
|
49620
|
+
if (objects.length > 0 && (previous === "{" || previous === ",") && (next === ":" || next === "," || next === "}" || next === "="))
|
|
49621
|
+
objects[objects.length - 1] = true;
|
|
49622
|
+
}
|
|
49623
|
+
return false;
|
|
49624
|
+
}
|
|
49625
|
+
async function gatewayBindingBlockers(context) {
|
|
49626
|
+
const blockers = [];
|
|
49627
|
+
for (const path2 of context.files) {
|
|
49628
|
+
if (!SOURCE_MODULE_PATTERN.test(path2) || /\.d\.[cm]?ts$/u.test(path2))
|
|
49629
|
+
continue;
|
|
49630
|
+
const absolute = resolve5(context.applicationDirectory, path2);
|
|
49631
|
+
const information = await stat2(absolute);
|
|
49632
|
+
if (!information.isFile() || information.size > MAXIMUM_SOURCE_BYTES)
|
|
49633
|
+
continue;
|
|
49634
|
+
if (!usesGatewayBinding(await readFile3(absolute, "utf8")))
|
|
49635
|
+
continue;
|
|
49636
|
+
blockers.push({
|
|
49637
|
+
code: "gateway_binding_unsupported",
|
|
49638
|
+
path: repositoryPath(context.applicationRoot, path2),
|
|
49639
|
+
message: "Mail and storage gateway Service Bindings are unsupported. In server code, create the client from its OHMYHOST_MAIL_GATEWAY_URL or OHMYHOST_STORAGE_GATEWAY_URL, environment key and project ID, plus the environment ID for storage. Use the global fetch with fetch: (request) => fetch(request), and capabilityFetch: (request) => fetch(request) for storage signed URLs."
|
|
49640
|
+
});
|
|
49641
|
+
}
|
|
49642
|
+
return blockers;
|
|
49643
|
+
}
|
|
49440
49644
|
async function storageClientBlockers(context) {
|
|
49441
49645
|
if (context.config?.storage === void 0)
|
|
49442
49646
|
return [];
|
|
@@ -49454,7 +49658,7 @@ async function storageClientBlockers(context) {
|
|
|
49454
49658
|
{
|
|
49455
49659
|
code: "storage_client_missing",
|
|
49456
49660
|
path: null,
|
|
49457
|
-
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
|
|
49661
|
+
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_URL, OHMYHOST_PROJECT_ID, OHMYHOST_ENVIRONMENT_ID and OHMYHOST_STORAGE_KEY, with the global fetch as fetch: (request) => fetch(request) and capabilityFetch: (request) => fetch(request). Reserve an upload, PUT the bytes to the returned signed URL, complete it, and serve reads with createSignedRead. There is no FILES bucket binding.'
|
|
49458
49662
|
}
|
|
49459
49663
|
];
|
|
49460
49664
|
}
|
|
@@ -49969,10 +50173,12 @@ async function detectR2Storage(root, files) {
|
|
|
49969
50173
|
function runtimeContract(capabilities) {
|
|
49970
50174
|
return Object.freeze({
|
|
49971
50175
|
bindings: Object.freeze([
|
|
49972
|
-
|
|
49973
|
-
|
|
49974
|
-
|
|
49975
|
-
|
|
50176
|
+
.../* @__PURE__ */ new Set([
|
|
50177
|
+
...capabilities.auth ? ["BETTER_AUTH_SECRET"] : [],
|
|
50178
|
+
...capabilities.storage ? STORAGE_RUNTIME_BINDINGS : [],
|
|
50179
|
+
...capabilities.database ? ["OHMYHOST_DATABASE"] : [],
|
|
50180
|
+
...capabilities.mail ? MAIL_RUNTIME_BINDINGS : []
|
|
50181
|
+
])
|
|
49976
50182
|
].sort()),
|
|
49977
50183
|
packages: Object.freeze({
|
|
49978
50184
|
...capabilities.auth ? { betterAuth: MANAGED_BETTER_AUTH_VERSION } : {},
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ohmyhost/product-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.38",
|
|
4
4
|
"private": true,
|
|
5
5
|
"ohmyhost": {
|
|
6
6
|
"deployment": "production",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
34
|
"build": "tsc --project tsconfig.build.json",
|
|
35
|
-
"typecheck": "tsc --project tsconfig.json --noEmit",
|
|
35
|
+
"typecheck": "tsc --project tsconfig.json --noEmit --incremental --tsBuildInfoFile typecheck.tsbuildinfo",
|
|
36
36
|
"test": "vitest run --root ../.. apps/product-cli/test"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
@@ -75,6 +75,8 @@ export type RepositoryFramework = Exclude<FrameworkFamilyClassification, "ambigu
|
|
|
75
75
|
const MAXIMUM_SOURCE_BYTES = 2 * 1024 * 1024;
|
|
76
76
|
const SOURCE_MODULE_PATTERN = /\.(?:[cm]?[jt]s|[jt]sx)$/u;
|
|
77
77
|
const STORAGE_CLIENT_CALL_PATTERN = /\bcreatePrivateStorageClient\s*\(/u;
|
|
78
|
+
const GATEWAY_BINDING_NAME = /^OHMYHOST_(?:MAIL|STORAGE)_GATEWAY$/u;
|
|
79
|
+
const GATEWAY_WORD_TOKEN = /[\p{ID_Start}_$][\p{ID_Continue}$]*/uy;
|
|
78
80
|
// The catalog names a module only by a path of its grammar; a file name with a space, for example,
|
|
79
81
|
// is not one, so its blocker carries the path beside a sentence that does not quote it.
|
|
80
82
|
const UNNAMED_MODULE_MESSAGES: Readonly<Record<DatabaseContractCode, string>> = Object.freeze({
|
|
@@ -89,15 +91,19 @@ function callsStorageClient(source: string): boolean {
|
|
|
89
91
|
return STORAGE_CLIENT_CALL_PATTERN.test(sourceWithoutComments(source));
|
|
90
92
|
}
|
|
91
93
|
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
+
// Global fetch reaches the private storage gateway using these values and its environment key.
|
|
95
|
+
// No raw bucket binding is handed to customer code.
|
|
94
96
|
const STORAGE_RUNTIME_BINDINGS = Object.freeze([
|
|
95
97
|
"OHMYHOST_ENVIRONMENT_ID",
|
|
96
98
|
"OHMYHOST_PROJECT_ID",
|
|
97
|
-
"OHMYHOST_STORAGE_GATEWAY",
|
|
98
99
|
"OHMYHOST_STORAGE_GATEWAY_URL",
|
|
99
100
|
"OHMYHOST_STORAGE_KEY",
|
|
100
101
|
]);
|
|
102
|
+
const MAIL_RUNTIME_BINDINGS = Object.freeze([
|
|
103
|
+
"OHMYHOST_MAIL_GATEWAY_URL",
|
|
104
|
+
"OHMYHOST_MAIL_KEY",
|
|
105
|
+
"OHMYHOST_PROJECT_ID",
|
|
106
|
+
]);
|
|
101
107
|
// The runtime release is published unchanged as `@amerged/ohmyhost-runtime`; this pinned alias keeps
|
|
102
108
|
// its import name and, unlike the website archive each release removes, stays installable.
|
|
103
109
|
const CUSTOMER_RUNTIME_SOURCE = `npm:@amerged/ohmyhost-runtime@${cliPackage.version}`;
|
|
@@ -105,6 +111,7 @@ const CUSTOMER_RUNTIME_SOURCE = `npm:@amerged/ohmyhost-runtime@${cliPackage.vers
|
|
|
105
111
|
/** A catalog code, which planning answers with the same sentence, or a check only init runs. */
|
|
106
112
|
export type RepositoryInitBlockerCode =
|
|
107
113
|
| FrameworkConversionDiagnosticCode
|
|
114
|
+
| "gateway_binding_unsupported"
|
|
108
115
|
| "migration_filename_noncanonical"
|
|
109
116
|
| "migration_sql_not_admitted"
|
|
110
117
|
| "storage_client_missing";
|
|
@@ -507,7 +514,7 @@ interface SourceCheckContext {
|
|
|
507
514
|
/**
|
|
508
515
|
* Every blocker of a source with a valid or proposed configuration, in the order planning judges:
|
|
509
516
|
* migration filenames, the shared source rules, the Worker module handlers and the database
|
|
510
|
-
* contract; init's own storage client and migration SQL checks come last.
|
|
517
|
+
* contract; init's own gateway binding, storage client and migration SQL checks come last.
|
|
511
518
|
*/
|
|
512
519
|
async function judgeSource(
|
|
513
520
|
context: SourceCheckContext &
|
|
@@ -528,6 +535,7 @@ async function judgeSource(
|
|
|
528
535
|
...source.findings.map((finding) => requiredCatalogBlocker(finding, context.applicationRoot)),
|
|
529
536
|
...(await workerModuleBlockers(context)),
|
|
530
537
|
...(await databaseContractBlockers(context, context.provider)),
|
|
538
|
+
...(await gatewayBindingBlockers(context)),
|
|
531
539
|
...(await storageClientBlockers(context)),
|
|
532
540
|
...(await migrationSqlBlockers(context, context.provider)),
|
|
533
541
|
],
|
|
@@ -621,6 +629,168 @@ async function workerModuleBlockers(context: SourceCheckContext): Promise<Reposi
|
|
|
621
629
|
);
|
|
622
630
|
}
|
|
623
631
|
|
|
632
|
+
/** Keep literal text inert, but scan executable template interpolations as ordinary code. */
|
|
633
|
+
function gatewaySourceTokens(source: string): readonly string[] {
|
|
634
|
+
const tokens: string[] = [];
|
|
635
|
+
let cursor = 0;
|
|
636
|
+
const quoted = (quote: string) => {
|
|
637
|
+
const start = cursor++;
|
|
638
|
+
while (cursor < source.length) {
|
|
639
|
+
const character = source[cursor++];
|
|
640
|
+
if (character === "\\") cursor++;
|
|
641
|
+
else if (character === quote) break;
|
|
642
|
+
}
|
|
643
|
+
tokens.push(source.slice(start, cursor));
|
|
644
|
+
};
|
|
645
|
+
const code = (interpolation: boolean): void => {
|
|
646
|
+
let braces = 0;
|
|
647
|
+
while (cursor < source.length) {
|
|
648
|
+
const character = source[cursor] as string;
|
|
649
|
+
if (interpolation && character === "}" && braces === 0) {
|
|
650
|
+
cursor++;
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
if (character === '"' || character === "'") {
|
|
654
|
+
quoted(character);
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
if (character === "`") {
|
|
658
|
+
cursor++;
|
|
659
|
+
while (cursor < source.length) {
|
|
660
|
+
const literal = source[cursor++];
|
|
661
|
+
if (literal === "\\") cursor++;
|
|
662
|
+
else if (literal === "`") break;
|
|
663
|
+
else if (literal === "$" && source[cursor] === "{") {
|
|
664
|
+
cursor++;
|
|
665
|
+
tokens.push("(");
|
|
666
|
+
code(true);
|
|
667
|
+
tokens.push(")");
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
if (/\s/u.test(character)) {
|
|
673
|
+
cursor++;
|
|
674
|
+
continue;
|
|
675
|
+
}
|
|
676
|
+
if (character === "{") braces++;
|
|
677
|
+
if (character === "}") braces--;
|
|
678
|
+
GATEWAY_WORD_TOKEN.lastIndex = cursor;
|
|
679
|
+
const word = GATEWAY_WORD_TOKEN.exec(source)?.[0];
|
|
680
|
+
const token =
|
|
681
|
+
word ??
|
|
682
|
+
(["?.", "=>"].includes(source.slice(cursor, cursor + 2))
|
|
683
|
+
? source.slice(cursor, cursor + 2)
|
|
684
|
+
: character);
|
|
685
|
+
tokens.push(token);
|
|
686
|
+
cursor += token.length;
|
|
687
|
+
}
|
|
688
|
+
};
|
|
689
|
+
code(false);
|
|
690
|
+
return tokens;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/** Static value accesses only: declarations, literal messages and object-literal keys are inert. */
|
|
694
|
+
function usesGatewayBinding(source: string): boolean {
|
|
695
|
+
const tokens = gatewaySourceTokens(sourceWithoutComments(source));
|
|
696
|
+
const objects: boolean[] = [];
|
|
697
|
+
let declaration: "type" | "interface" | "annotation" | undefined;
|
|
698
|
+
let declarationDepth = 0;
|
|
699
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
700
|
+
const token = tokens[index] as string;
|
|
701
|
+
const previous = tokens[index - 1];
|
|
702
|
+
const next = tokens[index + 1];
|
|
703
|
+
if (declaration !== undefined) {
|
|
704
|
+
if (
|
|
705
|
+
declarationDepth === 0 &&
|
|
706
|
+
(token === ";" || /^(?:export|const|let|var|function|class|type|interface)$/u.test(token))
|
|
707
|
+
) {
|
|
708
|
+
declaration = undefined;
|
|
709
|
+
if (token === ";") continue;
|
|
710
|
+
} else {
|
|
711
|
+
if (token === "{" || token === "(" || token === "[") declarationDepth++;
|
|
712
|
+
if (token === "}" || token === ")" || token === "]") {
|
|
713
|
+
declarationDepth--;
|
|
714
|
+
if (declaration !== "type" && declarationDepth === 0) declaration = undefined;
|
|
715
|
+
}
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
if (
|
|
720
|
+
(token === "type" || token === "interface") &&
|
|
721
|
+
next !== undefined &&
|
|
722
|
+
/^[\p{ID_Start}_$][\p{ID_Continue}$]*$/u.test(next) &&
|
|
723
|
+
previous !== "." &&
|
|
724
|
+
previous !== "?."
|
|
725
|
+
) {
|
|
726
|
+
declaration = token;
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
// An inline variable type is not an object binding pattern; resume at its initializer.
|
|
730
|
+
if (token === "{" && previous === ":" && /^(?:const|let|var)$/u.test(tokens[index - 3] ?? "")) {
|
|
731
|
+
declaration = "annotation";
|
|
732
|
+
declarationDepth = 1;
|
|
733
|
+
continue;
|
|
734
|
+
}
|
|
735
|
+
if (token === "{") objects.push(false);
|
|
736
|
+
if (token === "}") {
|
|
737
|
+
const gateway = objects.pop();
|
|
738
|
+
let after = index + 1;
|
|
739
|
+
if (tokens[after] === ":") {
|
|
740
|
+
after++;
|
|
741
|
+
while (after < tokens.length && !/^(?:=|;|\{|\})$/u.test(tokens[after] as string)) after++;
|
|
742
|
+
}
|
|
743
|
+
if (gateway && tokens[after] === "=") return true;
|
|
744
|
+
}
|
|
745
|
+
const quoted = token.startsWith('"') || token.startsWith("'");
|
|
746
|
+
const name = quoted ? token.slice(1, -1) : token;
|
|
747
|
+
if (!GATEWAY_BINDING_NAME.test(name)) continue;
|
|
748
|
+
if (!quoted && (previous === "." || previous === "?.")) return true;
|
|
749
|
+
const receiver = tokens[index - (tokens[index - 2] === "?." ? 3 : 2)] ?? "";
|
|
750
|
+
if (
|
|
751
|
+
quoted &&
|
|
752
|
+
previous === "[" &&
|
|
753
|
+
next === "]" &&
|
|
754
|
+
(/^[\p{ID_Start}_$][\p{ID_Continue}$]*$/u.test(receiver) ||
|
|
755
|
+
receiver === ")" ||
|
|
756
|
+
receiver === "]") &&
|
|
757
|
+
!/^(?:return|throw|yield|case|new|typeof|void|delete|await|in|of)$/u.test(receiver)
|
|
758
|
+
)
|
|
759
|
+
return true;
|
|
760
|
+
if (
|
|
761
|
+
objects.length > 0 &&
|
|
762
|
+
(previous === "{" || previous === ",") &&
|
|
763
|
+
(next === ":" || next === "," || next === "}" || next === "=")
|
|
764
|
+
)
|
|
765
|
+
objects[objects.length - 1] = true;
|
|
766
|
+
}
|
|
767
|
+
return false;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* Init names legacy mail/storage bindings before a customer builds code whose gateway calls the
|
|
772
|
+
* egress policy cannot serve. This local guidance does not change planning's admission policy.
|
|
773
|
+
*/
|
|
774
|
+
async function gatewayBindingBlockers(
|
|
775
|
+
context: SourceCheckContext,
|
|
776
|
+
): Promise<RepositoryInitBlocker[]> {
|
|
777
|
+
const blockers: RepositoryInitBlocker[] = [];
|
|
778
|
+
for (const path of context.files) {
|
|
779
|
+
if (!SOURCE_MODULE_PATTERN.test(path) || /\.d\.[cm]?ts$/u.test(path)) continue;
|
|
780
|
+
const absolute = resolve(context.applicationDirectory, path);
|
|
781
|
+
const information = await stat(absolute);
|
|
782
|
+
if (!information.isFile() || information.size > MAXIMUM_SOURCE_BYTES) continue;
|
|
783
|
+
if (!usesGatewayBinding(await readFile(absolute, "utf8"))) continue;
|
|
784
|
+
blockers.push({
|
|
785
|
+
code: "gateway_binding_unsupported",
|
|
786
|
+
path: repositoryPath(context.applicationRoot, path),
|
|
787
|
+
message:
|
|
788
|
+
"Mail and storage gateway Service Bindings are unsupported. In server code, create the client from its OHMYHOST_MAIL_GATEWAY_URL or OHMYHOST_STORAGE_GATEWAY_URL, environment key and project ID, plus the environment ID for storage. Use the global fetch with fetch: (request) => fetch(request), and capabilityFetch: (request) => fetch(request) for storage signed URLs.",
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
return blockers;
|
|
792
|
+
}
|
|
793
|
+
|
|
624
794
|
/**
|
|
625
795
|
* Declared storage is reached only through the private gateway client. A storage-enabled app whose
|
|
626
796
|
* source never constructs that client deploys green and fails at its first upload, so name it here.
|
|
@@ -641,7 +811,7 @@ async function storageClientBlockers(
|
|
|
641
811
|
code: "storage_client_missing",
|
|
642
812
|
path: null,
|
|
643
813
|
message:
|
|
644
|
-
'ohmyhost.yaml declares storage, but no application source calls createPrivateStorageClient. Import it from "@ohmyhost/customer-runtime/storage" and build it in server code from
|
|
814
|
+
'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_URL, OHMYHOST_PROJECT_ID, OHMYHOST_ENVIRONMENT_ID and OHMYHOST_STORAGE_KEY, with the global fetch as fetch: (request) => fetch(request) and capabilityFetch: (request) => fetch(request). Reserve an upload, PUT the bytes to the returned signed URL, complete it, and serve reads with createSignedRead. There is no FILES bucket binding.',
|
|
645
815
|
},
|
|
646
816
|
];
|
|
647
817
|
}
|
|
@@ -1418,10 +1588,12 @@ function runtimeContract(capabilities: RuntimeCapabilities): Readonly<{
|
|
|
1418
1588
|
return Object.freeze({
|
|
1419
1589
|
bindings: Object.freeze(
|
|
1420
1590
|
[
|
|
1421
|
-
...(
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1591
|
+
...new Set([
|
|
1592
|
+
...(capabilities.auth ? ["BETTER_AUTH_SECRET"] : []),
|
|
1593
|
+
...(capabilities.storage ? STORAGE_RUNTIME_BINDINGS : []),
|
|
1594
|
+
...(capabilities.database ? ["OHMYHOST_DATABASE"] : []),
|
|
1595
|
+
...(capabilities.mail ? MAIL_RUNTIME_BINDINGS : []),
|
|
1596
|
+
]),
|
|
1425
1597
|
].sort(),
|
|
1426
1598
|
),
|
|
1427
1599
|
packages: Object.freeze({
|
|
@@ -150,7 +150,7 @@
|
|
|
150
150
|
},
|
|
151
151
|
"scripts": {
|
|
152
152
|
"build": "tsc --project tsconfig.build.json",
|
|
153
|
-
"typecheck": "tsc --project tsconfig.json --noEmit",
|
|
153
|
+
"typecheck": "tsc --project tsconfig.json --noEmit --incremental --tsBuildInfoFile typecheck.tsbuildinfo",
|
|
154
154
|
"test": "vitest run --root ../.. packages/contracts/test",
|
|
155
155
|
"validate": "pnpm run typecheck && pnpm run test"
|
|
156
156
|
},
|
|
@@ -48,7 +48,7 @@ const CORPUS_NEXT_PAGE = `export default function Home() {
|
|
|
48
48
|
return <main>Hello from ohmyho.st</main>;
|
|
49
49
|
}
|
|
50
50
|
`;
|
|
51
|
-
// The config `create-next-app@16.4.
|
|
51
|
+
// The config `create-next-app@16.4.1 --yes --no-tailwind` writes: Cache Components with partial
|
|
52
52
|
// prefetching.
|
|
53
53
|
const CREATE_NEXT_APP_CONFIG = `import type { NextConfig } from "next";
|
|
54
54
|
|
|
@@ -124,10 +124,7 @@ export const FRAMEWORK_ADMISSION_CONFORMANCE_CORPUS: Readonly<{
|
|
|
124
124
|
Readonly<{ diagnostic: FrameworkConversionDiagnostic }>)[];
|
|
125
125
|
}> = Object.freeze({
|
|
126
126
|
admitted: Object.freeze([
|
|
127
|
-
corpusCase(
|
|
128
|
-
"Next.js 16.3.8 with npm on the edge runtime (the exact customer source)",
|
|
129
|
-
nextSource(),
|
|
130
|
-
),
|
|
127
|
+
corpusCase("Next.js 16.4.1 with npm on the edge runtime", nextSource()),
|
|
131
128
|
corpusCase("Next.js 17.0.0, a later stable major", nextSource({ declared: "17.0.0" })),
|
|
132
129
|
corpusCase(
|
|
133
130
|
"Vite ^8.2.0 resolved to 8.3.4 by pnpm-lock.yaml",
|
|
@@ -147,26 +144,26 @@ export const FRAMEWORK_ADMISSION_CONFORMANCE_CORPUS: Readonly<{
|
|
|
147
144
|
Object.freeze({
|
|
148
145
|
...corpusCase(
|
|
149
146
|
"Next.js ^16.3.0 with bun.lock, which ohmyho.st does not resolve",
|
|
150
|
-
nextSource({ declared: "^16.3.0", resolved: "16.4.
|
|
147
|
+
nextSource({ declared: "^16.3.0", resolved: "16.4.1", manager: "bun" }),
|
|
151
148
|
),
|
|
152
149
|
packages: Object.freeze(["next"] as const),
|
|
153
150
|
}),
|
|
154
151
|
]),
|
|
155
152
|
refused: Object.freeze([
|
|
156
153
|
refusedCase(
|
|
157
|
-
"Next.js 16.
|
|
158
|
-
nextSource({ declared: "16.
|
|
159
|
-
versionFinding("package-lock.json", "next", "16.
|
|
154
|
+
"Next.js 16.4.0 below the 16.x security minimum",
|
|
155
|
+
nextSource({ declared: "16.4.0" }),
|
|
156
|
+
versionFinding("package-lock.json", "next", "16.4.0", "16.4.1"),
|
|
160
157
|
),
|
|
161
158
|
refusedCase(
|
|
162
|
-
"Next.js 15.5.
|
|
163
|
-
nextSource({ declared: "15.5.
|
|
164
|
-
versionFinding("package.json", "next", "15.5.
|
|
159
|
+
"Next.js 15.5.28 declared with yarn, because no 15.x release is admitted",
|
|
160
|
+
nextSource({ declared: "15.5.28", manager: "yarn" }),
|
|
161
|
+
versionFinding("package.json", "next", "15.5.28", "16.4.1"),
|
|
165
162
|
),
|
|
166
163
|
refusedCase(
|
|
167
|
-
"Next.js ^16.3.0 resolved to 16.
|
|
168
|
-
nextSource({ declared: "^16.3.0", resolved: "16.
|
|
169
|
-
versionFinding("package-lock.json", "next", "16.
|
|
164
|
+
"Next.js ^16.3.0 resolved to 16.4.0 by package-lock.json",
|
|
165
|
+
nextSource({ declared: "^16.3.0", resolved: "16.4.0" }),
|
|
166
|
+
versionFinding("package-lock.json", "next", "16.4.0", "16.4.1"),
|
|
170
167
|
),
|
|
171
168
|
refusedCase(
|
|
172
169
|
"TanStack Start 1.168.59 resolved by pnpm-lock.yaml below its security minimum",
|
|
@@ -354,7 +351,7 @@ function nextSource(
|
|
|
354
351
|
): Readonly<Record<string, string>> {
|
|
355
352
|
const manager = options.manager ?? "npm";
|
|
356
353
|
const [packageManager, lockfile] = CORPUS_MANAGERS[manager];
|
|
357
|
-
const next = options.declared ?? "16.
|
|
354
|
+
const next = options.declared ?? "16.4.1";
|
|
358
355
|
const react = { react: "19.1.2", "react-dom": "19.1.2" };
|
|
359
356
|
const runtime = options.field === "devDependencies" ? react : { next, ...react };
|
|
360
357
|
return Object.freeze({
|
|
@@ -380,7 +377,7 @@ function nextSource(
|
|
|
380
377
|
}
|
|
381
378
|
|
|
382
379
|
/**
|
|
383
|
-
* The next.config.ts and Tailwind CSS files and packages `create-next-app@16.4.
|
|
380
|
+
* The next.config.ts and Tailwind CSS files and packages `create-next-app@16.4.1 --yes` writes, in
|
|
384
381
|
* an App Router source with the packageManager, lockfile and ohmyhost.yaml ohmyho.st requires;
|
|
385
382
|
* `tailwind: false` is its --no-tailwind output. On Turbopack, its default bundler, it adds only the
|
|
386
383
|
* Turbopack rule for Tailwind CSS; `postcss` adds the postcss.config.mjs and @tailwindcss/postcss it
|
|
@@ -390,7 +387,7 @@ function createNextAppSource(
|
|
|
390
387
|
options: Readonly<{ tailwind: boolean; postcss?: boolean }>,
|
|
391
388
|
): Readonly<Record<string, string>> {
|
|
392
389
|
const [packageManager, lockfile] = CORPUS_MANAGERS.npm;
|
|
393
|
-
const runtime = { next: "16.4.
|
|
390
|
+
const runtime = { next: "16.4.1", react: "19.3.0", "react-dom": "19.3.0" };
|
|
394
391
|
const postcss = options.postcss === true;
|
|
395
392
|
return Object.freeze({
|
|
396
393
|
"ohmyhost.yaml": corpusConfiguration("npm", ".open-next/assets", "edge"),
|
|
@@ -15,8 +15,15 @@ export const PINNED_BUN_VERSION = "1.2.22";
|
|
|
15
15
|
* upper bound: later patches, minors and majors reach the real build, which reports adapter failures.
|
|
16
16
|
* Denied ranges are half-open [introduced, fixed); a null fix denies every later release.
|
|
17
17
|
*
|
|
18
|
-
* next: GHSA-
|
|
19
|
-
*
|
|
18
|
+
* next: GHSA-2464-fhr4-w9xw (critical, remote code execution through the Image Optimization API with
|
|
19
|
+
* SVG and sharp before 0.35.5) and GHSA-p3m5-59mp-jv5r (high, denial of service through Server
|
|
20
|
+
* Actions), both fixed in 15.5.28 and 16.4.1; GHSA-673q-mjm6-r5xr (critical, remote code execution
|
|
21
|
+
* through `use cache` functions that return typed arrays, Next.js 16 only, fixed in 16.4.1).
|
|
22
|
+
* There is no 15.x floor: the pinned OpenNext has refused Next.js 15 since 21 October 2026, so every 15.x
|
|
23
|
+
* release is refused with the minimum 16.4.1 instead of sending a customer to 15.5.28 and a failing build.
|
|
24
|
+
* Earlier floors: GHSA-4jqv-mc3x-m676 and GHSA-mcj8-r9mp-w47p (fixed in 15.5.27 and 16.3.8);
|
|
25
|
+
* GHSA-cjq9-62q9-8jv4, GHSA-f87g-xv8r-7p7x, GHSA-3w37-wq28-93x7, GHSA-h694-7cp9-m8p3 and
|
|
26
|
+
* GHSA-39w2-rjm5-chcv (16.3.8).
|
|
20
27
|
* @tanstack/react-start: GHSA-qx66-fv34-fjm8 (fixed in 1.168.60).
|
|
21
28
|
* vite: GHSA-64vr-g452-qvp3 (fixed in 5.4.6), the only advisory affecting `vite build` output.
|
|
22
29
|
* @type {FrameworkVersionPolicy}
|
|
@@ -27,7 +34,7 @@ export const FRAMEWORK_VERSION_POLICY = Object.freeze({
|
|
|
27
34
|
floors: Object.freeze(["14.4.0"]),
|
|
28
35
|
denied: Object.freeze([]),
|
|
29
36
|
}),
|
|
30
|
-
next: Object.freeze({ floors: Object.freeze(["
|
|
37
|
+
next: Object.freeze({ floors: Object.freeze(["16.4.1"]), denied: Object.freeze([]) }),
|
|
31
38
|
vite: Object.freeze({ floors: Object.freeze(["5.4.6"]), denied: Object.freeze([]) }),
|
|
32
39
|
"@tanstack/react-start": Object.freeze({
|
|
33
40
|
floors: Object.freeze(["1.168.60"]),
|
|
@@ -209,6 +209,9 @@ function normalizeEgressOrigins(value: readonly string[] | undefined): readonly
|
|
|
209
209
|
function forbiddenEgressHostname(hostname: string): boolean {
|
|
210
210
|
const normalized = hostname.toLowerCase();
|
|
211
211
|
return (
|
|
212
|
+
/^ohmyho-(?:mail|storage)-gateway-[a-z0-9][a-z0-9-]*\.[a-z0-9][a-z0-9-]*\.workers\.dev\.?$/u.test(
|
|
213
|
+
normalized,
|
|
214
|
+
) ||
|
|
212
215
|
normalized === "localhost" ||
|
|
213
216
|
normalized.endsWith(".localhost") ||
|
|
214
217
|
normalized.endsWith(".local") ||
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ohmyhost/sdk-ts",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.38",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"build": "tsc --project tsconfig.build.json",
|
|
15
15
|
"check": "pnpm --dir ../.. sdk-ts:check",
|
|
16
16
|
"generate": "pnpm --dir ../.. sdk-ts:generate",
|
|
17
|
-
"typecheck": "tsc --project tsconfig.json --noEmit",
|
|
17
|
+
"typecheck": "tsc --project tsconfig.json --noEmit --incremental --tsBuildInfoFile typecheck.tsbuildinfo",
|
|
18
18
|
"test": "pnpm run check && vitest run --root ../.. packages/sdk-ts"
|
|
19
19
|
},
|
|
20
20
|
"license": "Apache-2.0"
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
34
|
"build": "tsc --project tsconfig.build.json",
|
|
35
|
-
"typecheck": "tsc --project tsconfig.json --noEmit",
|
|
35
|
+
"typecheck": "tsc --project tsconfig.json --noEmit --incremental --tsBuildInfoFile typecheck.tsbuildinfo",
|
|
36
36
|
"test": "vitest run --root ../.. packages/workos-auth-contracts/src"
|
|
37
37
|
},
|
|
38
38
|
"license": "Apache-2.0"
|