@geekmidas/manifest 10.0.0-alpha.167 → 10.0.0-alpha.169

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -33,13 +33,17 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
33
33
  * this.
34
34
  */
35
35
  const DEFAULT_POSTGRES_VERSION = 18;
36
+ /** Whether a declaration is built into a public bundle — a site or an app. */
37
+ function isPublicDeclaration(declaration) {
38
+ return declaration.kind === "site" || declaration.kind === "mobile-app";
39
+ }
36
40
  /** A site's subdomain that means every subdomain — see `SiteDeclaration`. */
37
41
  const WILDCARD_SUBDOMAIN = "*";
38
42
  /**
39
43
  * Whether a declaration is a site answering on every subdomain.
40
44
  */
41
45
  function isWildcardSite(declaration) {
42
- return declaration?.kind === "site" && declaration.subdomain === "*";
46
+ return declaration?.kind === "site" && "subdomain" in declaration && declaration.subdomain === "*";
43
47
  }
44
48
  /**
45
49
  * What a `Telemetry` node provides to each process with an edge to it.
@@ -1008,6 +1012,47 @@ const PUBLIC_PREFIX = {
1008
1012
  expo: "EXPO_PUBLIC_"
1009
1013
  };
1010
1014
  /**
1015
+ * Every public value a site's or a mobile app's edges reach — the one walk of
1016
+ * its edges that every target makes, so what a bundle inlines cannot differ
1017
+ * between `gkm dev`, a compose build and the AWS target.
1018
+ *
1019
+ * Each edge's `PUBLIC` roles, and, because a bucket and the file server over
1020
+ * it are one construct to a page:
1021
+ *
1022
+ * - an edge to a bucket (a full-stack site's `.dependsOn([assets.server])`)
1023
+ * also reaches each file server over it — its objects are shown from there;
1024
+ * - an edge to a file server also reaches its bucket's public values — where
1025
+ * the bucket signs the links to its private objects.
1026
+ *
1027
+ * A bucket's connection is never one: `PUBLIC.objects` does not list it.
1028
+ */
1029
+ function publicValues(declaration, manifest) {
1030
+ const values = /* @__PURE__ */ new Map();
1031
+ const reach = (id) => {
1032
+ const target = manifest[id];
1033
+ if (!target) return;
1034
+ for (const role of PUBLIC[target.kind] ?? []) {
1035
+ const key = providedKeyFor(id, target.kind, role);
1036
+ values.set(key, {
1037
+ id,
1038
+ kind: target.kind,
1039
+ role,
1040
+ key
1041
+ });
1042
+ }
1043
+ };
1044
+ for (const edge of declaration.dependencies) {
1045
+ const target = manifest[edge.target];
1046
+ if (!target) continue;
1047
+ reach(edge.target);
1048
+ if (target.kind === "objects") {
1049
+ for (const [id, other] of Object.entries(manifest)) if (other.kind === "file-server" && other.of === edge.target) reach(id);
1050
+ }
1051
+ if (target.kind === "file-server") reach(target.of);
1052
+ }
1053
+ return [...values.values()];
1054
+ }
1055
+ /**
1011
1056
  * The keys a site's bundle needs, mapped to the key each value comes from —
1012
1057
  * `{ VITE_API_URL: 'API_URL' }` — its edges' public values, and a site's own
1013
1058
  * address (`{ VITE_WEB_URL: 'WEB_URL' }`).
@@ -1030,31 +1075,14 @@ function publicEnvFor(declaration, manifest) {
1030
1075
  const prefix = PUBLIC_PREFIX[declaration.variant];
1031
1076
  const keys = {};
1032
1077
  if (declaration.kind === "site") {
1033
- const own = provideKey(declaration.id, "url");
1078
+ const own = providedKeyFor(declaration.id, "site", "url");
1034
1079
  keys[`${prefix}${own}`] = own;
1035
1080
  }
1036
1081
  if (isWildcardSite(declaration)) for (const role of ["domain", "protectedSubdomains"]) {
1037
- const key = provideKey(declaration.id, role);
1082
+ const key = providedKeyFor(declaration.id, "site", role);
1038
1083
  keys[`${prefix}${key}`] = key;
1039
1084
  }
1040
- const inline = (id, target) => {
1041
- for (const role of PUBLIC[target.kind] ?? []) {
1042
- const key = providedKeyFor(id, target.kind, role);
1043
- keys[`${prefix}${key}`] = key;
1044
- }
1045
- };
1046
- for (const edge of declaration.dependencies) {
1047
- const target = manifest[edge.target];
1048
- if (!target) continue;
1049
- inline(edge.target, target);
1050
- if (target.kind === "objects") {
1051
- for (const [id, other] of Object.entries(manifest)) if (other.kind === "file-server" && other.of === edge.target) inline(id, other);
1052
- }
1053
- if (target.kind === "file-server") {
1054
- const bucket = manifest[target.of];
1055
- if (bucket) inline(target.of, bucket);
1056
- }
1057
- }
1085
+ for (const { key } of publicValues(declaration, manifest)) keys[`${prefix}${key}`] = key;
1058
1086
  return keys;
1059
1087
  }
1060
1088
  /**
@@ -1069,25 +1097,11 @@ const IMAGE_ORIGINS_KEY = "GKM_IMAGE_ORIGINS";
1069
1097
  /**
1070
1098
  * The keys whose values are the origins a site's pages load files from: each
1071
1099
  * file server it reaches, and where each bucket it reaches signs its links —
1072
- * by either edge, the same walk {@link publicEnvFor} makes. Sorted, so the
1100
+ * by either edge, read off {@link publicValues}. Sorted, so the
1073
1101
  * value composed from them does not churn.
1074
1102
  */
