@fjall/util 2.23.0 → 2.24.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/.minified +1 -1
- package/dist/cdkTmpdirCleanup.d.ts +21 -0
- package/dist/cdkTmpdirCleanup.js +1 -0
- package/dist/config.d.ts +2 -6
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/infra/deployArtefacts.d.ts +77 -0
- package/dist/infra/deployArtefacts.js +1 -0
- package/dist/infra/tokenScopes.d.ts +5 -1
- package/dist/infra/tokenScopes.js +1 -1
- package/dist/naming/reservedAppNames.d.ts +11 -2
- package/dist/naming/reservedAppNames.js +1 -1
- package/dist/securityHelpers.d.ts +7 -3
- package/dist/securityHelpers.js +1 -1
- package/package.json +6 -2
package/dist/.minified
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
75 files minified at 2026-07-05T07:50:20.044Z
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface StaleCdkTmpdir {
|
|
2
|
+
readonly path: string;
|
|
3
|
+
readonly sizeBytes: number;
|
|
4
|
+
readonly mtime: Date;
|
|
5
|
+
}
|
|
6
|
+
export interface FindOptions {
|
|
7
|
+
/** Base tmpdir to scan. Defaults to os.tmpdir(); overridden by tests. */
|
|
8
|
+
readonly tmpdirPath?: string;
|
|
9
|
+
/** Minimum age before a dir is reported. Defaults to 24h. */
|
|
10
|
+
readonly maxAgeMs?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface CleanupResult {
|
|
13
|
+
readonly removed: number;
|
|
14
|
+
readonly failed: number;
|
|
15
|
+
readonly bytesReclaimed: number;
|
|
16
|
+
}
|
|
17
|
+
export declare function findStaleCdkTmpdirs(options?: FindOptions): Promise<StaleCdkTmpdir[]>;
|
|
18
|
+
export declare function removeStaleCdkTmpdirs(dirs: readonly StaleCdkTmpdir[]): Promise<CleanupResult>;
|
|
19
|
+
/** Find + remove in one call — the shape vitest teardown reapers consume. */
|
|
20
|
+
export declare function cleanupStaleCdkTmpdirs(options?: FindOptions): Promise<CleanupResult>;
|
|
21
|
+
export declare function formatBytes(bytes: number): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readdir as d,stat as l,rm as h}from"fs/promises";import{tmpdir as w}from"os";import{join as m}from"path";import{logger as g}from"./logger.js";const B="cdkTmpdirCleanup";function o(e,t,r={}){g.debug(B,e,{error:t instanceof Error?t.message:String(t),...r})}const T="cdk.out",x=1440*60*1e3;async function u(e){let t=0,r;try{r=await d(e,{withFileTypes:!0})}catch(i){return o("readdir failed",i,{path:e}),0}for(const i of r){const a=m(e,i.name);if(i.isDirectory())t+=await u(a);else if(i.isFile())try{const n=await l(a);t+=n.size}catch(n){o("stat failed",n,{path:a})}}return t}async function D(e={}){const t=e.tmpdirPath??w(),r=e.maxAgeMs??x,i=Date.now()-r;let a;try{a=await d(t)}catch(s){return o("tmpdir readdir failed",s,{root:t}),[]}const n=[];for(const s of a){if(!s.startsWith(T))continue;const c=m(t,s);let f;try{f=await l(c)}catch(y){o("stat failed",y,{path:c});continue}if(!f.isDirectory()||f.mtimeMs>i)continue;const p=await u(c);n.push({path:c,sizeBytes:p,mtime:f.mtime})}return n}async function M(e){let t=0,r=0,i=0;for(const a of e)try{await h(a.path,{recursive:!0,force:!0}),t+=1,i+=a.sizeBytes}catch(n){o("rm failed",n,{path:a.path}),r+=1}return{removed:t,failed:r,bytesReclaimed:i}}async function E(e={}){const t=await D(e);return M(t)}function F(e){const t=["B","KB","MB","GB","TB"];let r=e,i=0;for(;r>=1024&&i<t.length-1;)r/=1024,i+=1;return`${r.toFixed(r<10?2:1)} ${t[i]}`}export{E as cleanupStaleCdkTmpdirs,D as findStaleCdkTmpdirs,F as formatBytes,M as removeStaleCdkTmpdirs};
|
package/dist/config.d.ts
CHANGED
|
@@ -25,12 +25,8 @@ export type S3BpaMode = (typeof S3_BPA_MODES)[number];
|
|
|
25
25
|
* 2026-06-10-centralised-root-access-default-on). Absent ⇒ "centralised"
|
|
26
26
|
* (default-on); "off" maps to OrgSetupConfig.skipRootAccessManagement at the
|
|
27
27
|
* adapter boundaries (CLI buildOrgSetupConfig, webapp setup route). The
|
|
28
|
-
* webapp
|
|
29
|
-
*
|
|
30
|
-
* TODO 2026-06-11, owner: paul — tracked in
|
|
31
|
-
* aiDocs/plans/tasks/2026-06-11-root-access-p2-t11-org-config-field-fjall.md:
|
|
32
|
-
* once a published @fjall/util ships this tuple, convert the webapp twin into
|
|
33
|
-
* a re-export from "@fjall/util/config" (TRAIL_LIFECYCLE_STATES precedent).
|
|
28
|
+
* webapp re-exports this tuple at webapp/app/.server/constants/rootAccess.ts
|
|
29
|
+
* (P2-T11, TRAIL_LIFECYCLE_STATES precedent).
|
|
34
30
|
*/
|
|
35
31
|
export declare const ROOT_ACCESS_MANAGEMENT_MODES: readonly ["centralised", "off"];
|
|
36
32
|
export type RootAccessManagementMode = (typeof ROOT_ACCESS_MANAGEMENT_MODES)[number];
|
package/dist/index.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export { ACCOUNT_STAGES_WITH_ROOT, STRUCTURAL_ENVIRONMENTS, ACCOUNT_STAGES, ACCO
|
|
|
14
14
|
export { RESOURCE_CATEGORIES, type ResourceCategory, categoriseResource, getExpectedDuration, getFriendlyResourceType } from "./resourceCategorisation.js";
|
|
15
15
|
export { parseGitRemoteUrl, type GitProvider, type ParsedGitRemote } from "./repo/gitRemoteParser.js";
|
|
16
16
|
export { abbreviateRegion, AWS_REGIONS_METADATA, DEFAULT_REGION, getRegionInfo, MAX_SECONDARY_REGIONS, OPT_IN_REGION_CODES, optInRegionWarning, regions, suggestRegionForTimezone, type RegionCode, type RegionInfo } from "./infra/regions.js";
|
|
17
|
-
export { SCOPE_VALUES, type TokenScope } from "./infra/tokenScopes.js";
|
|
17
|
+
export { SCOPE_VALUES, MACHINE_ONLY_SCOPES, USER_GRANTABLE_SCOPES, type TokenScope } from "./infra/tokenScopes.js";
|
|
18
18
|
export { SECRET_NAME_PATTERN, SECRET_NAME_ERROR, SSM_COMPONENT_PATTERN, SSM_COMPONENT_ERROR, SSM_STANDARD_MAX_VALUE_BYTES, SecretNamespaceSchema, type SecretNamespace, buildNamespaceParts, buildParameterPath, parseParameterPath, isManageablePath, parseDotEnv, escapeDotEnvValue } from "./secrets.js";
|
|
19
19
|
export { ConnectionWireSchema, type ConnectionWire, ConnectionsListResponseSchema, type ConnectionsListResponse } from "./infra/connectionsWire.js";
|
|
20
20
|
export { deriveRegionsFromOrgConfig, deriveTargets, deriveAllTargets, environmentOrTier, findTarget, generateTargetName, type OrgConfigRegions, type TargetAccount, type DerivedTarget } from "./targets.js";
|
|
@@ -24,4 +24,5 @@ export { findInfrastructurePaths, findBoundaryPath, isInfrastructureFile, type M
|
|
|
24
24
|
export { inferContainerFromCandidates } from "./repo/inferContainerFromCandidates.js";
|
|
25
25
|
export { RESERVED_APP_NAMES, type ReservedAppName, RESERVED_APP_NAME_MESSAGE, isReservedAppName } from "./naming/reservedAppNames.js";
|
|
26
26
|
export { deriveContentHashTag } from "./infra/deriveContentHashTag.js";
|
|
27
|
+
export { DEPLOY_MODES, DeployModeSchema, type DeployMode, IMAGE_TAG_PATTERN, ImageTagSchema, type ImageTag, ServiceArtefactSchema, type ServiceArtefact, ServiceArtefactsSchema, type ServiceArtefacts, ARTEFACT_OUTPUT_FIELDS, type ArtefactOutputField, artefactOutputKey } from "./infra/deployArtefacts.js";
|
|
27
28
|
export { MIGRATION_SNAPSHOT_NAME_PREFIX, EXPECTED_SCHEMA_VERSION_ENV, EXPECTED_SCHEMA_VERSION_TOOL_ENV, EXPECTED_CH_SCHEMA_VERSION_ENV, SCHEMA_ADMIN_USER_ENV, SCHEMA_ADMIN_PASSWORD_ENV, PRISMA_MIGRATION_DIR_RE, CLICKHOUSE_MIGRATION_SKIP_RE } from "./migration/constants.js";
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DNS_APEX as o,getDomainExportNames as t}from"./infra/domainExports.js";import{BACKUP_VAULT_NAME as a}from"./infra/backupVault.js";import{APPROVAL_TOKEN_OUTPUT_PREFIX as
|
|
1
|
+
import{DNS_APEX as o,getDomainExportNames as t}from"./infra/domainExports.js";import{BACKUP_VAULT_NAME as a}from"./infra/backupVault.js";import{APPROVAL_TOKEN_OUTPUT_PREFIX as n}from"./deploy/approvalTokenOutput.js";import{imageTagParameterName as i}from"./infra/imageTags.js";import{toPascalCase as m,toKebab as T,toValidDatabaseName as R,toScreamingSnake as N,capitalise as s,getSafeZoneName as C,accountConstructKey as O,hasAsciiStableConstructKey as c}from"./naming/caseConversion.js";import{findAccountNameCollision as p}from"./naming/accountNameCollision.js";import{defaultConnectedAccountName as P,suffixedAccountName as I,REGION_SHORT_CODES as M,findTrailingRegionShortCode as u,regionSuffixRejectionMessage as x}from"./naming/connectedAccountName.js";import{normaliseError as d,getErrorMessage as l,hasErrorCode as U,getErrorCode as h,getErrorStack as V,formatErrorString as G}from"./errorUtils.js";import{singleton as v}from"./async/singleton.js";import{DANGEROUS_ENV_VARS as b,filterDangerousEnvVars as F,maskSensitiveOutput as y,parseShellArgs as K}from"./securityHelpers.js";import{sleep as W}from"./async/sleep.js";import{mapSettledWithConcurrency as Y}from"./async/concurrency.js";import{ACCOUNT_STAGES_WITH_ROOT as j,STRUCTURAL_ENVIRONMENTS as z,ACCOUNT_STAGES as Z,ACCOUNT_STAGE_LABELS as q,isAccountStage as w,ACCOUNT_TIERS as J,AccountTierSchema as Q,isAccountTier as $,environmentToTier as ee,stageFromWireEnvironment as re,accountTier as oe,getEnvironmentLabel as te,ACCOUNT_ROLES as Ee}from"./environments.js";import{RESOURCE_CATEGORIES as Se,categoriseResource as ne,getExpectedDuration as _e,getFriendlyResourceType as ie}from"./resourceCategorisation.js";import{parseGitRemoteUrl as me}from"./repo/gitRemoteParser.js";import{abbreviateRegion as Re,AWS_REGIONS_METADATA as Ne,DEFAULT_REGION as se,getRegionInfo as Ce,MAX_SECONDARY_REGIONS as Oe,OPT_IN_REGION_CODES as ce,optInRegionWarning as fe,regions as pe,suggestRegionForTimezone as ge}from"./infra/regions.js";import{SCOPE_VALUES as Ie,MACHINE_ONLY_SCOPES as Me,USER_GRANTABLE_SCOPES as ue}from"./infra/tokenScopes.js";import{SECRET_NAME_PATTERN as De,SECRET_NAME_ERROR as de,SSM_COMPONENT_PATTERN as le,SSM_COMPONENT_ERROR as Ue,SSM_STANDARD_MAX_VALUE_BYTES as he,SecretNamespaceSchema as Ve,buildNamespaceParts as Ge,buildParameterPath as Le,parseParameterPath as ve,isManageablePath as He,parseDotEnv as be,escapeDotEnvValue as Fe}from"./secrets.js";import{ConnectionWireSchema as Ke,ConnectionsListResponseSchema as Xe}from"./infra/connectionsWire.js";import{deriveRegionsFromOrgConfig as Be,deriveTargets as Ye,deriveAllTargets as ke,environmentOrTier as je,findTarget as ze,generateTargetName as Ze}from"./targets.js";import{buildAppConfigPath as we}from"./repo/appPath.js";import{findInfrastructurePaths as Qe,findBoundaryPath as $e,isInfrastructureFile as er}from"./repo/findInfrastructurePaths.js";import{inferContainerFromCandidates as or}from"./repo/inferContainerFromCandidates.js";import{RESERVED_APP_NAMES as Er,RESERVED_APP_NAME_MESSAGE as ar,isReservedAppName as Sr}from"./naming/reservedAppNames.js";import{deriveContentHashTag as _r}from"./infra/deriveContentHashTag.js";import{DEPLOY_MODES as Ar,DeployModeSchema as mr,IMAGE_TAG_PATTERN as Tr,ImageTagSchema as Rr,ServiceArtefactSchema as Nr,ServiceArtefactsSchema as sr,ARTEFACT_OUTPUT_FIELDS as Cr,artefactOutputKey as Or}from"./infra/deployArtefacts.js";import{MIGRATION_SNAPSHOT_NAME_PREFIX as fr,EXPECTED_SCHEMA_VERSION_ENV as pr,EXPECTED_SCHEMA_VERSION_TOOL_ENV as gr,EXPECTED_CH_SCHEMA_VERSION_ENV as Pr,SCHEMA_ADMIN_USER_ENV as Ir,SCHEMA_ADMIN_PASSWORD_ENV as Mr,PRISMA_MIGRATION_DIR_RE as ur,CLICKHOUSE_MIGRATION_SKIP_RE as xr}from"./migration/constants.js";export{Ee as ACCOUNT_ROLES,Z as ACCOUNT_STAGES,j as ACCOUNT_STAGES_WITH_ROOT,q as ACCOUNT_STAGE_LABELS,J as ACCOUNT_TIERS,n as APPROVAL_TOKEN_OUTPUT_PREFIX,Cr as ARTEFACT_OUTPUT_FIELDS,Ne as AWS_REGIONS_METADATA,Q as AccountTierSchema,a as BACKUP_VAULT_NAME,xr as CLICKHOUSE_MIGRATION_SKIP_RE,Ke as ConnectionWireSchema,Xe as ConnectionsListResponseSchema,b as DANGEROUS_ENV_VARS,se as DEFAULT_REGION,Ar as DEPLOY_MODES,o as DNS_APEX,mr as DeployModeSchema,Pr as EXPECTED_CH_SCHEMA_VERSION_ENV,pr as EXPECTED_SCHEMA_VERSION_ENV,gr as EXPECTED_SCHEMA_VERSION_TOOL_ENV,Tr as IMAGE_TAG_PATTERN,Rr as ImageTagSchema,Me as MACHINE_ONLY_SCOPES,Oe as MAX_SECONDARY_REGIONS,fr as MIGRATION_SNAPSHOT_NAME_PREFIX,ce as OPT_IN_REGION_CODES,ur as PRISMA_MIGRATION_DIR_RE,M as REGION_SHORT_CODES,Er as RESERVED_APP_NAMES,ar as RESERVED_APP_NAME_MESSAGE,Se as RESOURCE_CATEGORIES,Mr as SCHEMA_ADMIN_PASSWORD_ENV,Ir as SCHEMA_ADMIN_USER_ENV,Ie as SCOPE_VALUES,de as SECRET_NAME_ERROR,De as SECRET_NAME_PATTERN,Ue as SSM_COMPONENT_ERROR,le as SSM_COMPONENT_PATTERN,he as SSM_STANDARD_MAX_VALUE_BYTES,z as STRUCTURAL_ENVIRONMENTS,Ve as SecretNamespaceSchema,Nr as ServiceArtefactSchema,sr as ServiceArtefactsSchema,ue as USER_GRANTABLE_SCOPES,Re as abbreviateRegion,O as accountConstructKey,oe as accountTier,Or as artefactOutputKey,we as buildAppConfigPath,Ge as buildNamespaceParts,Le as buildParameterPath,s as capitalise,ne as categoriseResource,P as defaultConnectedAccountName,ke as deriveAllTargets,_r as deriveContentHashTag,Be as deriveRegionsFromOrgConfig,Ye as deriveTargets,je as environmentOrTier,ee as environmentToTier,Fe as escapeDotEnvValue,F as filterDangerousEnvVars,p as findAccountNameCollision,$e as findBoundaryPath,Qe as findInfrastructurePaths,ze as findTarget,u as findTrailingRegionShortCode,G as formatErrorString,Ze as generateTargetName,t as getDomainExportNames,te as getEnvironmentLabel,h as getErrorCode,l as getErrorMessage,V as getErrorStack,_e as getExpectedDuration,ie as getFriendlyResourceType,Ce as getRegionInfo,C as getSafeZoneName,c as hasAsciiStableConstructKey,U as hasErrorCode,i as imageTagParameterName,or as inferContainerFromCandidates,w as isAccountStage,$ as isAccountTier,er as isInfrastructureFile,He as isManageablePath,Sr as isReservedAppName,Y as mapSettledWithConcurrency,y as maskSensitiveOutput,d as normaliseError,fe as optInRegionWarning,be as parseDotEnv,me as parseGitRemoteUrl,ve as parseParameterPath,K as parseShellArgs,x as regionSuffixRejectionMessage,pe as regions,v as singleton,W as sleep,re as stageFromWireEnvironment,I as suffixedAccountName,ge as suggestRegionForTimezone,T as toKebab,m as toPascalCase,N as toScreamingSnake,R as toValidDatabaseName};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared deploy artefact contract — the single source of truth for the
|
|
3
|
+
* engine→consumer wire shapes that describe WHAT a deploy produced.
|
|
4
|
+
*
|
|
5
|
+
* Three coupled surfaces import from here so none can drift:
|
|
6
|
+
* - `@fjall/deploy-core` — emits `ServiceArtefact[]` on `DeployResult`
|
|
7
|
+
* and derives its per-service output keys via `artefactOutputKey`
|
|
8
|
+
* - `@fjall/cli` — `commandSchemas` re-exports `ImageTagSchema` for
|
|
9
|
+
* `--image-tag` validation
|
|
10
|
+
* - webapp worker — parses the per-service outputs and (Phase 1b)
|
|
11
|
+
* consumes `ServiceArtefact[]` for Release persistence
|
|
12
|
+
*
|
|
13
|
+
* The output-key contract (`artefactOutputKey`) reproduces the historical
|
|
14
|
+
* inline `${serviceName}TaskDefinition` / `${serviceName}ImageTag` / …
|
|
15
|
+
* template literals byte-identically; the regression pins in
|
|
16
|
+
* `util/src/__tests__/deployArtefacts.test.ts` freeze that wire format.
|
|
17
|
+
*/
|
|
18
|
+
import { z } from "zod";
|
|
19
|
+
export declare const DEPLOY_MODES: readonly ["full", "code-only", "rollback"];
|
|
20
|
+
export declare const DeployModeSchema: z.ZodEnum<{
|
|
21
|
+
full: "full";
|
|
22
|
+
"code-only": "code-only";
|
|
23
|
+
rollback: "rollback";
|
|
24
|
+
}>;
|
|
25
|
+
export type DeployMode = z.infer<typeof DeployModeSchema>;
|
|
26
|
+
/**
|
|
27
|
+
* Single source for image-tag validation (previously declared 3×: CLI
|
|
28
|
+
* commandSchemas inline regex, webapp IMAGE_TAG_PATTERN, deploymentHelpers
|
|
29
|
+
* inline copy). Matches Docker tag grammar as fjall constrains it.
|
|
30
|
+
*/
|
|
31
|
+
export declare const IMAGE_TAG_PATTERN: RegExp;
|
|
32
|
+
export declare const ImageTagSchema: z.ZodString;
|
|
33
|
+
export type ImageTag = z.infer<typeof ImageTagSchema>;
|
|
34
|
+
/**
|
|
35
|
+
* One rolled-out (or pushed) service image identity.
|
|
36
|
+
*
|
|
37
|
+
* `imageDigest` and `taskDefinitionArn` are optional for honesty, not
|
|
38
|
+
* convenience: the code-only rollout falls back to tag pinning when ECR
|
|
39
|
+
* digest resolution fails, and the full-deploy path resolves the live task
|
|
40
|
+
* definition best-effort AFTER CloudFormation has rolled the service —
|
|
41
|
+
* neither may invent placeholder values. `previousTaskDefinitionArn` exists
|
|
42
|
+
* only where an explicit RegisterTaskDefinition rollout captured it.
|
|
43
|
+
* `ecrRepositoryArn` is absent for non-ECR registries.
|
|
44
|
+
*/
|
|
45
|
+
export declare const ServiceArtefactSchema: z.ZodObject<{
|
|
46
|
+
serviceName: z.ZodString;
|
|
47
|
+
imageTag: z.ZodString;
|
|
48
|
+
imageDigest: z.ZodOptional<z.ZodString>;
|
|
49
|
+
imageUri: z.ZodString;
|
|
50
|
+
ecrRepositoryArn: z.ZodOptional<z.ZodString>;
|
|
51
|
+
taskDefinitionArn: z.ZodOptional<z.ZodString>;
|
|
52
|
+
previousTaskDefinitionArn: z.ZodOptional<z.ZodString>;
|
|
53
|
+
}, z.core.$strict>;
|
|
54
|
+
export type ServiceArtefact = z.infer<typeof ServiceArtefactSchema>;
|
|
55
|
+
export declare const ServiceArtefactsSchema: z.ZodArray<z.ZodObject<{
|
|
56
|
+
serviceName: z.ZodString;
|
|
57
|
+
imageTag: z.ZodString;
|
|
58
|
+
imageDigest: z.ZodOptional<z.ZodString>;
|
|
59
|
+
imageUri: z.ZodString;
|
|
60
|
+
ecrRepositoryArn: z.ZodOptional<z.ZodString>;
|
|
61
|
+
taskDefinitionArn: z.ZodOptional<z.ZodString>;
|
|
62
|
+
previousTaskDefinitionArn: z.ZodOptional<z.ZodString>;
|
|
63
|
+
}, z.core.$strict>>;
|
|
64
|
+
export type ServiceArtefacts = z.infer<typeof ServiceArtefactsSchema>;
|
|
65
|
+
/**
|
|
66
|
+
* The six per-service output-key suffixes the deploy engine emits and the
|
|
67
|
+
* webapp worker parses (`webapp/scripts/deploymentJobHandler/imageRollout.ts`).
|
|
68
|
+
* Order matters only for readers; the VALUES are a wire contract.
|
|
69
|
+
*/
|
|
70
|
+
export declare const ARTEFACT_OUTPUT_FIELDS: readonly ["TaskDefinition", "PreviousTaskDefinition", "ImageTag", "ImageUri", "EcrRepositoryArn", "ImageDigest"];
|
|
71
|
+
export type ArtefactOutputField = (typeof ARTEFACT_OUTPUT_FIELDS)[number];
|
|
72
|
+
/**
|
|
73
|
+
* Derive the per-service deploy output key — MUST stay byte-identical to the
|
|
74
|
+
* historical `${serviceName}${field}` template literals (the worker
|
|
75
|
+
* slices the `serviceName` back off the `TaskDefinition`-suffixed keys).
|
|
76
|
+
*/
|
|
77
|
+
export declare function artefactOutputKey(serviceName: string, field: ArtefactOutputField): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{z as e}from"zod";const n=["full","code-only","rollback"],c=e.enum(n),o=/^[a-zA-Z0-9._-]+$/,r=e.string().min(1).max(128).regex(o),a=e.object({serviceName:e.string().min(1),imageTag:r,imageDigest:e.string().min(1).optional(),imageUri:e.string().min(1),ecrRepositoryArn:e.string().min(1).optional(),taskDefinitionArn:e.string().min(1).optional(),previousTaskDefinitionArn:e.string().min(1).optional()}).strict(),m=e.array(a),g=["TaskDefinition","PreviousTaskDefinition","ImageTag","ImageUri","EcrRepositoryArn","ImageDigest"];function p(t,i){return`${t}${i}`}export{g as ARTEFACT_OUTPUT_FIELDS,n as DEPLOY_MODES,c as DeployModeSchema,o as IMAGE_TAG_PATTERN,r as ImageTagSchema,a as ServiceArtefactSchema,m as ServiceArtefactsSchema,p as artefactOutputKey};
|
|
@@ -7,5 +7,9 @@
|
|
|
7
7
|
* a scope the CLI did not know how to request, or vice versa. Both
|
|
8
8
|
* consumers now import `SCOPE_VALUES` / `TokenScope` from here.
|
|
9
9
|
*/
|
|
10
|
-
export declare const SCOPE_VALUES: readonly ["read", "write", "deploy", "secrets:read", "secrets:write", "destroy", "admin", "applications:read", "applications:deploy"];
|
|
10
|
+
export declare const SCOPE_VALUES: readonly ["read", "write", "deploy", "secrets:read", "secrets:write", "destroy", "admin", "applications:read", "applications:deploy", "deploy:oidc:mint"];
|
|
11
11
|
export type TokenScope = (typeof SCOPE_VALUES)[number];
|
|
12
|
+
/** Scopes only a machine principal may hold — never a user-minted token. */
|
|
13
|
+
export declare const MACHINE_ONLY_SCOPES: readonly TokenScope[];
|
|
14
|
+
/** Scopes a user `*` grant expands to (excludes `admin` + machine-only). */
|
|
15
|
+
export declare const USER_GRANTABLE_SCOPES: readonly TokenScope[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
const r=["read","write","deploy","secrets:read","secrets:write","destroy","admin","applications:read","applications:deploy","deploy:oidc:mint"],s={read:"user",write:"user",deploy:"user","secrets:read":"user","secrets:write":"user",destroy:"user",admin:"admin","applications:read":"user","applications:deploy":"user","deploy:oidc:mint":"machine"},t=r.filter(e=>s[e]==="machine"),i=r.filter(e=>s[e]==="user");export{t as MACHINE_ONLY_SCOPES,r as SCOPE_VALUES,i as USER_GRANTABLE_SCOPES};
|
|
@@ -12,6 +12,15 @@
|
|
|
12
12
|
* customer-owned application sharing that name would collide structurally
|
|
13
13
|
* with the organisation entry under `[container/]fjall/fjall/infrastructure.ts`.
|
|
14
14
|
*
|
|
15
|
+
* `"organisation"` / `"platform"` / `"account"` are the org-tier deploy
|
|
16
|
+
* targets (deploy-core `ORGANISATION_TYPES` — the coupled SSoT; a deploy-core
|
|
17
|
+
* parity test pins the subset relation, since util sits below deploy-core and
|
|
18
|
+
* cannot import it). Deploy consumers classify a deploy as org-level from the
|
|
19
|
+
* target NAME alone (`fjall deploy account`, the worker's org-level gate), so
|
|
20
|
+
* an application sharing one of these names would be mis-classified as an
|
|
21
|
+
* org-tier deploy — bypassing the app-scoped session policy and injecting org
|
|
22
|
+
* identity into synth.
|
|
23
|
+
*
|
|
15
24
|
* **Boundary with `isReservedSlug`** — `webapp/app/.server/utils/reservedSlugs.ts`
|
|
16
25
|
* guards organisation slugs (URL-scoped, webapp-only). This list is
|
|
17
26
|
* application-scoped and ships from `@fjall/util` for cross-system reuse
|
|
@@ -21,14 +30,14 @@
|
|
|
21
30
|
* before testing, so `"Fjall"`, `"FJALL"`, and `"fjALL"` all reject
|
|
22
31
|
* identically.
|
|
23
32
|
*/
|
|
24
|
-
export declare const RESERVED_APP_NAMES: readonly ["fjall"];
|
|
33
|
+
export declare const RESERVED_APP_NAMES: readonly ["fjall", "organisation", "platform", "account"];
|
|
25
34
|
export type ReservedAppName = (typeof RESERVED_APP_NAMES)[number];
|
|
26
35
|
/**
|
|
27
36
|
* Canonical user-facing rejection message for a reserved application name.
|
|
28
37
|
* Shared by every create surface (the CLI `CreateApplicationSchema`, the webapp
|
|
29
38
|
* create route, the webapp scaffold schema) so the wording cannot drift.
|
|
30
39
|
*/
|
|
31
|
-
export declare const RESERVED_APP_NAME_MESSAGE = "
|
|
40
|
+
export declare const RESERVED_APP_NAME_MESSAGE = "This application name is reserved for Fjall's organisation-tier infrastructure.";
|
|
32
41
|
/**
|
|
33
42
|
* Case-insensitive membership check. The canonical entrypoint — consumers
|
|
34
43
|
* MUST route through this helper rather than calling `.includes(name.toLowerCase())`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
const r=["fjall","organisation","platform","account"],o="This application name is reserved for Fjall's organisation-tier infrastructure.";function t(e){return r.includes(e.toLowerCase())}export{r as RESERVED_APP_NAMES,o as RESERVED_APP_NAME_MESSAGE,t as isReservedAppName};
|
|
@@ -20,9 +20,12 @@ export declare const DANGEROUS_ENV_VARS: Set<string>;
|
|
|
20
20
|
*/
|
|
21
21
|
export declare function filterDangerousEnvVars(env: Record<string, string | undefined>): Record<string, string | undefined>;
|
|
22
22
|
/**
|
|
23
|
-
* Matches the FULL
|
|
23
|
+
* Matches the FULL agent token: prefix (16 base32) + separator (.) + secret (40 base32),
|
|
24
|
+
* for both scoped-family kinds — `fjall_ak_` (scoped) and `fjall_dk_` (deploy).
|
|
24
25
|
* Base32 alphabet: A-Z2-7. The `.` separator MUST be included or the secret leaks unmasked.
|
|
25
|
-
* Single source of truth — all masking call sites import this.
|
|
26
|
+
* Single source of truth — all masking call sites import this. Coupled to the prefix
|
|
27
|
+
* SSoT `AGENT_TOKEN_PREFIXES` at webapp/app/.server/models/auth/agent-token-kind.ts —
|
|
28
|
+
* a new token kind there MUST extend this alternation.
|
|
26
29
|
*
|
|
27
30
|
* Public form has NO `g` flag — `.test()` consumers must not share `lastIndex` across calls.
|
|
28
31
|
* The global form lives in `SCOPED_TOKEN_GLOBAL_REGEX` below for the iteration site.
|
|
@@ -33,7 +36,8 @@ export declare const SCOPED_TOKEN_REGEX: RegExp;
|
|
|
33
36
|
* Patterns: postgres://user:pass@host, password=xxx, secret=xxx, apikey=xxx,
|
|
34
37
|
* GitHub tokens (ghu_/ghs_/ghp_/gho_/github_pat_), bare AWS access-key IDs
|
|
35
38
|
* (AKIA-prefixed and ASIA-prefixed), AWS secret keys (env, INI, and JSON key
|
|
36
|
-
* spellings), session tokens, ARN account IDs,
|
|
39
|
+
* spellings), session tokens, ARN account IDs, agent tokens (`fjall_ak_`
|
|
40
|
+
* scoped, `fjall_dk_` deploy).
|
|
37
41
|
*
|
|
38
42
|
* Single source of truth — consumer loggers (CLI, worker, webapp) MUST
|
|
39
43
|
* NOT re-implement these patterns inline. See
|
package/dist/securityHelpers.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const A=new Set(["NODE_OPTIONS","NODE_PATH","NODE_EXTRA_CA_CERTS","NODE_DEBUG","NODE_PRESERVE_SYMLINKS","LD_PRELOAD","LD_LIBRARY_PATH","LD_AUDIT","LD_BIND_NOW","DYLD_INSERT_LIBRARIES","DYLD_LIBRARY_PATH","DYLD_FRAMEWORK_PATH","PYTHONPATH","PYTHONSTARTUP","PERL5LIB","PERL5OPT","RUBYLIB","RUBYOPT","HOME","XDG_CONFIG_HOME","AWS_SHARED_CREDENTIALS_FILE","AWS_CONFIG_FILE","SHELL","BASH_ENV","ENV","ZDOTDIR"]);function
|
|
1
|
+
const A=new Set(["NODE_OPTIONS","NODE_PATH","NODE_EXTRA_CA_CERTS","NODE_DEBUG","NODE_PRESERVE_SYMLINKS","LD_PRELOAD","LD_LIBRARY_PATH","LD_AUDIT","LD_BIND_NOW","DYLD_INSERT_LIBRARIES","DYLD_LIBRARY_PATH","DYLD_FRAMEWORK_PATH","PYTHONPATH","PYTHONSTARTUP","PERL5LIB","PERL5OPT","RUBYLIB","RUBYOPT","HOME","XDG_CONFIG_HOME","AWS_SHARED_CREDENTIALS_FILE","AWS_CONFIG_FILE","SHELL","BASH_ENV","ENV","ZDOTDIR"]);function i(e){return Object.fromEntries(Object.entries(e).filter(([r])=>!A.has(r.toUpperCase())))}const l=/fjall_(?:ak|dk)_[A-Z2-7]{16}\.[A-Z2-7]{40}/,c=/(fjall_(?:ak|dk)_)[A-Z2-7]{16}\.[A-Z2-7]{40}/g;function o(e){return e.replace(/-----BEGIN [A-Z ]*PRIVATE KEY-----(?:[^"\\]|\\[\\nrt"])*?-----END [A-Z ]*PRIVATE KEY-----/g,"-----BEGIN [REDACTED] PRIVATE KEY-----...-----END [REDACTED] PRIVATE KEY-----").replace(/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,"-----BEGIN [REDACTED] PRIVATE KEY-----...-----END [REDACTED] PRIVATE KEY-----").replace(/(\w+:\/\/[^:]+:)[^@]+(@)/gi,"$1***$2").replace(/(?<![a-zA-Z])(password|passwd|secret|api[_-]?key|token|auth|credential)([=:])["']?[^\s"']+/gi,"$1$2***").replace(/\b(ghu_|ghs_|ghp_|gho_|github_pat_)[A-Za-z0-9_]+/g,"$1***").replace(/\b(sk|rk)_(live|test)_[A-Za-z0-9]{8,}/g,"$1_$2_***").replace(/\bwhsec_[A-Za-z0-9]{8,}/g,"whsec_***").replace(/(?<=Authorization:\s*Bearer\s+)[A-Za-z0-9._~+/=-]+/gi,"***").replace(/\b(AKIA|ASIA)[A-Z0-9]{12,}\b/g,"$1***").replace(/(?<=aws_secret_access_key\s*=\s*["']?|SecretAccessKey[=:]\s*|"(aws)?secretAccessKey":\s*"|"aws_secret_access_key":\s*")[A-Za-z0-9/+=]{40,}/gi,"***").replace(/(arn:aws[^:]*:[^:]*:[^:]*:)(\d{12})(:[^\s]*)/g,"$1***$3").replace(/(?<="(aws)?sessionToken":\s*"|"aws_session_token":\s*")[^"]+/gi,"***").replace(/(?<=aws_session_token\s*[=:]\s*["']?|SessionToken[=:]\s*["']?)[^\s"']+/gi,"***").replace(/(?<="(internal[Aa]piKey|fjallCallbackToken)":\s*")[^"]+/g,"***").replace(c,"$1***")}function D(e){const r=[];let s="",E=!1,t=!1,n=!1,_=!1;for(const a of e){if(n){s+=a,n=!1;continue}if(a==="\\"&&!E){n=!0;continue}if(a==="'"&&!t){E=!E,_=!0;continue}if(a==='"'&&!E){t=!t,_=!0;continue}if(a===" "&&!E&&!t){(s||_)&&(r.push(s),s="",_=!1);continue}s+=a}if(E||t)throw new Error(`Unbalanced ${E?"single":"double"} quote in command: ${e.slice(0,80)}`);if(n)throw new Error(`Trailing backslash in command: ${e.slice(0,80)}`);return(s||_)&&r.push(s),r}export{A as DANGEROUS_ENV_VARS,l as SCOPED_TOKEN_REGEX,i as filterDangerousEnvVars,o as maskSensitiveOutput,D as parseShellArgs};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fjall/util",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.24.0",
|
|
4
4
|
"description": "Common utility methods",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -42,6 +42,10 @@
|
|
|
42
42
|
"types": "./dist/fsHelpers.d.ts",
|
|
43
43
|
"default": "./dist/fsHelpers.js"
|
|
44
44
|
},
|
|
45
|
+
"./cdkTmpdirCleanup": {
|
|
46
|
+
"types": "./dist/cdkTmpdirCleanup.d.ts",
|
|
47
|
+
"default": "./dist/cdkTmpdirCleanup.js"
|
|
48
|
+
},
|
|
45
49
|
"./migration": {
|
|
46
50
|
"types": "./dist/migration/index.d.ts",
|
|
47
51
|
"default": "./dist/migration/index.js"
|
|
@@ -121,5 +125,5 @@
|
|
|
121
125
|
"engines": {
|
|
122
126
|
"node": ">=22.0.0"
|
|
123
127
|
},
|
|
124
|
-
"gitHead": "
|
|
128
|
+
"gitHead": "616d7af846f0eebfa3409f5f47c98fbee836bad9"
|
|
125
129
|
}
|