spfn 0.3.0-beta.13 → 0.3.0-beta.14
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 +12 -8
- package/dist/index.js +15 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -495,7 +495,7 @@ reads plain `process.env`.
|
|
|
495
495
|
| `secret rotate [key]` | Rotate values; external secrets are flagged for manual reissue (`-a/--all`) |
|
|
496
496
|
| `secret keygen` | Generate an age key pair for the SOPS no-cloud backend |
|
|
497
497
|
| `secret recipients <add\|remove\|list> [age1…]` | Manage `.sops.yaml` recipients + re-encrypt |
|
|
498
|
-
| `secret check` | Static lint — flag plaintext secret leaks; with a deployed `--env`, names
|
|
498
|
+
| `secret check` | Static lint — flag plaintext secret leaks; with a deployed `--env`, names outside their declared layer or in both files |
|
|
499
499
|
| `secret export` | Decrypt and merge one deployment's layers into a 0600 dotenv file (prints names only) |
|
|
500
500
|
|
|
501
501
|
Options: `-e, --env <env>` (`local` default; also `development`/`staging`/`production`),
|
|
@@ -508,11 +508,15 @@ whole of stdin as the value — one trailing newline dropped, inner newlines kep
|
|
|
508
508
|
local and deployed targets alike. It never prompts, and refuses a terminal on stdin.
|
|
509
509
|
Shaped values such as key pairs come from the app's own command, piped in.
|
|
510
510
|
|
|
511
|
-
**Layers.** A schema entry's `layer` (`'environment'`
|
|
512
|
-
deployment's value
|
|
513
|
-
|
|
514
|
-
`--instance blue`, on
|
|
515
|
-
the other layer is refused.
|
|
511
|
+
**Layers.** A schema entry's `layer` (`'environment'` or `'instance'`) pins where a
|
|
512
|
+
deployment's value must live; unset, the value may come from any layer. Without
|
|
513
|
+
`--instance`, `set`/`generate`/`rotate`/`list` on a deployed environment work on
|
|
514
|
+
`secrets/<env>.enc.json`; with `--instance blue`, on `secrets/<env>.blue.enc.json`. A named
|
|
515
|
+
key declared with the other layer is refused; a key without `layer` is accepted by either.
|
|
516
|
+
`--all` (and `list`) takes the names declared with the target's layer, and names without
|
|
517
|
+
`layer` on the environment target only — so `--all --instance blue` never fills an
|
|
518
|
+
instance file with names every instance shares. To give each instance its own value of
|
|
519
|
+
such a name, set it by name with `--instance`.
|
|
516
520
|
|
|
517
521
|
**Export.**
|
|
518
522
|
|
|
@@ -525,8 +529,8 @@ Decrypts `secrets/<env>.enc.json` and — with `--instance`, when it exists —
|
|
|
525
529
|
instance values such as port and URLs), and writes the merge to `--out`. Only names in the
|
|
526
530
|
whole-app list are written; an unlisted name in an encrypted file is a warning by name.
|
|
527
531
|
The export fails, writing nothing, when a required name is in no layer, a name is in two
|
|
528
|
-
layers,
|
|
529
|
-
instance layer, a value fails its validator (the error names the variable and the
|
|
532
|
+
layers, a name declared `instance` is in the environment file, a name declared
|
|
533
|
+
`environment` is in an instance layer, a value fails its validator (the error names the variable and the
|
|
530
534
|
reason, never the value), or decryption fails. `--out` is required and its directory must
|
|
531
535
|
exist; relative paths resolve from the working directory. The file is created 0600 beside
|
|
532
536
|
the destination and renamed over it, so a failure leaves the previous file untouched
|
package/dist/index.js
CHANGED
|
@@ -949,7 +949,7 @@ import { dirname as dirname2, join as join10 } from "path";
|
|
|
949
949
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
950
950
|
function getCliVersion() {
|
|
951
951
|
if (true) {
|
|
952
|
-
return "0.3.0-beta.
|
|
952
|
+
return "0.3.0-beta.14";
|
|
953
953
|
}
|
|
954
954
|
return fromPackageJson ?? (fromPackageJson = readPackageVersion());
|
|
955
955
|
}
|
|
@@ -5375,8 +5375,8 @@ function secretEntries(schema) {
|
|
|
5375
5375
|
function generatableSecrets(schema) {
|
|
5376
5376
|
return secretEntries(schema).filter((entry) => !!entry.generate);
|
|
5377
5377
|
}
|
|
5378
|
-
function
|
|
5379
|
-
return entry.layer
|
|
5378
|
+
function allowsLayer(entry, layer) {
|
|
5379
|
+
return entry.layer === void 0 || entry.layer === layer;
|
|
5380
5380
|
}
|
|
5381
5381
|
|
|
5382
5382
|
// src/utils/env-list.ts
|
|
@@ -5432,7 +5432,7 @@ function describeEnvList(options) {
|
|
|
5432
5432
|
return options.package ?? "whole app";
|
|
5433
5433
|
}
|
|
5434
5434
|
function describeRules(entry) {
|
|
5435
|
-
return `"${entry.type}" (${entry.required ? "required" : "optional"}${entry.sensitive ? ", sensitive" : ""}${
|
|
5435
|
+
return `"${entry.type}" (${entry.required ? "required" : "optional"}${entry.sensitive ? ", sensitive" : ""}${entry.layer ? `, ${entry.layer} layer` : ""})`;
|
|
5436
5436
|
}
|
|
5437
5437
|
async function loadAppSchemas(cwd) {
|
|
5438
5438
|
const paths = (await loadAppConfig3(cwd)).env?.schemas ?? [];
|
|
@@ -7514,15 +7514,15 @@ function targetLayer(target) {
|
|
|
7514
7514
|
}
|
|
7515
7515
|
function entriesForTarget(entries, target) {
|
|
7516
7516
|
const layer = targetLayer(target);
|
|
7517
|
-
return layer ? entries.filter((entry) =>
|
|
7517
|
+
return layer ? entries.filter((entry) => (entry.layer ?? "environment") === layer) : entries;
|
|
7518
7518
|
}
|
|
7519
7519
|
function assertTargetLayer(entry, target) {
|
|
7520
7520
|
const layer = targetLayer(target);
|
|
7521
|
-
if (!entry || !layer ||
|
|
7521
|
+
if (!entry || !layer || allowsLayer(entry, layer)) {
|
|
7522
7522
|
return;
|
|
7523
7523
|
}
|
|
7524
7524
|
const hint = layer === "environment" ? "pass --instance <name>" : "drop --instance";
|
|
7525
|
-
logger.error(`${entry.key} is declared with layer "${
|
|
7525
|
+
logger.error(`${entry.key} is declared with layer "${entry.layer}" \u2014 ${hint}.`);
|
|
7526
7526
|
process.exit(1);
|
|
7527
7527
|
}
|
|
7528
7528
|
|
|
@@ -7946,6 +7946,7 @@ async function secretCheck(options) {
|
|
|
7946
7946
|
}
|
|
7947
7947
|
function checkPlacement(cwd, schema, target, issues, warnings) {
|
|
7948
7948
|
const files = [{ file: getSopsFile(cwd, target.env), layer: "environment" }];
|
|
7949
|
+
const seenIn = /* @__PURE__ */ new Map();
|
|
7949
7950
|
if (target.instance) {
|
|
7950
7951
|
files.push({ file: getSopsFile(cwd, target.env, target.instance), layer: "instance" });
|
|
7951
7952
|
}
|
|
@@ -7954,9 +7955,12 @@ function checkPlacement(cwd, schema, target, issues, warnings) {
|
|
|
7954
7955
|
const entry = Object.hasOwn(schema, name) ? schema[name] : void 0;
|
|
7955
7956
|
if (!entry) {
|
|
7956
7957
|
warnings.push(`${chalk43.cyan(name)} in ${chalk43.yellow(file.relFile)} is not in the env list \u2014 export skips it.`);
|
|
7957
|
-
} else if (
|
|
7958
|
-
issues.push(`${chalk43.cyan(name)} is declared with layer "${
|
|
7958
|
+
} else if (!allowsLayer(entry, layer)) {
|
|
7959
|
+
issues.push(`${chalk43.cyan(name)} is declared with layer "${entry.layer}" but is in ${chalk43.yellow(file.relFile)}.`);
|
|
7960
|
+
} else if (seenIn.has(name)) {
|
|
7961
|
+
issues.push(`${chalk43.cyan(name)} is in both ${chalk43.yellow(seenIn.get(name))} and ${chalk43.yellow(file.relFile)}.`);
|
|
7959
7962
|
}
|
|
7963
|
+
seenIn.set(name, file.relFile);
|
|
7960
7964
|
}
|
|
7961
7965
|
}
|
|
7962
7966
|
}
|
|
@@ -8023,8 +8027,8 @@ function placementError(entry, found) {
|
|
|
8023
8027
|
if (found.length > 1) {
|
|
8024
8028
|
return `in more than one layer: ${found.map((source) => source.label).join(" and ")}`;
|
|
8025
8029
|
}
|
|
8026
|
-
if (found[0].kind
|
|
8027
|
-
return `declared with layer "${
|
|
8030
|
+
if (!allowsLayer(entry, found[0].kind)) {
|
|
8031
|
+
return `declared with layer "${entry.layer}", but found in ${found[0].label}`;
|
|
8028
8032
|
}
|
|
8029
8033
|
return void 0;
|
|
8030
8034
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spfn",
|
|
3
|
-
"version": "0.3.0-beta.
|
|
3
|
+
"version": "0.3.0-beta.14",
|
|
4
4
|
"description": "Scaffold a full-stack TypeScript backend onto a Next.js app built with an AI coding agent: auth, database, typed routes and codegen, one fixed vertical slice per feature",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|