1075
1103
  function imageOriginSources(declaration, manifest) {
1076
- const sources = /* @__PURE__ */ new Set();
1077
- const bucket = (id) => sources.add(providedKeyFor(id, "objects", "signedOrigin"));
1078
- const server = (id) => sources.add(providedKeyFor(id, "file-server", "url"));
1079
- for (const edge of declaration.dependencies) {
1080
- const target = manifest[edge.target];
1081
- if (target?.kind === "objects") {
1082
- bucket(edge.target);
1083
- for (const [id, other] of Object.entries(manifest)) if (other.kind === "file-server" && other.of === edge.target) server(id);
1084
- }
1085
- if (target?.kind === "file-server") {
1086
- server(edge.target);
1087
- if (manifest[target.of]?.kind === "objects") bucket(target.of);
1088
- }
1089
- }
1090
- return [...sources].sort();
1104
+ return publicValues(declaration, manifest).filter(({ kind, role }) => kind === "file-server" && role === "url" || kind === "objects" && role === "signedOrigin").map(({ key }) => key).sort();
1091
1105
  }
1092
1106
  /**
1093
1107
  * {@link IMAGE_ORIGINS_KEY}'s value: the origin of each source that resolved,
@@ -1123,6 +1137,14 @@ function bucketExpiry(manifest, bucketId) {
1123
1137
  return Object.keys(expire).length > 0 ? expire : void 0;
1124
1138
  }
1125
1139
  //#endregion
1140
+ //#region src/email.ts
1141
+ /** The local part mail is sent from when the construct names none. */
1142
+ const DEFAULT_SENDER = "noreply";
1143
+ /** `<local part>@<domain>` — `info@shop.example.com`, else `noreply@…`. */
1144
+ function senderAddress(email, domain) {
1145
+ return `${email.from ?? "noreply"}@${domain}`;
1146
+ }
1147
+ //#endregion
1126
1148
  //#region src/encryption.ts
1127
1149
  /**
1128
1150
  * The `kms://` URL an `Encryption` construct reads on AWS.
@@ -1219,6 +1241,81 @@ const EDGE_TOKEN_HEADER = "x-gkm-edge-token";
1219
1241
  /** Where a stack's processes read the token {@link EDGE_TOKEN_HEADER} carries. */
1220
1242
  const EDGE_TOKEN_KEY = "GKM_EDGE_TOKEN";
1221
1243
  //#endregion
