@penvhq/launcher 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  httpFetcher,
6
6
  nodeSpawner,
7
7
  runLauncher
8
- } from "./chunk-WA5LM767.js";
8
+ } from "./chunk-HQTTS5BH.js";
9
9
 
10
10
  // src/bin.ts
11
11
  function readLine() {
@@ -72,7 +72,7 @@ function readKey(source, index) {
72
72
  const match = /^[A-Za-z_$][A-Za-z0-9_$]*/.exec(source.slice(index));
73
73
  return match === null ? void 0 : { key: match[0], end: index + match[0].length };
74
74
  }
75
- function typeSlotIn(source, open, close) {
75
+ function providerSlotIn(source, open, close) {
76
76
  let i = skipTrivia(source, open + 1);
77
77
  while (i < close - 1) {
78
78
  const key = readKey(source, i);
@@ -88,7 +88,7 @@ function typeSlotIn(source, open, close) {
88
88
  let valueEnd;
89
89
  if (QUOTES.has(ch)) {
90
90
  valueEnd = skipString(source, valueStart);
91
- if (key.key === "type" && ch !== "`") {
91
+ if (key.key === "provider" && ch !== "`") {
92
92
  return {
93
93
  start: valueStart,
94
94
  end: valueEnd,
@@ -111,8 +111,33 @@ function typeSlotIn(source, open, close) {
111
111
  }
112
112
  return void 0;
113
113
  }
114
- function scan(source) {
115
- const found = /(?:^|[\s{,;])providers\s*:\s*\{/.exec(source);
114
+ function blankComments(source) {
115
+ let out = "";
116
+ let i = 0;
117
+ while (i < source.length) {
118
+ const ch = source.charAt(i);
119
+ if (QUOTES.has(ch)) {
120
+ const end = skipString(source, i);
121
+ out += source.slice(i, end);
122
+ i = end;
123
+ continue;
124
+ }
125
+ if (ch === "/" && (source.charAt(i + 1) === "/" || source.charAt(i + 1) === "*")) {
126
+ const line = source.charAt(i + 1) === "/";
127
+ const found = line ? source.indexOf("\n", i) : source.indexOf("*/", i + 2);
128
+ const end = found === -1 ? source.length : line ? found : found + 2;
129
+ out += source.slice(i, end).replace(/[^\n]/g, " ");
130
+ i = end;
131
+ continue;
132
+ }
133
+ out += ch;
134
+ i += 1;
135
+ }
136
+ return out;
137
+ }
138
+ function scan(raw) {
139
+ const source = blankComments(raw);
140
+ const found = /(?:^|[\s{,;])environments\s*:\s*\{/.exec(source);
116
141
  if (found === null) {
117
142
  return void 0;
118
143
  }
@@ -133,31 +158,42 @@ function scan(source) {
133
158
  return void 0;
134
159
  }
135
160
  const entryOpen = skipTrivia(source, colon + 1);
136
- if (source.charAt(entryOpen) !== "{") {
137
- return void 0;
138
- }
139
- const entryClose = matchBrace(source, entryOpen);
140
- if (entryClose === -1) {
161
+ const ch = source.charAt(entryOpen);
162
+ let entryEnd;
163
+ let slot;
164
+ if (ch === "{") {
165
+ entryEnd = matchBrace(source, entryOpen);
166
+ if (entryEnd === -1) {
167
+ return void 0;
168
+ }
169
+ slot = providerSlotIn(source, entryOpen, entryEnd);
170
+ } else if (QUOTES.has(ch)) {
171
+ entryEnd = skipString(source, entryOpen);
172
+ slot = ch === "`" ? void 0 : {
173
+ start: entryOpen,
174
+ end: entryEnd,
175
+ value: source.slice(entryOpen + 1, entryEnd - 1)
176
+ };
177
+ } else {
141
178
  return void 0;
142
179
  }
143
- const slot = typeSlotIn(source, entryOpen, entryClose);
144
- entries.push({ environment: key.key, type: slot?.value, slot });
145
- i = skipTrivia(source, entryClose);
180
+ entries.push({ environment: key.key, provider: slot?.value, slot });
181
+ i = skipTrivia(source, entryEnd);
146
182
  if (source.charAt(i) === ",") {
147
183
  i = skipTrivia(source, i + 1);
148
184
  }
149
185
  }
150
186
  return entries;
151
187
  }
152
- function readProviderEntries(source) {
153
- return scan(source)?.map(({ environment, type }) => ({ environment, type }));
188
+ function readEnvironmentProviders(source) {
189
+ return scan(source)?.map(({ environment, provider }) => ({ environment, provider }));
154
190
  }
155
- function setProviderType(source, environment, type) {
191
+ function setEnvironmentProvider(source, environment, provider) {
156
192
  const entry = scan(source)?.find((candidate) => candidate.environment === environment);
157
193
  if (entry?.slot === void 0) {
158
194
  return void 0;
159
195
  }
160
- return `${source.slice(0, entry.slot.start)}${JSON.stringify(type)}${source.slice(entry.slot.end)}`;
196
+ return `${source.slice(0, entry.slot.start)}${JSON.stringify(provider)}${source.slice(entry.slot.end)}`;
161
197
  }
162
198
 
163
199
  // src/errors.ts
@@ -167,7 +203,8 @@ import {
167
203
  LOCAL_EXTENSIONS_PATH,
168
204
  MANIFEST_PATH,
169
205
  OFFICIAL_SCOPE,
170
- PenvError
206
+ PenvError,
207
+ RESERVED_ENTRY_FIELDS
171
208
  } from "@penvhq/core";
172
209
  var INSTALL_COMMAND = "penv install";
173
210
  var UPGRADE_COMMAND = "penv upgrade";
@@ -483,6 +520,26 @@ var DeclarationNotSelfContainedError = class extends PenvError {
483
520
  );
484
521
  }
485
522
  };
523
+ var DeclarationReservedFieldError = class extends PenvError {
524
+ name = "DeclarationReservedFieldError";
525
+ constructor(name, file, field2) {
526
+ super(
527
+ "PENV_DECLARATION_RESERVED_FIELD",
528
+ `The declaration ${name} ships at \`${file}\` declares \`${field2}\`, which penv owns on every environment entry`,
529
+ `Report it to ${name}. penv reserves ${RESERVED_ENTRY_FIELDS.map((reserved) => `\`${reserved}\``).join(", ")} inside an \`environments\` entry, so a provider names its own fields in its own vocabulary \u2014 \`project\`, \`path\` \u2014 and leaves those four to penv.`
530
+ );
531
+ }
532
+ };
533
+ var DeclarationShapeUnreadableError = class extends PenvError {
534
+ name = "DeclarationShapeUnreadableError";
535
+ constructor(name, file, member) {
536
+ super(
537
+ "PENV_DECLARATION_SHAPE_UNREADABLE",
538
+ member === void 0 ? `The declaration ${name} ships at \`${file}\` has a \`ProviderConfigMap\` member penv cannot read` : `The declaration ${name} ships at \`${file}\` writes \`${member}\` as something other than an entry shape`,
539
+ `Report it to ${name}. Each \`ProviderConfigMap\` member is written as its own object literal \u2014 \`"@acme/provider-x": { readonly path: string }\` \u2014 so penv can check it names none of ${RESERVED_ENTRY_FIELDS.map((reserved) => `\`${reserved}\``).join(", ")}, which it owns on every environment entry.`
540
+ );
541
+ }
542
+ };
486
543
  var EnginePinUnreleasedError = class extends PenvError {
487
544
  name = "EnginePinUnreleasedError";
488
545
  constructor() {
@@ -539,7 +596,7 @@ var UpgradeUnattendedError = class extends PenvError {
539
596
  super(
540
597
  "PENV_UPGRADE_UNATTENDED",
541
598
  `Upgrading rewrites ${MANIFEST_PATH} and package.json, and this run has nobody to decide that`,
542
- `Run \`${UPGRADE_COMMAND} <version> ${YES_FLAG}\` \u2014 unattended, penv moves the pin only to a version you named. In CI, run \`${INSTALL_COMMAND}\`: it installs what the committed manifest already pins.`
599
+ `Run \`${UPGRADE_COMMAND} ${YES_FLAG}\` to take what \`latest\` points at, or \`${UPGRADE_COMMAND} <version> ${YES_FLAG}\` to name one. In CI, run \`${INSTALL_COMMAND}\`: it installs what the committed manifest already pins.`
543
600
  );
544
601
  }
545
602
  };
@@ -577,7 +634,7 @@ var EngineEntryError = class extends PenvError {
577
634
  // src/declaration.ts
578
635
  import { mkdirSync, readFileSync, writeFileSync } from "fs";
579
636
  import { dirname, join, resolve, sep } from "path";
580
- import { EXTENSIONS_PATH as EXTENSIONS_PATH2 } from "@penvhq/core";
637
+ import { EXTENSIONS_PATH as EXTENSIONS_PATH2, RESERVED_ENTRY_FIELDS as RESERVED_ENTRY_FIELDS2 } from "@penvhq/core";
581
638
  var AUGMENTATION_TARGET = "@penvhq/core";
582
639
  var MODULE_SPECIFIER = /(?:\bfrom|\bmodule|\bimport|\brequire)\s*\(?\s*(["'])([^"']*)\1/g;
583
640
  function field(source, key) {
@@ -616,7 +673,7 @@ function openShape(name) {
616
673
 
617
674
  declare module "${AUGMENTATION_TARGET}" {
618
675
  interface ProviderConfigMap {
619
- ${JSON.stringify(name)}: ProviderConfig & { readonly type: ${JSON.stringify(name)} };
676
+ ${JSON.stringify(name)}: ProviderConfig & { readonly provider: ${JSON.stringify(name)} };
620
677
  }
621
678
  }
622
679
  `;
@@ -634,12 +691,166 @@ function assertSelfContained(name, file, source) {
634
691
  }
635
692
  }
636
693
  }
694
+ function endOfString(source, index) {
695
+ const quote = source.charAt(index);
696
+ let i = index + 1;
697
+ while (i < source.length) {
698
+ const ch = source.charAt(i);
699
+ if (ch === "\\") {
700
+ i += 2;
701
+ continue;
702
+ }
703
+ if (ch === quote) {
704
+ return i + 1;
705
+ }
706
+ i += 1;
707
+ }
708
+ return source.length;
709
+ }
710
+ function withoutComments(source) {
711
+ let out = "";
712
+ let i = 0;
713
+ while (i < source.length) {
714
+ const ch = source.charAt(i);
715
+ if (ch === '"' || ch === "'" || ch === "`") {
716
+ const end = endOfString(source, i);
717
+ out += source.slice(i, end);
718
+ i = end;
719
+ continue;
720
+ }
721
+ if (ch === "/" && (source.charAt(i + 1) === "/" || source.charAt(i + 1) === "*")) {
722
+ const line = source.charAt(i + 1) === "/";
723
+ const end = line ? source.indexOf("\n", i) : source.indexOf("*/", i + 2);
724
+ i = end === -1 ? source.length : line ? end : end + 2;
725
+ out += " ";
726
+ continue;
727
+ }
728
+ out += ch;
729
+ i += 1;
730
+ }
731
+ return out;
732
+ }
733
+ var RESERVED_FIELDS = [...RESERVED_ENTRY_FIELDS2].sort((a, b) => b.length - a.length);
734
+ var RESERVED_MEMBER = new RegExp(
735
+ `(?:^|[\\s;{,])(?:readonly\\s+)?(["']?)(${RESERVED_FIELDS.join("|")})\\1\\s*\\??\\s*:`
736
+ );
737
+ function endOfBrace(code, open) {
738
+ let depth = 0;
739
+ let i = open;
740
+ while (i < code.length) {
741
+ const ch = code.charAt(i);
742
+ if (ch === '"' || ch === "'" || ch === "`") {
743
+ i = endOfString(code, i);
744
+ continue;
745
+ }
746
+ if (ch === "{" || ch === "[" || ch === "(") {
747
+ depth += 1;
748
+ } else if (ch === "}" || ch === "]" || ch === ")") {
749
+ depth -= 1;
750
+ if (depth === 0) {
751
+ return i + 1;
752
+ }
753
+ }
754
+ i += 1;
755
+ }
756
+ return -1;
757
+ }
758
+ function ownMembers(body) {
759
+ let members = "";
760
+ let depth = 0;
761
+ let i = 0;
762
+ while (i < body.length) {
763
+ const ch = body.charAt(i);
764
+ if (ch === '"' || ch === "'" || ch === "`") {
765
+ const end = endOfString(body, i);
766
+ if (depth === 0) {
767
+ members += body.slice(i, end);
768
+ }
769
+ i = end;
770
+ continue;
771
+ }
772
+ if (ch === "{" || ch === "[" || ch === "(") {
773
+ depth += 1;
774
+ } else if (ch === "}" || ch === "]" || ch === ")") {
775
+ depth -= 1;
776
+ } else if (depth === 0) {
777
+ members += ch;
778
+ i += 1;
779
+ continue;
780
+ }
781
+ if (depth <= 1) {
782
+ members += " ";
783
+ }
784
+ i += 1;
785
+ }
786
+ return members;
787
+ }
788
+ var MEMBER_NAME = /^(?:readonly\s+)?(?:(["'])([^"']*)\1|([A-Za-z_$][\w$]*))\s*\??\s*:/;
789
+ function mapMembers(source) {
790
+ const code = withoutComments(source);
791
+ const map = /interface\s+ProviderConfigMap\s*\{/.exec(code);
792
+ if (map === null) {
793
+ return [];
794
+ }
795
+ const open = map.index + map[0].length - 1;
796
+ const close = endOfBrace(code, open);
797
+ if (close === -1) {
798
+ return { unreadable: void 0 };
799
+ }
800
+ const members = [];
801
+ let i = open + 1;
802
+ for (; ; ) {
803
+ while (/\s|;|,/.test(code.charAt(i))) {
804
+ i += 1;
805
+ }
806
+ if (i >= close - 1) {
807
+ return members;
808
+ }
809
+ const name = MEMBER_NAME.exec(code.slice(i, close - 1));
810
+ if (name === null) {
811
+ return { unreadable: void 0 };
812
+ }
813
+ let value = i + name[0].length;
814
+ while (/\s/.test(code.charAt(value))) {
815
+ value += 1;
816
+ }
817
+ const declared = name[2] ?? name[3] ?? "";
818
+ if (code.charAt(value) !== "{") {
819
+ return { unreadable: declared };
820
+ }
821
+ const end = endOfBrace(code, value);
822
+ if (end === -1) {
823
+ return { unreadable: declared };
824
+ }
825
+ members.push({ name: declared, body: code.slice(value + 1, end - 1) });
826
+ i = end;
827
+ while (/\s/.test(code.charAt(i))) {
828
+ i += 1;
829
+ }
830
+ if (i < close - 1 && code.charAt(i) !== ";" && code.charAt(i) !== ",") {
831
+ return { unreadable: declared };
832
+ }
833
+ }
834
+ }
835
+ function assertEntryShapesAreReadable(name, file, source) {
836
+ const members = mapMembers(source);
837
+ if (!Array.isArray(members)) {
838
+ throw new DeclarationShapeUnreadableError(name, file, members.unreadable);
839
+ }
840
+ for (const member of members) {
841
+ const field2 = RESERVED_MEMBER.exec(ownMembers(member.body))?.[2];
842
+ if (field2 !== void 0) {
843
+ throw new DeclarationReservedFieldError(name, file, field2);
844
+ }
845
+ }
846
+ }
637
847
  function renderDeclaration(subject, shipped) {
638
848
  if (shipped === void 0) {
639
849
  return `${header(subject)}
640
850
  ${openShape(subject.name)}`;
641
851
  }
642
852
  assertSelfContained(subject.name, shipped.file, shipped.source);
853
+ assertEntryShapesAreReadable(subject.name, shipped.file, shipped.source);
643
854
  const body = shipped.source.replace(/^/, "").trimEnd();
644
855
  return `${header(subject)}
645
856
  ${body}
@@ -1160,30 +1371,33 @@ function chooseEnvironments(answer, offered) {
1160
1371
  function quoted(environments) {
1161
1372
  return environments.map((environment) => `\`${environment}\``).join(", ");
1162
1373
  }
1374
+ function blindAdvice(name) {
1375
+ const spec = JSON.stringify(name);
1376
+ return `Point an environment at ${name} in penv.config.ts: \`production: ${spec}\`, or \`production: { provider: ${spec} }\` plus the provider's own fields and a \`keySource\`.`;
1377
+ }
1163
1378
  function configAdvice(name, pointing, unpointed) {
1164
- const add2 = `Add \`type: ${JSON.stringify(name)}\``;
1165
1379
  if (pointing.length === 0) {
1166
- return `${add2} to an environment in penv.config.ts.`;
1380
+ return blindAdvice(name);
1167
1381
  }
1168
- return `${quoted(pointing)} already ${pointing.length === 1 ? "points" : "point"} at ${name}. ${add2} to ${quoted(unpointed)} in penv.config.ts if ${unpointed.length === 1 ? "it" : "they"} should too.`;
1382
+ return `${quoted(pointing)} already ${pointing.length === 1 ? "points" : "point"} at ${name}. Point ${quoted(unpointed)} at it in penv.config.ts if ${unpointed.length === 1 ? "it" : "they"} should too.`;
1169
1383
  }
1170
1384
  async function offerConfigEdit(options, name, ask) {
1171
1385
  const { io, root } = options;
1172
1386
  const configFile = findConfigFile(root);
1173
- const blind = `Add \`type: ${JSON.stringify(name)}\` to an environment in penv.config.ts.`;
1387
+ const blind = blindAdvice(name);
1174
1388
  if (configFile === void 0) {
1175
1389
  io.out(blind);
1176
1390
  return;
1177
1391
  }
1178
1392
  const shown = relative(root, configFile).split(sep2).join("/");
1179
1393
  let current = readFileSync4(configFile, "utf8");
1180
- const entries = readProviderEntries(current);
1394
+ const entries = readEnvironmentProviders(current);
1181
1395
  if (entries === void 0 || entries.length === 0) {
1182
1396
  io.out(blind);
1183
1397
  return;
1184
1398
  }
1185
- const pointing = entries.filter((entry) => entry.type === name).map((entry) => entry.environment);
1186
- const offered = entries.filter((entry) => entry.type !== name).map((entry) => entry.environment);
1399
+ const pointing = entries.filter((entry) => entry.provider === name).map((entry) => entry.environment);
1400
+ const offered = entries.filter((entry) => entry.provider !== name).map((entry) => entry.environment);
1187
1401
  if (offered.length === 0) {
1188
1402
  return;
1189
1403
  }
@@ -1202,9 +1416,9 @@ async function offerConfigEdit(options, name, ask) {
1202
1416
  return;
1203
1417
  }
1204
1418
  for (const environment of chosen) {
1205
- const next = setProviderType(current, environment, name);
1419
+ const next = setEnvironmentProvider(current, environment, name);
1206
1420
  if (next === void 0) {
1207
- io.out(`Add \`type: ${JSON.stringify(name)}\` to \`${environment}\` in ${shown}.`);
1421
+ io.out(`Add \`provider: ${JSON.stringify(name)}\` to \`${environment}\` in ${shown}.`);
1208
1422
  continue;
1209
1423
  }
1210
1424
  current = next;
@@ -1482,8 +1696,8 @@ var DEV_PIN_INTEGRITY = "sha512-development-build-not-a-published-release";
1482
1696
  var DEV_PIN_VERSION = "0.0.0-dev";
1483
1697
  var BUNDLED_ENGINE_PIN = {
1484
1698
  package: ENGINE_PACKAGE3,
1485
- version: "0.14.0",
1486
- integrity: "sha512-2EJHq5DSuqyfscjhFZmaszT4dZdYfboJJzyFwTZkJPWdcVZmXODhsqF2xtG+9U/MYojo6rmQzVlHXhVQRnooXg=="
1699
+ version: "0.16.0",
1700
+ integrity: "sha512-DSA07wrSown70tU/9ix9yozJihsQjHFPN3/iXLiH6sS6LYT+/PWBFmVI6C2tH/qaUL2GGh4mgRHL8LGM749C8Q=="
1487
1701
  };
1488
1702
  function assertReleasePin(pin) {
1489
1703
  if (pin.integrity === DEV_PIN_INTEGRITY || pin.version === DEV_PIN_VERSION) {
@@ -1586,7 +1800,7 @@ async function upgrade(options) {
1586
1800
  if (options.noDownload === true) {
1587
1801
  throw new UpgradeNoDownloadError();
1588
1802
  }
1589
- if ((options.ci === true || !io.interactive) && !(request.yes && request.version !== void 0)) {
1803
+ if ((options.ci === true || !io.interactive) && !request.yes) {
1590
1804
  throw new UpgradeUnattendedError();
1591
1805
  }
1592
1806
  const release = await fetchRelease({
@@ -1966,8 +2180,8 @@ async function delegate(options, engine, forwarded, home, cwd) {
1966
2180
  }
1967
2181
 
1968
2182
  export {
1969
- readProviderEntries,
1970
- setProviderType,
2183
+ readEnvironmentProviders,
2184
+ setEnvironmentProvider,
1971
2185
  INSTALL_COMMAND,
1972
2186
  UPGRADE_COMMAND,
1973
2187
  YES_FLAG,
@@ -2003,6 +2217,7 @@ export {
2003
2217
  TrustReasonMissingError,
2004
2218
  DeclarationMissingError,
2005
2219
  DeclarationNotSelfContainedError,
2220
+ DeclarationReservedFieldError,
2006
2221
  EnginePinUnreleasedError,
2007
2222
  EnginePinMismatchError,
2008
2223
  UpgradeSubjectError,
@@ -2044,4 +2259,4 @@ export {
2044
2259
  upgrade,
2045
2260
  runLauncher
2046
2261
  };
2047
- //# sourceMappingURL=chunk-WA5LM767.js.map
2262
+ //# sourceMappingURL=chunk-HQTTS5BH.js.map