@everystack/cli 0.4.60 → 0.4.61
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/package.json +2 -2
- package/src/backfill.ts +3 -3
- package/src/cli/alter-type-dependents.ts +1 -1
- package/src/cli/apply-authority.ts +1 -1
- package/src/cli/apply-execute.ts +10 -10
- package/src/cli/audit-api.ts +4 -4
- package/src/cli/audit-source-api.ts +2 -2
- package/src/cli/authz-adoption-class.ts +4 -4
- package/src/cli/authz-baseline.ts +2 -2
- package/src/cli/authz-canonical.ts +2 -2
- package/src/cli/authz-compile.ts +3 -3
- package/src/cli/authz-contract-io.ts +2 -2
- package/src/cli/authz-contract.ts +4 -4
- package/src/cli/authz-derive.ts +2 -2
- package/src/cli/authz-identity.ts +1 -1
- package/src/cli/authz-ownership.ts +1 -1
- package/src/cli/authz-reconcile.ts +4 -4
- package/src/cli/authz-redteam.ts +1 -1
- package/src/cli/authz-render.ts +2 -2
- package/src/cli/aws.ts +53 -4
- package/src/cli/backfill.ts +3 -3
- package/src/cli/branch-db.ts +5 -5
- package/src/cli/bundle-weight.ts +2 -2
- package/src/cli/commands/analyze.ts +2 -2
- package/src/cli/commands/audit.ts +10 -10
- package/src/cli/commands/bundle.ts +6 -6
- package/src/cli/commands/cache.ts +75 -3
- package/src/cli/commands/console.ts +6 -6
- package/src/cli/commands/db-apply.ts +25 -25
- package/src/cli/commands/db-approvers.ts +4 -4
- package/src/cli/commands/db-authz.ts +15 -15
- package/src/cli/commands/db-backfill.ts +10 -10
- package/src/cli/commands/db-backup.ts +7 -7
- package/src/cli/commands/db-branch.ts +8 -8
- package/src/cli/commands/db-build.ts +7 -7
- package/src/cli/commands/db-check.ts +16 -16
- package/src/cli/commands/db-diff.ts +6 -6
- package/src/cli/commands/db-exec.ts +8 -8
- package/src/cli/commands/db-export.ts +10 -10
- package/src/cli/commands/db-fingerprint.ts +14 -14
- package/src/cli/commands/db-fork.ts +6 -6
- package/src/cli/commands/db-generate.ts +19 -19
- package/src/cli/commands/db-plan.ts +25 -25
- package/src/cli/commands/db-pull.ts +18 -18
- package/src/cli/commands/db-reconcile.ts +14 -14
- package/src/cli/commands/db-refresh.ts +7 -7
- package/src/cli/commands/db-snapshot.ts +5 -5
- package/src/cli/commands/db-swap.ts +26 -26
- package/src/cli/commands/db-sync.ts +18 -18
- package/src/cli/commands/db.ts +15 -15
- package/src/cli/commands/deploy.ts +39 -5
- package/src/cli/commands/diag.ts +4 -4
- package/src/cli/commands/lighthouse.ts +3 -3
- package/src/cli/commands/logs.ts +5 -5
- package/src/cli/commands/pipeline-run.ts +7 -7
- package/src/cli/commands/runbook.ts +5 -5
- package/src/cli/commands/secrets.ts +5 -5
- package/src/cli/commands/security-probe.ts +4 -4
- package/src/cli/commands/security.ts +6 -6
- package/src/cli/commands/status.ts +3 -3
- package/src/cli/commands/task-probe.ts +4 -4
- package/src/cli/commands/ui-audit.ts +5 -5
- package/src/cli/commands/update.ts +64 -8
- package/src/cli/component-audit.ts +5 -5
- package/src/cli/config.ts +2 -2
- package/src/cli/db-build.ts +6 -6
- package/src/cli/db-source.ts +2 -2
- package/src/cli/declared-derived.ts +5 -5
- package/src/cli/declared-diff.ts +5 -5
- package/src/cli/deploy-probe.ts +3 -3
- package/src/cli/derived-apply.ts +5 -5
- package/src/cli/derived-compile.ts +4 -4
- package/src/cli/derived-grants.ts +1 -1
- package/src/cli/derived-introspect.ts +3 -3
- package/src/cli/derived-lint.ts +2 -2
- package/src/cli/derived-plan.ts +5 -5
- package/src/cli/derived-render.ts +6 -6
- package/src/cli/direct-venue.ts +2 -2
- package/src/cli/discover.ts +2 -2
- package/src/cli/edge-plan.ts +8 -8
- package/src/cli/exec-digest.ts +1 -1
- package/src/cli/exec-execute.ts +1 -1
- package/src/cli/exec-log.ts +1 -1
- package/src/cli/exec-run.ts +2 -2
- package/src/cli/git-descent.ts +5 -5
- package/src/cli/index.ts +46 -46
- package/src/cli/migration-compile.ts +7 -7
- package/src/cli/migration-generate.ts +6 -6
- package/src/cli/model-api.ts +16 -16
- package/src/cli/model-render.ts +6 -6
- package/src/cli/mutation-lease.ts +1 -1
- package/src/cli/observability.ts +1 -1
- package/src/cli/refresh-execute.ts +2 -2
- package/src/cli/runbook/generate.ts +3 -3
- package/src/cli/runbook/index.ts +7 -7
- package/src/cli/runbook/sections.ts +2 -2
- package/src/cli/schema-compile.ts +4 -4
- package/src/cli/schema-diff.ts +1 -1
- package/src/cli/schema-fingerprint.ts +7 -7
- package/src/cli/schema-introspect.ts +3 -3
- package/src/cli/schema-source.ts +1 -1
- package/src/cli/schema-swap.ts +4 -4
- package/src/cli/security-catalog.ts +1 -1
- package/src/cli/stage-backup.ts +3 -3
- package/src/cli/stage-read-consistency.ts +6 -6
- package/src/cli/state-apply.ts +7 -7
- package/src/cli/swap-execute.ts +3 -3
- package/src/cli/swap-heartbeat.ts +1 -1
- package/src/cli/swap-pair.ts +7 -7
- package/src/cli/task-poll.ts +2 -2
- package/src/cli/ui-bloat.ts +1 -1
- package/src/cli/utils/secrets.ts +1 -1
- package/src/exec.ts +7 -7
- package/src/reconcile.ts +2 -2
- package/src/refresh.ts +2 -2
- package/src/sst.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@everystack/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.61",
|
|
4
4
|
"description": "CLI and OTA updates for Expo apps on everystack",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"author": "Scalable Technology, Inc. <licensing@scalable.technology>",
|
|
@@ -160,7 +160,7 @@
|
|
|
160
160
|
"jest": "29.7.0",
|
|
161
161
|
"react": "19.2.0",
|
|
162
162
|
"ts-jest": "29.4.9",
|
|
163
|
-
"@everystack/server": "0.4.
|
|
163
|
+
"@everystack/server": "0.4.22"
|
|
164
164
|
},
|
|
165
165
|
"scripts": {
|
|
166
166
|
"test": "jest",
|
package/src/backfill.ts
CHANGED
|
@@ -13,11 +13,11 @@ export {
|
|
|
13
13
|
readBackfillLog,
|
|
14
14
|
executeBackfills,
|
|
15
15
|
markBackfillApplied,
|
|
16
|
-
} from './cli/backfill
|
|
16
|
+
} from './cli/backfill';
|
|
17
17
|
export type {
|
|
18
18
|
BackfillPlan,
|
|
19
19
|
BackfillRecord,
|
|
20
20
|
BackfillRunOptions,
|
|
21
21
|
BackfillRunResult,
|
|
22
|
-
} from './cli/backfill
|
|
23
|
-
export type { SourceFile } from './cli/derived-source
|
|
22
|
+
} from './cli/backfill';
|
|
23
|
+
export type { SourceFile } from './cli/derived-source';
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* by the command shell.
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
-
import { importOptionalAws } from './aws
|
|
27
|
+
import { importOptionalAws } from './aws';
|
|
28
28
|
|
|
29
29
|
/** The SSM parameter holding the approver set — a JSON array of entries. */
|
|
30
30
|
export function approverParamName(appName: string, stage: string): string {
|
package/src/cli/apply-execute.ts
CHANGED
|
@@ -29,15 +29,15 @@
|
|
|
29
29
|
* and the memoir stay here, atomic with the connection.
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
|
-
import { introspectContract, type QueryRunner, type AuthzContract } from './authz-contract
|
|
33
|
-
import type { SessionRunner } from './session
|
|
34
|
-
import { introspectSchema, type SchemaSnapshot } from './schema-introspect
|
|
35
|
-
import { FUNCTIONS_SQL, contractFunctionRow } from './security-catalog
|
|
36
|
-
import { fingerprintLive } from './schema-fingerprint
|
|
37
|
-
import { governedLiveFingerprint } from './edge-plan
|
|
38
|
-
import { applyGeneratedStatements } from './state-apply
|
|
39
|
-
import { ENSURE_RECONCILER_SQL, renderSchemaLogInsert, renderSchemaLogFingerprintUpdate } from './derived-apply
|
|
40
|
-
import { checkPlanPrecondition, planHash, type EdgePlan } from './edge-plan
|
|
32
|
+
import { introspectContract, type QueryRunner, type AuthzContract } from './authz-contract';
|
|
33
|
+
import type { SessionRunner } from './session';
|
|
34
|
+
import { introspectSchema, type SchemaSnapshot } from './schema-introspect';
|
|
35
|
+
import { FUNCTIONS_SQL, contractFunctionRow } from './security-catalog';
|
|
36
|
+
import { fingerprintLive } from './schema-fingerprint';
|
|
37
|
+
import { governedLiveFingerprint } from './edge-plan';
|
|
38
|
+
import { applyGeneratedStatements } from './state-apply';
|
|
39
|
+
import { ENSURE_RECONCILER_SQL, renderSchemaLogInsert, renderSchemaLogFingerprintUpdate } from './derived-apply';
|
|
40
|
+
import { checkPlanPrecondition, planHash, type EdgePlan } from './edge-plan';
|
|
41
41
|
|
|
42
42
|
export interface ApplyPlanOptions {
|
|
43
43
|
actor?: string | null;
|
|
@@ -237,4 +237,4 @@ export async function executeApplyPlan(
|
|
|
237
237
|
// Re-exported so a single import (`@everystack/cli/apply`) gives a consumer —
|
|
238
238
|
// notably the ops Lambda's db:apply action — everything it needs to validate
|
|
239
239
|
// and run a plan without reaching into the CLI's internal module layout.
|
|
240
|
-
export { PLAN_VERSION, planHash, checkPlanPrecondition, type EdgePlan } from './edge-plan
|
|
240
|
+
export { PLAN_VERSION, planHash, checkPlanPrecondition, type EdgePlan } from './edge-plan';
|
package/src/cli/audit-api.ts
CHANGED
|
@@ -28,7 +28,7 @@ export {
|
|
|
28
28
|
classifyPrivateEnvKeys,
|
|
29
29
|
scanSecretValue,
|
|
30
30
|
summarize,
|
|
31
|
-
} from './bundle-audit
|
|
31
|
+
} from './bundle-audit';
|
|
32
32
|
|
|
33
33
|
export {
|
|
34
34
|
// types
|
|
@@ -45,7 +45,7 @@ export {
|
|
|
45
45
|
scanSourceMapsPresent,
|
|
46
46
|
scanDependencyWeight,
|
|
47
47
|
resolveWeightBudget,
|
|
48
|
-
} from './bundle-weight
|
|
48
|
+
} from './bundle-weight';
|
|
49
49
|
|
|
50
50
|
export {
|
|
51
51
|
// types
|
|
@@ -59,7 +59,7 @@ export {
|
|
|
59
59
|
scanHardcodedColors,
|
|
60
60
|
scanBundleUiBloat,
|
|
61
61
|
resolveUiBloatBudget,
|
|
62
|
-
} from './ui-bloat
|
|
62
|
+
} from './ui-bloat';
|
|
63
63
|
|
|
64
64
|
// NOTE: the source-side AST scanners (component-audit + component-model) moved to
|
|
65
65
|
// `@everystack/cli/audit/source` — they pull in `typescript`, which this barrel must
|
|
@@ -76,4 +76,4 @@ export {
|
|
|
76
76
|
totalSizes,
|
|
77
77
|
bucketSource,
|
|
78
78
|
composeSourceMap,
|
|
79
|
-
} from './bundle
|
|
79
|
+
} from './bundle';
|
|
@@ -22,7 +22,7 @@ export {
|
|
|
22
22
|
scanDuplicateJsx,
|
|
23
23
|
scanDuplicateComponents,
|
|
24
24
|
auditComponents,
|
|
25
|
-
} from './component-audit
|
|
25
|
+
} from './component-audit';
|
|
26
26
|
|
|
27
27
|
export {
|
|
28
28
|
// types — the faithful component model (shared with the 5b frontend Module)
|
|
@@ -33,4 +33,4 @@ export {
|
|
|
33
33
|
// the source-side foundation: parse/discover .tsx into neutral descriptors
|
|
34
34
|
parseComponents,
|
|
35
35
|
discoverComponents,
|
|
36
|
-
} from './component-model
|
|
36
|
+
} from './component-model';
|
|
@@ -28,10 +28,10 @@
|
|
|
28
28
|
* output so the two can never drift apart.
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
|
-
import { matchPolicies, roleSetEqual } from './authz-identity
|
|
32
|
-
import { governedRoleSet } from './authz-reconcile
|
|
33
|
-
import { effectivePolicyCheck, holdsPrivilege, isPolicyDead, policyCommands } from './authz-contract
|
|
34
|
-
import type { AuthzContract, PolicyContract, TableContract } from './authz-contract
|
|
31
|
+
import { matchPolicies, roleSetEqual } from './authz-identity';
|
|
32
|
+
import { governedRoleSet } from './authz-reconcile';
|
|
33
|
+
import { effectivePolicyCheck, holdsPrivilege, isPolicyDead, policyCommands } from './authz-contract';
|
|
34
|
+
import type { AuthzContract, PolicyContract, TableContract } from './authz-contract';
|
|
35
35
|
|
|
36
36
|
export type AdoptionClass = 'convention' | 'capability' | 'narrowing' | 'dead' | 'unclassified';
|
|
37
37
|
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
|
|
37
37
|
import fs from 'node:fs/promises';
|
|
38
38
|
import path from 'node:path';
|
|
39
|
-
import { FINGERPRINT_VERSION } from './schema-fingerprint
|
|
40
|
-
import type { GrantExemption } from './authz-reconcile
|
|
39
|
+
import { FINGERPRINT_VERSION } from './schema-fingerprint';
|
|
40
|
+
import type { GrantExemption } from './authz-reconcile';
|
|
41
41
|
|
|
42
42
|
/** The repo-relative artifact. Generated — regenerating it must reproduce it byte for byte. */
|
|
43
43
|
export const BASELINE_FILE = 'db/authz-baseline.json';
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
* the same identity break in the opposite direction, and it is the harder one to notice.
|
|
32
32
|
*/
|
|
33
33
|
|
|
34
|
-
import type { PolicyContract, TableContract } from './authz-contract
|
|
35
|
-
import { effectivePolicyCheck, isPolicyDead, isPolicySubsumed } from './authz-contract
|
|
34
|
+
import type { PolicyContract, TableContract } from './authz-contract';
|
|
35
|
+
import { effectivePolicyCheck, isPolicyDead, isPolicySubsumed } from './authz-contract';
|
|
36
36
|
|
|
37
37
|
/** PostgreSQL's open role set, kept whole. */
|
|
38
38
|
const PUBLIC_ROLE = 'public';
|
package/src/cli/authz-compile.ts
CHANGED
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
23
|
import { isColumnAbility, type ModelDescriptor, type Ability } from '@everystack/model';
|
|
24
|
-
import type { PolicyContract, TableContract, PolicyCommand } from './authz-contract
|
|
25
|
-
import { parenthesizeOnce } from './authz-contract
|
|
26
|
-
import { normalizeDeparsedExpr } from './deparse-normal
|
|
24
|
+
import type { PolicyContract, TableContract, PolicyCommand } from './authz-contract';
|
|
25
|
+
import { parenthesizeOnce } from './authz-contract';
|
|
26
|
+
import { normalizeDeparsedExpr } from './deparse-normal';
|
|
27
27
|
|
|
28
28
|
export interface CompileOptions {
|
|
29
29
|
/** Schema the table lives in. Default: `public`. */
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
|
|
10
10
|
import fs from 'node:fs/promises';
|
|
11
11
|
import path from 'node:path';
|
|
12
|
-
import type { AuthzContract, TableContract, FunctionContract } from './authz-contract
|
|
13
|
-
import { renderContractMarkdown } from './authz-render
|
|
12
|
+
import type { AuthzContract, TableContract, FunctionContract } from './authz-contract';
|
|
13
|
+
import { renderContractMarkdown } from './authz-render';
|
|
14
14
|
|
|
15
15
|
/** The human-readable review doc written alongside the JSON. */
|
|
16
16
|
export const REVIEW_FILE = 'AUTHORIZATION.md';
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
* - The contract is a strict sublayer of the Model: defineModel compiles to it.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import { parsePgArray } from './security-catalog
|
|
25
|
-
import { INTROSPECTION_SESSION, type SessionRunner } from './session
|
|
26
|
-
import { matchPolicies, roleSetEqual } from './authz-identity
|
|
27
|
-
import { normalizeDeparsedExpr } from './deparse-normal
|
|
24
|
+
import { parsePgArray } from './security-catalog';
|
|
25
|
+
import { INTROSPECTION_SESSION, type SessionRunner } from './session';
|
|
26
|
+
import { matchPolicies, roleSetEqual } from './authz-identity';
|
|
27
|
+
import { normalizeDeparsedExpr } from './deparse-normal';
|
|
28
28
|
|
|
29
29
|
// ---------------------------------------------------------------------------
|
|
30
30
|
// The contract format — the frozen, reviewable, version-controlled shape.
|
package/src/cli/authz-derive.ts
CHANGED
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
28
|
import { EXTRA_PRIVILEGES } from '@everystack/model';
|
|
29
|
-
import { holdsPrivilege, holdsTablePrivilege } from './authz-contract
|
|
30
|
-
import type { TableContract, PolicyContract } from './authz-contract
|
|
29
|
+
import { holdsPrivilege, holdsTablePrivilege } from './authz-contract';
|
|
30
|
+
import type { TableContract, PolicyContract } from './authz-contract';
|
|
31
31
|
|
|
32
32
|
/** One rendered decision: either an ability line, or a comment explaining the omission. */
|
|
33
33
|
export interface DerivedAbilities {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
* introspection's search_path is what closes that hole.
|
|
35
35
|
*/
|
|
36
36
|
|
|
37
|
-
import { effectivePolicyCheck, type PolicyContract } from './authz-contract
|
|
37
|
+
import { effectivePolicyCheck, type PolicyContract } from './authz-contract';
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
40
|
* Do two role lists name the same set?
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
27
|
import type { WrittenBy, ModelDescriptor } from '@everystack/model';
|
|
28
|
-
import { IGNORED_SCHEMAS, type QueryRunner, type AuthzContract } from './authz-contract
|
|
28
|
+
import { IGNORED_SCHEMAS, type QueryRunner, type AuthzContract } from './authz-contract';
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* Every base table with its owner. The exclusions are RLS_SQL's, verbatim — base
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
* this layer reconciles without a migration file (unlike the data layer).
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import type { AuthzContract, TableContract, PolicyContract, SequenceGrantContract } from './authz-contract
|
|
19
|
-
import { effectivePolicyCheck, parenthesizeOnce, isPolicyDead, isPolicySubsumed } from './authz-contract
|
|
20
|
-
import { matchPolicies } from './authz-identity
|
|
21
|
-
import { quoteQualified } from './pg-ident
|
|
18
|
+
import type { AuthzContract, TableContract, PolicyContract, SequenceGrantContract } from './authz-contract';
|
|
19
|
+
import { effectivePolicyCheck, parenthesizeOnce, isPolicyDead, isPolicySubsumed } from './authz-contract';
|
|
20
|
+
import { matchPolicies } from './authz-identity';
|
|
21
|
+
import { quoteQualified } from './pg-ident';
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Always governed, whatever the models say. `PUBLIC` because a grant to PUBLIC is the
|
package/src/cli/authz-redteam.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* discards. Nothing persists; one round trip. The builder and evaluator here are pure.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import type { AuthzContract, TableContract } from './authz-contract
|
|
18
|
+
import type { AuthzContract, TableContract } from './authz-contract';
|
|
19
19
|
|
|
20
20
|
export type ProbeCommand = 'SELECT' | 'INSERT' | 'UPDATE' | 'DELETE';
|
|
21
21
|
export const PROBE_COMMANDS: ProbeCommand[] = ['SELECT', 'INSERT', 'UPDATE', 'DELETE'];
|
package/src/cli/authz-render.ts
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* throwaway. Anything it does not recognise is shown verbatim, never hidden.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import type { AuthzContract, TableContract, PolicyContract } from './authz-contract
|
|
17
|
-
import { hasPrivilege } from './authz-redteam
|
|
16
|
+
import type { AuthzContract, TableContract, PolicyContract } from './authz-contract';
|
|
17
|
+
import { hasPrivilege } from './authz-redteam';
|
|
18
18
|
|
|
19
19
|
/** True when the expression resolves to the signed-in user's id (any of the forms). */
|
|
20
20
|
function isCurrentUserExpr(s: string): boolean {
|
package/src/cli/aws.ts
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* Authentication uses the developer's IAM credentials (default credential chain).
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { isOpsHandlerCrash, formatOpsCrashReport, fetchDiagnosisWithRetry } from './ops-diagnostics
|
|
9
|
-
import { tagInvokeTransport } from './ops-advice
|
|
10
|
-
import type { SessionRunner } from './session
|
|
8
|
+
import { isOpsHandlerCrash, formatOpsCrashReport, fetchDiagnosisWithRetry } from './ops-diagnostics';
|
|
9
|
+
import { tagInvokeTransport } from './ops-advice';
|
|
10
|
+
import type { SessionRunner } from './session';
|
|
11
11
|
|
|
12
12
|
let s3Client: InstanceType<typeof import('@aws-sdk/client-s3').S3Client> | null = null;
|
|
13
13
|
let lambdaClient: InstanceType<typeof import('@aws-sdk/client-lambda').LambdaClient> | null = null;
|
|
@@ -366,7 +366,7 @@ export async function invokeAction(
|
|
|
366
366
|
const live = await resolveFunctionName(region, functionName);
|
|
367
367
|
if (live === functionName) throw tagInvokeTransport(err);
|
|
368
368
|
try {
|
|
369
|
-
const { invalidateCacheByFunctionName } = await import('./discover
|
|
369
|
+
const { invalidateCacheByFunctionName } = await import('./discover');
|
|
370
370
|
await invalidateCacheByFunctionName(functionName);
|
|
371
371
|
} catch {
|
|
372
372
|
// Cache invalidation is best-effort
|
|
@@ -559,6 +559,20 @@ export async function createInvalidation(
|
|
|
559
559
|
return result.Invalidation?.Id;
|
|
560
560
|
}
|
|
561
561
|
|
|
562
|
+
/**
|
|
563
|
+
* Get the deploy status of a CloudFront distribution.
|
|
564
|
+
* Returns 'Deployed' when the distribution is fully propagated.
|
|
565
|
+
*/
|
|
566
|
+
export async function getDistributionStatus(
|
|
567
|
+
region: string,
|
|
568
|
+
distributionId: string,
|
|
569
|
+
): Promise<string> {
|
|
570
|
+
const { CloudFrontClient, GetDistributionCommand } = await import('@aws-sdk/client-cloudfront');
|
|
571
|
+
const client = new CloudFrontClient({ region });
|
|
572
|
+
const result = await client.send(new GetDistributionCommand({ Id: distributionId }));
|
|
573
|
+
return result.Distribution?.Status ?? 'Unknown';
|
|
574
|
+
}
|
|
575
|
+
|
|
562
576
|
/**
|
|
563
577
|
* Write a versioning key to CloudFront KeyValueStore.
|
|
564
578
|
* KVS requires ETag for optimistic concurrency — DescribeKeyValueStore first.
|
|
@@ -712,3 +726,38 @@ export async function describeRdsSnapshots(
|
|
|
712
726
|
summaries.sort((a, b) => (b.created?.getTime() ?? Infinity) - (a.created?.getTime() ?? Infinity));
|
|
713
727
|
return summaries;
|
|
714
728
|
}
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* Force Lambda to retire all warm execution environments by bumping the
|
|
732
|
+
* EVERYSTACK_GEN environment variable. New requests cold-start fresh
|
|
733
|
+
* containers that pick up the latest S3 bundle.
|
|
734
|
+
*
|
|
735
|
+
* The call is a no-code-upload configuration change — it completes in ~2s,
|
|
736
|
+
* and Lambda drains in-flight requests before retiring each environment.
|
|
737
|
+
*/
|
|
738
|
+
export async function rollingRestart(
|
|
739
|
+
region: string,
|
|
740
|
+
functionName: string,
|
|
741
|
+
): Promise<{ previousGen: string; newGen: string }> {
|
|
742
|
+
const client = await getLambda(region);
|
|
743
|
+
const {
|
|
744
|
+
GetFunctionConfigurationCommand,
|
|
745
|
+
UpdateFunctionConfigurationCommand,
|
|
746
|
+
} = await import('@aws-sdk/client-lambda');
|
|
747
|
+
|
|
748
|
+
const current = await client.send(
|
|
749
|
+
new GetFunctionConfigurationCommand({ FunctionName: functionName }),
|
|
750
|
+
);
|
|
751
|
+
const env = current.Environment?.Variables ?? {};
|
|
752
|
+
const previousGen = env.EVERYSTACK_GEN ?? '0';
|
|
753
|
+
const newGen = String(Number(previousGen) + 1);
|
|
754
|
+
|
|
755
|
+
await client.send(new UpdateFunctionConfigurationCommand({
|
|
756
|
+
FunctionName: functionName,
|
|
757
|
+
Environment: {
|
|
758
|
+
Variables: { ...env, EVERYSTACK_GEN: newGen },
|
|
759
|
+
},
|
|
760
|
+
}));
|
|
761
|
+
|
|
762
|
+
return { previousGen, newGen };
|
|
763
|
+
}
|
package/src/cli/backfill.ts
CHANGED
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
import { createHash } from 'node:crypto';
|
|
27
|
-
import { lex, normalizeSql, splitSqlStatements, type SourceFile } from './derived-source
|
|
28
|
-
import { escapeLiteral } from './derived-apply
|
|
29
|
-
import type { QueryRunner } from './authz-contract
|
|
27
|
+
import { lex, normalizeSql, splitSqlStatements, type SourceFile } from './derived-source';
|
|
28
|
+
import { escapeLiteral } from './derived-apply';
|
|
29
|
+
import type { QueryRunner } from './authz-contract';
|
|
30
30
|
|
|
31
31
|
// ---------------------------------------------------------------------------
|
|
32
32
|
// Identity + advisories (pure).
|
package/src/cli/branch-db.ts
CHANGED
|
@@ -22,18 +22,18 @@
|
|
|
22
22
|
import { createHash } from 'node:crypto';
|
|
23
23
|
import { execSync } from 'node:child_process';
|
|
24
24
|
import type { ModelDescriptor } from '@everystack/model';
|
|
25
|
-
import type { QueryRunner } from './authz-contract
|
|
26
|
-
import { escapeLiteral } from './derived-apply
|
|
27
|
-
import { createUrlRunner } from './db-source
|
|
25
|
+
import type { QueryRunner } from './authz-contract';
|
|
26
|
+
import { escapeLiteral } from './derived-apply';
|
|
27
|
+
import { createUrlRunner } from './db-source';
|
|
28
28
|
import {
|
|
29
29
|
withDatabase,
|
|
30
30
|
createDatabase,
|
|
31
31
|
dropDatabase,
|
|
32
32
|
buildIntoDatabase,
|
|
33
33
|
type BuildOptions,
|
|
34
|
-
} from './db-build
|
|
34
|
+
} from './db-build';
|
|
35
35
|
|
|
36
|
-
export { withDatabase } from './db-build
|
|
36
|
+
export { withDatabase } from './db-build';
|
|
37
37
|
|
|
38
38
|
// ---------------------------------------------------------------------------
|
|
39
39
|
// Naming (pure).
|
package/src/cli/bundle-weight.ts
CHANGED
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
* without touching the filesystem.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
import type { AuditFinding } from './bundle-audit
|
|
22
|
-
import type { Composition } from './bundle
|
|
21
|
+
import type { AuditFinding } from './bundle-audit';
|
|
22
|
+
import type { Composition } from './bundle';
|
|
23
23
|
|
|
24
24
|
/** One file's relative path and byte size — the only input the gates need. */
|
|
25
25
|
export interface AssetFile {
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import path from 'node:path';
|
|
12
|
-
import { analyzeSSRPatterns, generateSSRReport } from '../ssr-analyzer
|
|
13
|
-
import { step, success, fail, info } from '../output
|
|
12
|
+
import { analyzeSSRPatterns, generateSSRReport } from '../ssr-analyzer';
|
|
13
|
+
import { step, success, fail, info } from '../output';
|
|
14
14
|
|
|
15
15
|
export async function analyzeSSRCommand(
|
|
16
16
|
_positional: string | undefined,
|
|
@@ -8,16 +8,16 @@
|
|
|
8
8
|
* the heavier `audit:lighthouse` stay separate commands (run them explicitly).
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { resolveConfig, opsFunction } from '../config
|
|
12
|
-
import { runBundleAudit, runBundleWeightAudit, reportBundleAudit } from './bundle
|
|
13
|
-
import { runUiAudit, reportUiAudit } from './ui-audit
|
|
14
|
-
import { checkRunbook } from './runbook
|
|
15
|
-
import { resolveWeightBudget } from '../bundle-weight
|
|
16
|
-
import { runSecurityProbe, reportProbeResults } from './security-probe
|
|
17
|
-
import { auditDeployedSql, printReport, loadWaivers } from './security
|
|
18
|
-
import { collectLighthouseRuns } from './lighthouse
|
|
19
|
-
import { aggregate, formatLighthouseReport } from '../lighthouse
|
|
20
|
-
import { step, info, fail, success, warn } from '../output
|
|
11
|
+
import { resolveConfig, opsFunction } from '../config';
|
|
12
|
+
import { runBundleAudit, runBundleWeightAudit, reportBundleAudit } from './bundle';
|
|
13
|
+
import { runUiAudit, reportUiAudit } from './ui-audit';
|
|
14
|
+
import { checkRunbook } from './runbook';
|
|
15
|
+
import { resolveWeightBudget } from '../bundle-weight';
|
|
16
|
+
import { runSecurityProbe, reportProbeResults } from './security-probe';
|
|
17
|
+
import { auditDeployedSql, printReport, loadWaivers } from './security';
|
|
18
|
+
import { collectLighthouseRuns } from './lighthouse';
|
|
19
|
+
import { aggregate, formatLighthouseReport } from '../lighthouse';
|
|
20
|
+
import { step, info, fail, success, warn } from '../output';
|
|
21
21
|
|
|
22
22
|
export async function auditCommand(flags: Record<string, string>): Promise<void> {
|
|
23
23
|
step('Resolving deployed config...');
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
formatBundleReport,
|
|
18
18
|
type ChunkInput,
|
|
19
19
|
type Composition,
|
|
20
|
-
} from '../bundle
|
|
20
|
+
} from '../bundle';
|
|
21
21
|
import {
|
|
22
22
|
scanSecretShapes,
|
|
23
23
|
extractBundleUrls,
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
scanSecretValue,
|
|
28
28
|
summarize,
|
|
29
29
|
type AuditFinding,
|
|
30
|
-
} from '../bundle-audit
|
|
30
|
+
} from '../bundle-audit';
|
|
31
31
|
import {
|
|
32
32
|
scanAssetWeights,
|
|
33
33
|
scanTotalWeight,
|
|
@@ -42,14 +42,14 @@ import {
|
|
|
42
42
|
DEFAULT_WEIGHT_BUDGET,
|
|
43
43
|
type AssetFile,
|
|
44
44
|
type WeightBudget,
|
|
45
|
-
} from '../bundle-weight
|
|
45
|
+
} from '../bundle-weight';
|
|
46
46
|
import {
|
|
47
47
|
scanBundleUiBloat,
|
|
48
48
|
DEFAULT_UI_BLOAT_BUDGET,
|
|
49
49
|
type UiBloatBudget,
|
|
50
|
-
} from '../ui-bloat
|
|
51
|
-
import { resolveConfig } from '../config
|
|
52
|
-
import { fail, info, step, success, warn } from '../output
|
|
50
|
+
} from '../ui-bloat';
|
|
51
|
+
import { resolveConfig } from '../config';
|
|
52
|
+
import { fail, info, step, success, warn } from '../output';
|
|
53
53
|
|
|
54
54
|
// Expo Router web export puts client chunks under client/_expo/...; a plain
|
|
55
55
|
// `expo export` omits the client/ prefix. Convention, not config.
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
* Writes directly to CloudFront KVS — no Lambda invoke.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { resolveConfig } from '../config
|
|
13
|
-
import { putKvsKey, createInvalidation } from '../aws
|
|
14
|
-
import { step, success, warn, fail, info } from '../output
|
|
12
|
+
import { resolveConfig } from '../config';
|
|
13
|
+
import { putKvsKey, createInvalidation, getDistributionStatus, getFromS3 } from '../aws';
|
|
14
|
+
import { step, success, warn, fail, info } from '../output';
|
|
15
15
|
|
|
16
16
|
const VALID_ORIGINS = ['api', 'media', 'web'] as const;
|
|
17
17
|
type CacheOrigin = typeof VALID_ORIGINS[number];
|
|
@@ -32,6 +32,19 @@ export async function cachePurgeCommand(flags: Record<string, string>): Promise<
|
|
|
32
32
|
process.exit(1);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
const force = flags.force === 'true';
|
|
36
|
+
|
|
37
|
+
// Safety gate: verify the origin is serving the expected release and the distribution is stable.
|
|
38
|
+
if (!force) {
|
|
39
|
+
const channel = flags.channel || flags.stage || 'production';
|
|
40
|
+
const blocked = await preflight(config.region, config.baseUrl, config.updatesBucket, channel, config.distributionId);
|
|
41
|
+
if (blocked) {
|
|
42
|
+
fail(blocked);
|
|
43
|
+
info('Pass --force to override this check.');
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
35
48
|
// Key resolution: --path wins > --origin > global
|
|
36
49
|
let key: string;
|
|
37
50
|
if (flags.path) {
|
|
@@ -91,3 +104,62 @@ export async function cachePurgeCommand(flags: Record<string, string>): Promise<
|
|
|
91
104
|
}
|
|
92
105
|
}
|
|
93
106
|
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Pre-purge safety checks. Returns an error message if blocked, null if clear.
|
|
110
|
+
* @internal Exported for testing.
|
|
111
|
+
*/
|
|
112
|
+
export async function preflight(
|
|
113
|
+
region: string,
|
|
114
|
+
baseUrl: string,
|
|
115
|
+
updatesBucket: string,
|
|
116
|
+
channel: string,
|
|
117
|
+
distributionId?: string,
|
|
118
|
+
): Promise<string | null> {
|
|
119
|
+
// 1. Read the channel pointer to get the expected updateId.
|
|
120
|
+
const pointerKey = `${channel}/web/manifest.json`;
|
|
121
|
+
const pointerData = await getFromS3(region, updatesBucket, pointerKey);
|
|
122
|
+
if (!pointerData) {
|
|
123
|
+
return `No release manifest found for channel "${channel}". Run \`everystack update\` first.`;
|
|
124
|
+
}
|
|
125
|
+
let expectedUpdateId: string;
|
|
126
|
+
try {
|
|
127
|
+
const pointer = JSON.parse(pointerData.toString('utf8'));
|
|
128
|
+
expectedUpdateId = pointer.updateId;
|
|
129
|
+
} catch {
|
|
130
|
+
return `Channel manifest for "${channel}" is malformed.`;
|
|
131
|
+
}
|
|
132
|
+
if (!expectedUpdateId) {
|
|
133
|
+
return `Channel manifest for "${channel}" has no updateId.`;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 2. Probe the origin for the serving release.
|
|
137
|
+
try {
|
|
138
|
+
const probe = `${baseUrl}?_probe=${Date.now()}`;
|
|
139
|
+
const res = await fetch(probe, { redirect: 'follow' });
|
|
140
|
+
const serving = res.headers.get('x-everystack-release');
|
|
141
|
+
if (serving !== expectedUpdateId) {
|
|
142
|
+
return (
|
|
143
|
+
`Origin is serving release ${serving ? serving.slice(0, 12) + '...' : '(none)'} ` +
|
|
144
|
+
`but the channel pointer is ${expectedUpdateId.slice(0, 12)}.... ` +
|
|
145
|
+
`Purging now would expose stale containers to uncached traffic.`
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
} catch (err: any) {
|
|
149
|
+
return `Could not reach origin at ${baseUrl}: ${err.message}`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// 3. Check distribution status (if discoverable).
|
|
153
|
+
if (distributionId) {
|
|
154
|
+
try {
|
|
155
|
+
const status = await getDistributionStatus(region, distributionId);
|
|
156
|
+
if (status !== 'Deployed') {
|
|
157
|
+
return `CloudFront distribution is "${status}" (not Deployed). Wait for propagation to finish.`;
|
|
158
|
+
}
|
|
159
|
+
} catch {
|
|
160
|
+
// Best-effort — missing permission is not a blocker.
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
@@ -15,12 +15,12 @@ import * as readline from 'node:readline';
|
|
|
15
15
|
import * as fs from 'node:fs';
|
|
16
16
|
import * as path from 'node:path';
|
|
17
17
|
import * as os from 'node:os';
|
|
18
|
-
import { resolveConfig, opsFunction, type CliConfig } from '../config
|
|
19
|
-
import { invokeAction } from '../aws
|
|
20
|
-
import { success, fail, info } from '../output
|
|
21
|
-
import { formatResult } from '../utils/table
|
|
22
|
-
import { isComplete } from '../utils/bracket-counter
|
|
23
|
-
import { createCompleter } from '../utils/completer
|
|
18
|
+
import { resolveConfig, opsFunction, type CliConfig } from '../config';
|
|
19
|
+
import { invokeAction } from '../aws';
|
|
20
|
+
import { success, fail, info } from '../output';
|
|
21
|
+
import { formatResult } from '../utils/table';
|
|
22
|
+
import { isComplete } from '../utils/bracket-counter';
|
|
23
|
+
import { createCompleter } from '../utils/completer';
|
|
24
24
|
|
|
25
25
|
const HISTORY_DIR = path.join(os.homedir(), '.everystack');
|
|
26
26
|
const HISTORY_FILE = path.join(HISTORY_DIR, 'console_history');
|