1244
+ //#region src/hosts.ts
1245
+ /** Whether a declaration answers on a host of its own: a surface or a site. */
1246
+ function isHostedDeclaration(declaration) {
1247
+ return declaration?.kind === "rest-api" || declaration?.kind === "websocket-api" || declaration?.kind === "site";
1248
+ }
1249
+ /**
1250
+ * The one derivation of a construct's labels: its primary label, its
1251
+ * aliases and its redirects, in declared order.
1252
+ */
1253
+ function hostLabels(source) {
1254
+ return {
1255
+ primary: source.subdomain ?? kebabCase(source.id),
1256
+ aliases: [...source.aliases ?? []],
1257
+ redirects: [...source.redirects ?? []]
1258
+ };
1259
+ }
1260
+ /** One label of a DNS name: lowercase letters, digits and inner hyphens. */
1261
+ const LABEL$1 = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
1262
+ /** An alias or redirect that cannot be a label of its own under the domain. */
1263
+ var InvalidHostLabel = class extends Error {
1264
+ id;
1265
+ label;
1266
+ field;
1267
+ constructor(id, label, field) {
1268
+ super(`'${id}' lists '${label}' in ${field}, which is not a host label it can add: each entry is one lowercase label under the stage's domain ('rest', 'www') — no '*', no dots — listed once, and not the construct's own label. Fix or remove the entry.`);
1269
+ this.id = id;
1270
+ this.label = label;
1271
+ this.field = field;
1272
+ this.name = "InvalidHostLabel";
1273
+ }
1274
+ };
1275
+ /** A wildcard site with aliases or redirects. */
1276
+ var WildcardSiteCannotHaveAliases = class extends Error {
1277
+ id;
1278
+ constructor(id) {
1279
+ super(`'${id}' answers on every subdomain, so it has no single host for an alias to repeat or a redirect to point at. Drop \`aliases\` and \`redirects\`, or give the site a subdomain of its own.`);
1280
+ this.id = id;
1281
+ this.name = "WildcardSiteCannotHaveAliases";
1282
+ }
1283
+ };
1284
+ /** One label listed as both an alias and a redirect. */
1285
+ var HostLabelBothAliasAndRedirect = class extends Error {
1286
+ id;
1287
+ label;
1288
+ constructor(id, label) {
1289
+ super(`'${id}' lists '${label}' in both aliases and redirects, but a host either serves the site or sends the browser elsewhere. Keep it in one of the two.`);
1290
+ this.id = id;
1291
+ this.label = label;
1292
+ this.name = "HostLabelBothAliasAndRedirect";
1293
+ }
1294
+ };
1295
+ /**
1296
+ * What a construct's aliases and redirects have to be, checked where it is
1297
+ * constructed.
1298
+ *
1299
+ * @throws {WildcardSiteCannotHaveAliases} on a wildcard site with either
1300
+ * @throws {HostLabelBothAliasAndRedirect} when a label is in both lists
1301
+ * @throws {InvalidHostLabel} for `'*'`, a dot, a repeat, or its own label
1302
+ */
1303
+ function assertHostLabels(source) {
1304
+ const { primary, aliases, redirects } = hostLabels(source);
1305
+ if (aliases.length === 0 && redirects.length === 0) return;
1306
+ if (primary === "*") throw new WildcardSiteCannotHaveAliases(source.id);
1307
+ for (const label of aliases) if (redirects.includes(label)) throw new HostLabelBothAliasAndRedirect(source.id, label);
1308
+ const seen = /* @__PURE__ */ new Set([primary]);
1309
+ const check = (labels, field) => {
1310
+ for (const label of labels) {
1311
+ if (!LABEL$1.test(label) || seen.has(label)) throw new InvalidHostLabel(source.id, label, field);
1312
+ seen.add(label);
1313
+ }
1314
+ };
1315
+ check(aliases, "aliases");
1316
+ check(redirects, "redirects");
1317
+ }
1318
+ //#endregion
1222
1319
  //#region src/inbox.ts
1223
1320
  /** The event type a received message is announced as on its inbox's topic. */
1224
1321
  const INBOX_EVENT = "email.received";
@@ -1528,18 +1625,21 @@ exports.CLIENT_IP_HEADER = CLIENT_IP_HEADER;
1528
1625
  exports.DATABASE_FOLDERS = DATABASE_FOLDERS;
1529
1626
  exports.DEFAULT_DERIVED_DAYS = DEFAULT_DERIVED_DAYS;
1530
1627
  exports.DEFAULT_POSTGRES_VERSION = DEFAULT_POSTGRES_VERSION;
1628
+ exports.DEFAULT_SENDER = DEFAULT_SENDER;
1531
1629
  exports.DEFAULT_STAGE_URL = DEFAULT_STAGE_URL;
1532
1630
  exports.DERIVED_PREFIX = DERIVED_PREFIX;
1533
1631
  exports.DERIVES_FROM = DERIVES_FROM;
1534
1632
  exports.EDGE_TOKEN_HEADER = EDGE_TOKEN_HEADER;
1535
1633
  exports.EDGE_TOKEN_KEY = EDGE_TOKEN_KEY;
1536
1634
  exports.FILE_SERVER_SUFFIX = FILE_SERVER_SUFFIX;
1635
+ exports.HostLabelBothAliasAndRedirect = HostLabelBothAliasAndRedirect;
1537
1636
  exports.IMAGE_ORIGINS_KEY = IMAGE_ORIGINS_KEY;
1538
1637
  exports.INBOX_EVENT = INBOX_EVENT;
1539
1638
  exports.INBOX_MESSAGES_PREFIX = INBOX_MESSAGES_PREFIX;
1540
1639
  exports.IllegalDerivation = IllegalDerivation;
1541
1640
  exports.InvalidApiGatewayWebSocketUrl = InvalidApiGatewayWebSocketUrl;
1542
1641
  exports.InvalidConstructId = InvalidConstructId;
1642
+ exports.InvalidHostLabel = InvalidHostLabel;
1543
1643
  exports.KMS_SCHEME = KMS_SCHEME;
1544
1644
  exports.MIGRATIONS_ROOT = MIGRATIONS_ROOT;
1545
1645
  exports.NoUrlForStage = NoUrlForStage;
@@ -1550,8 +1650,10 @@ exports.SECRET_SYMBOLS = SECRET_SYMBOLS;
1550
1650
  exports.TELEMETRY_KEYS = TELEMETRY_KEYS;
1551
1651
  exports.UnknownParent = UnknownParent;
1552
1652
  exports.WILDCARD_SUBDOMAIN = WILDCARD_SUBDOMAIN;
1653
+ exports.WildcardSiteCannotHaveAliases = WildcardSiteCannotHaveAliases;
1553
1654
  exports.apiGatewayWebSocketUrl = apiGatewayWebSocketUrl;
1554
1655
  exports.assertDerivations = assertDerivations;
1656
+ exports.assertHostLabels = assertHostLabels;
1555
1657
  exports.bucketExpiry = bucketExpiry;
1556
1658
  exports.cacheTable = cacheTable;
1557
1659
  exports.canonicalId = canonicalId;
@@ -1565,10 +1667,13 @@ exports.environmentCase = environmentCase;
1565
1667
  exports.externalApiUrl = externalApiUrl;
1566
1668
  exports.fileServerName = fileServerName;
1567
1669
  exports.fileServerSurfaceId = fileServerSurfaceId;
1670
+ exports.hostLabels = hostLabels;
1568
1671
  exports.imageOriginSources = imageOriginSources;
1569
1672
  exports.imageOrigins = imageOrigins;
1570
1673
  exports.inboxRecipient = inboxRecipient;
1571
1674
  exports.isDerived = isDerived;
1675
+ exports.isHostedDeclaration = isHostedDeclaration;
1676
+ exports.isPublicDeclaration = isPublicDeclaration;
1572
1677
  exports.isWebOrigin = isWebOrigin;
1573
1678
  exports.isWildcardSite = isWildcardSite;
1574
1679
  exports.kebabCase = kebabCase;
@@ -1577,14 +1682,15 @@ exports.migrationFolder = migrationFolder;
1577
1682
  exports.migrationTargets = migrationTargets;
1578
1683
  exports.mobileOrigins = mobileOrigins;
1579
1684
  exports.parseApiGatewayWebSocketUrl = parseApiGatewayWebSocketUrl;
1580
- exports.provideKey = provideKey;
1581
1685
  exports.providedKeyFor = providedKeyFor;
1582
1686
  exports.provisionOrder = provisionOrder;
1583
1687
  exports.publicEnvFor = publicEnvFor;
1688
+ exports.publicValues = publicValues;
1584
1689
  exports.receiptRecipients = receiptRecipients;
1585
1690
  exports.schemeBase = schemeBase;
1586
1691
  exports.scopedName = scopedName;
1587
1692
  exports.seedFolder = seedFolder;
1693
+ exports.senderAddress = senderAddress;
1588
1694
  exports.serviceKey = serviceKey;
1589
1695
  exports.trustsOrigin = trustsOrigin;
1590
1696
  exports.wildcardDomainOf = wildcardDomainOf;