@fjall/util 0.89.5 → 0.89.6
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/LICENSE +50 -21
- package/README.md +22 -0
- package/dist/.minified +1 -0
- package/dist/Config.d.ts +76 -0
- package/dist/Config.js +1 -0
- package/dist/aws/CloudFormationFailureAnalyser.d.ts +32 -0
- package/dist/aws/CloudFormationFailureAnalyser.js +1 -0
- package/dist/aws/cloudformationTypes.d.ts +17 -0
- package/dist/aws/cloudformationTypes.js +1 -0
- package/dist/aws/errors.d.ts +26 -0
- package/dist/aws/errors.js +1 -0
- package/dist/aws/index.d.ts +3 -0
- package/dist/aws/index.js +1 -0
- package/dist/caseConversion.js +1 -0
- package/dist/constructMap.d.ts +80 -0
- package/dist/constructMap.js +1 -0
- package/dist/{src/domainExports.d.ts → domainExports.d.ts} +3 -1
- package/dist/domainExports.js +1 -0
- package/dist/environments.d.ts +23 -0
- package/dist/environments.js +1 -0
- package/dist/errorUtils.js +1 -0
- package/dist/fsHelpers.d.ts +4 -0
- package/dist/fsHelpers.js +1 -0
- package/dist/gitRemoteParser.d.ts +8 -0
- package/dist/gitRemoteParser.js +1 -0
- package/dist/index.d.ts +11 -1
- package/dist/index.js +1 -18
- package/dist/insights/computePatternFingerprint.d.ts +27 -0
- package/dist/insights/computePatternFingerprint.js +1 -0
- package/dist/logger.d.ts +32 -0
- package/dist/logger.js +2 -0
- package/dist/regions.d.ts +8 -0
- package/dist/regions.js +1 -0
- package/dist/resourceCategorisation.d.ts +28 -0
- package/dist/resourceCategorisation.js +1 -0
- package/dist/securityHelpers.d.ts +38 -0
- package/dist/securityHelpers.js +1 -0
- package/dist/singleton.d.ts +2 -0
- package/dist/singleton.js +1 -0
- package/dist/sleep.d.ts +4 -0
- package/dist/sleep.js +1 -0
- package/dist/targets.d.ts +57 -0
- package/dist/targets.js +1 -0
- package/package.json +55 -8
- package/dist/src/Config.d.ts +0 -114
- package/dist/src/Config.js +0 -276
- package/dist/src/caseConversion.js +0 -59
- package/dist/src/domainExports.js +0 -18
- package/dist/src/errorUtils.js +0 -69
- package/dist/src/index.d.ts +0 -5
- package/dist/src/index.js +0 -37
- package/dist/src/logger.d.ts +0 -12
- package/dist/src/logger.js +0 -36
- /package/dist/{src/caseConversion.d.ts → caseConversion.d.ts} +0 -0
- /package/dist/{src/errorUtils.d.ts → errorUtils.d.ts} +0 -0
package/LICENSE
CHANGED
|
@@ -1,21 +1,50 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Fjall
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
to
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
Fjall Proprietary Software Licence
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Fjall. All rights reserved.
|
|
4
|
+
|
|
5
|
+
This software, including all source, object, bundled, and minified forms
|
|
6
|
+
("the Software"), is the proprietary and confidential property of Fjall.
|
|
7
|
+
|
|
8
|
+
1. Permitted Use. Subject to the terms of this Licence, Fjall grants you
|
|
9
|
+
a non-exclusive, non-transferable, revocable licence to install the
|
|
10
|
+
Software via the npm registry and to execute it solely for the purpose
|
|
11
|
+
of deploying, operating, and managing your own applications and
|
|
12
|
+
infrastructure on cloud providers.
|
|
13
|
+
|
|
14
|
+
2. Restrictions. You may NOT, and may not permit any third party to:
|
|
15
|
+
(a) copy, redistribute, sublicense, sell, rent, lease, or otherwise
|
|
16
|
+
transfer the Software;
|
|
17
|
+
(b) modify, adapt, translate, or create derivative works of the Software;
|
|
18
|
+
(c) reverse engineer, decompile, disassemble, deminify, or otherwise
|
|
19
|
+
attempt to derive the source code, structure, or organisation of
|
|
20
|
+
the Software, except to the minimum extent expressly permitted by
|
|
21
|
+
applicable mandatory law;
|
|
22
|
+
(d) use the Software, or any portion of it, to develop, train, or
|
|
23
|
+
improve any product or service that competes with Fjall;
|
|
24
|
+
(e) remove, alter, or obscure any proprietary notices contained in
|
|
25
|
+
the Software;
|
|
26
|
+
(f) publish, share, or otherwise disclose the Software or its contents
|
|
27
|
+
to any third party.
|
|
28
|
+
|
|
29
|
+
3. Ownership. All right, title, and interest in and to the Software,
|
|
30
|
+
including all intellectual property rights, remain with Fjall. No
|
|
31
|
+
rights are granted except as expressly set out in this Licence.
|
|
32
|
+
|
|
33
|
+
4. Termination. This Licence terminates automatically if you breach any
|
|
34
|
+
of its terms. Upon termination you must cease all use of the Software
|
|
35
|
+
and destroy all copies in your possession.
|
|
36
|
+
|
|
37
|
+
5. Disclaimer of Warranty. THE SOFTWARE IS PROVIDED "AS IS" WITHOUT
|
|
38
|
+
WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
|
|
39
|
+
THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
|
|
40
|
+
AND NON-INFRINGEMENT.
|
|
41
|
+
|
|
42
|
+
6. Limitation of Liability. IN NO EVENT SHALL FJALL BE LIABLE FOR ANY
|
|
43
|
+
INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, OR PUNITIVE DAMAGES
|
|
44
|
+
ARISING OUT OF OR RELATED TO THE SOFTWARE, EVEN IF ADVISED OF THE
|
|
45
|
+
POSSIBILITY OF SUCH DAMAGES.
|
|
46
|
+
|
|
47
|
+
7. Governing Law. This Licence is governed by the laws of England and
|
|
48
|
+
Wales, without regard to conflict of laws principles.
|
|
49
|
+
|
|
50
|
+
For commercial licensing enquiries, contact: contact@fjall.io
|
package/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# @fjall/util
|
|
2
|
+
|
|
3
|
+
Shared utilities used across the Fjall ecosystem — `Result` types, structured logger, security helpers (`maskSensitiveOutput`, `filterDangerousEnvVars`, `parseShellArgs`), and small helpers consumed by `@fjall/cli`, `@fjall/generator`, `@fjall/deploy-core`, and the Fjall webapp.
|
|
4
|
+
|
|
5
|
+
The `@fjall/util` barrel export does **not** include Node-only modules — those are exposed via subpath imports so the package can also be consumed by webapp client bundles.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @fjall/util
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { success, failure, type Result } from "@fjall/util";
|
|
17
|
+
import { maskSensitiveOutput } from "@fjall/util";
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Licence
|
|
21
|
+
|
|
22
|
+
Proprietary — see [LICENSE](./LICENSE).
|
package/dist/.minified
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
21 files minified at 2026-04-20T23:47:24.814Z
|
package/dist/Config.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export type ProviderAccount = {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
environment: string;
|
|
6
|
+
managed?: boolean;
|
|
7
|
+
oidcRoleArn?: string;
|
|
8
|
+
};
|
|
9
|
+
export type Profile = {
|
|
10
|
+
type: "sso" | "oidc";
|
|
11
|
+
region: string;
|
|
12
|
+
ssoAccountId?: string;
|
|
13
|
+
ssoRoleName?: string;
|
|
14
|
+
ssoSession?: string;
|
|
15
|
+
oidcRoleArn?: string;
|
|
16
|
+
oidcProviderArn?: string;
|
|
17
|
+
roleArn?: string;
|
|
18
|
+
roleSessionName?: string;
|
|
19
|
+
};
|
|
20
|
+
export type SSOSession = {
|
|
21
|
+
ssoRegion: string;
|
|
22
|
+
ssoStartUrl: string;
|
|
23
|
+
};
|
|
24
|
+
declare const DomainConfigSchema: z.ZodObject<{
|
|
25
|
+
name: z.ZodString;
|
|
26
|
+
type: z.ZodEnum<{
|
|
27
|
+
apex: "apex";
|
|
28
|
+
delegated: "delegated";
|
|
29
|
+
}>;
|
|
30
|
+
parentDomain: z.ZodOptional<z.ZodString>;
|
|
31
|
+
account: z.ZodOptional<z.ZodString>;
|
|
32
|
+
}, z.core.$strict>;
|
|
33
|
+
export type DomainConfig = z.infer<typeof DomainConfigSchema>;
|
|
34
|
+
export declare const RootConfigSchema: z.ZodObject<{
|
|
35
|
+
activeTarget: z.ZodOptional<z.ZodString>;
|
|
36
|
+
domains: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
37
|
+
name: z.ZodString;
|
|
38
|
+
type: z.ZodEnum<{
|
|
39
|
+
apex: "apex";
|
|
40
|
+
delegated: "delegated";
|
|
41
|
+
}>;
|
|
42
|
+
parentDomain: z.ZodOptional<z.ZodString>;
|
|
43
|
+
account: z.ZodOptional<z.ZodString>;
|
|
44
|
+
}, z.core.$strict>>>;
|
|
45
|
+
}, z.core.$strict>;
|
|
46
|
+
type RootConfig = z.infer<typeof RootConfigSchema>;
|
|
47
|
+
/**
|
|
48
|
+
* Config class for loading and saving the root fjall-config.json.
|
|
49
|
+
* Single config file at fjall/fjall-config.json (or fjall-config.json at cwd).
|
|
50
|
+
* Stores active target and domains.
|
|
51
|
+
* Org-level config (accounts, primary/DR regions, OIDC) is served by OrgConfigClient from the API.
|
|
52
|
+
*/
|
|
53
|
+
export declare class Config {
|
|
54
|
+
rootConfig: RootConfig;
|
|
55
|
+
private configPath;
|
|
56
|
+
constructor(rootConfig?: RootConfig, configPath?: string);
|
|
57
|
+
/**
|
|
58
|
+
* Find the config directory by walking up the directory tree.
|
|
59
|
+
* Looks for fjall/fjall-config.json or direct fjall-config.json.
|
|
60
|
+
*/
|
|
61
|
+
private static findConfigDirectory;
|
|
62
|
+
private static loadConfigFile;
|
|
63
|
+
static loadConfig(): Config;
|
|
64
|
+
private static formatZodError;
|
|
65
|
+
saveConfig(): void;
|
|
66
|
+
static getConfigDirectory(): string | null;
|
|
67
|
+
getActiveTarget(): string | undefined;
|
|
68
|
+
setActiveTarget(name: string): void;
|
|
69
|
+
clearActiveTarget(): void;
|
|
70
|
+
getDomains(): DomainConfig[];
|
|
71
|
+
setDomains(domains: DomainConfig[]): void;
|
|
72
|
+
addDomain(domain: DomainConfig): void;
|
|
73
|
+
getDomain(name: string): DomainConfig | undefined;
|
|
74
|
+
removeDomain(name: string): boolean;
|
|
75
|
+
}
|
|
76
|
+
export {};
|
package/dist/Config.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import*as r from"fs";import*as s from"path";import{z as e}from"zod";import{logger as g}from"./logger.js";const l=10,m=e.object({name:e.string(),type:e.enum(["apex","delegated"]),parentDomain:e.string().optional(),account:e.string().optional()}).strict(),d=e.object({activeTarget:e.string().optional(),domains:e.array(m).optional()}).strict();class a{rootConfig;configPath=null;constructor(o,t){this.rootConfig=o??{},this.configPath=t??null}static findConfigDirectory(){let o=process.cwd();for(let t=0;t<l;t++){const n=s.join(o,"fjall"),i=s.join(n,"fjall-config.json");if(r.existsSync(i))return n;const c=s.join(o,"fjall-config.json");if(r.existsSync(c))return o;const f=s.dirname(o);if(f===o)break;o=f}return null}static loadConfigFile(o){try{return r.accessSync(o,r.constants.R_OK|r.constants.W_OK),r.readFileSync(o,{encoding:"utf8"})}catch(t){return g.debug("Config","Config file not accessible",{file:o,error:t instanceof Error?t.message:String(t)}),null}}static loadConfig(){const o=a.findConfigDirectory();if(!o)return new a;const t=s.join(o,"fjall-config.json"),n=a.loadConfigFile(t);let i;if(n)try{i=d.parse(JSON.parse(n))}catch(c){throw a.formatZodError(c,"fjall-config.json")}return new a(i,t)}static formatZodError(o,t){if(o instanceof e.ZodError&&o.issues.length>0){const c=o.issues.map(f=>`${f.path.join(".")}: ${f.message}`).join("; ");return new Error(`Failed to parse ${t}: ${c}`)}const i=(o instanceof Error?o.message:String(o)).replace(/\n/g," ").substring(0,500);return new Error(`Failed to parse ${t}: ${i}`)}saveConfig(){let o=this.configPath;if(!o){const c=a.findConfigDirectory()||s.join(process.cwd(),"fjall");o=s.join(c,"fjall-config.json")}const t=s.dirname(o);r.mkdirSync(t,{recursive:!0});const n=JSON.stringify(this.rootConfig,null,2),i=`${o}.tmp`;r.writeFileSync(i,n,{mode:384}),r.renameSync(i,o)}static getConfigDirectory(){return a.findConfigDirectory()}getActiveTarget(){return this.rootConfig.activeTarget}setActiveTarget(o){this.rootConfig.activeTarget=o}clearActiveTarget(){this.rootConfig.activeTarget=void 0}getDomains(){return this.rootConfig.domains??[]}setDomains(o){this.rootConfig.domains=o}addDomain(o){this.rootConfig.domains||(this.rootConfig.domains=[]),this.rootConfig.domains.push(o)}getDomain(o){return this.rootConfig.domains?.find(t=>t.name.toLowerCase()===o.toLowerCase())}removeDomain(o){if(!this.rootConfig.domains)return!1;const t=this.rootConfig.domains.findIndex(n=>n.name.toLowerCase()===o.toLowerCase());return t===-1?!1:(this.rootConfig.domains.splice(t,1),!0)}}export{a as Config,d as RootConfigSchema};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type ResourceEvent } from "./cloudformationTypes.js";
|
|
2
|
+
export interface RootCause {
|
|
3
|
+
resource: ResourceEvent;
|
|
4
|
+
reason: string;
|
|
5
|
+
category: "permissions" | "validation" | "dependency" | "limit" | "network" | "unknown";
|
|
6
|
+
isDirectCause: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface FailureAnalysis {
|
|
9
|
+
rootCause: RootCause;
|
|
10
|
+
affectedResources: ResourceEvent[];
|
|
11
|
+
dependencyChain: string[];
|
|
12
|
+
summary: string;
|
|
13
|
+
remediation: string[];
|
|
14
|
+
errorPattern?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* CloudFormationFailureAnalyser provides intelligent analysis of deployment failures
|
|
18
|
+
* It identifies root causes, dependency chains, and provides actionable remediation
|
|
19
|
+
*/
|
|
20
|
+
export declare class CloudFormationFailureAnalyser {
|
|
21
|
+
private knownErrorPatterns;
|
|
22
|
+
analyseFailure(eventHistory: Map<string, ResourceEvent[]>): FailureAnalysis | null;
|
|
23
|
+
private findFailedResources;
|
|
24
|
+
private findRootCause;
|
|
25
|
+
private categoriseError;
|
|
26
|
+
private isDirectCause;
|
|
27
|
+
private buildDependencyChain;
|
|
28
|
+
private generateRemediation;
|
|
29
|
+
private generateSummary;
|
|
30
|
+
private simplifyResourceType;
|
|
31
|
+
private isFailedStatus;
|
|
32
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class a{knownErrorPatterns=[{pattern:/AccessDenied|UnauthorizedOperation|Forbidden/i,category:"permissions",remediation:["Check IAM permissions for the deployment role","Ensure the role has necessary CloudFormation permissions","Verify service-linked roles are created if needed"]},{pattern:/Invalid.*Parameter|ValidationError|Invalid.*Value/i,category:"validation",remediation:["Review parameter values in your CDK code","Check for typos in resource names or ARNs","Ensure all required parameters are provided"]},{pattern:/Limit.*Exceeded|Quota.*Exceeded|Maximum.*reached/i,category:"limit",remediation:["Check AWS service quotas in the Service Quotas console","Request a quota increase if needed","Consider using a different region with available capacity"]},{pattern:/Timeout|Connection.*refused|Network.*unreachable/i,category:"network",remediation:["Check network connectivity and VPC settings","Verify security groups and NACLs","Ensure endpoints are accessible"]},{pattern:/already exists|Duplicate|ConflictException/i,category:"validation",remediation:["Resource with this name already exists","Consider using a different name or deleting the existing resource","Check if you are deploying to the correct account/region"]},{pattern:/Role.*not.*found|Role.*does.*not.*exist/i,category:"dependency",remediation:["Ensure IAM roles are created before dependent resources","Check role names and ARNs are correct","Verify cross-stack references are properly configured"]}];analyseFailure(e){const r=this.findFailedResources(e);if(r.length===0)return null;const s=this.findRootCause(r,e),n=this.buildDependencyChain(s.resource,r),i=this.generateRemediation(s),t=this.generateSummary(s,r);return{rootCause:s,affectedResources:r,dependencyChain:n,summary:t,remediation:i,errorPattern:s.category}}findFailedResources(e){const r=[];for(const[s,n]of e){const i=n[n.length-1];i&&this.isFailedStatus(i.status)&&r.push(i)}return r.sort((s,n)=>s.timestamp.getTime()-n.timestamp.getTime())}findRootCause(e,r){if(e.length===0)return{resource:{logicalId:"Unknown",resourceType:"Unknown",status:"FAILED",timestamp:new Date},reason:"No failed resources found",category:"unknown",isDirectCause:!1};const s=e[0],n=this.categoriseError(s.statusReason||""),i=this.isDirectCause(s,r);return{resource:s,reason:s.statusReason||"Unknown error",category:n,isDirectCause:i}}categoriseError(e){for(const r of this.knownErrorPatterns)if(r.pattern.test(e))return r.category;return"unknown"}isDirectCause(e,r){const s=e.statusReason||"";return s.includes("depends on")||s.includes("referenced by")||s.includes("required by")?!1:!(r.get(e.logicalId)||[]).some(t=>t.status.includes("COMPLETE")&&!t.status.includes("ROLLBACK"))}buildDependencyChain(e,r){const s=[];s.push(`${e.logicalId} (${e.resourceType}) - ROOT CAUSE`);for(const n of r)n.logicalId!==e.logicalId&&((n.statusReason||"").toLowerCase().includes(e.logicalId.toLowerCase())?s.push(` \u2192 ${n.logicalId} (${n.resourceType}) - Failed due to ${e.logicalId}`):s.push(` \u2192 ${n.logicalId} (${n.resourceType})`));return s}generateRemediation(e){const r=[];for(const s of this.knownErrorPatterns)if(s.category===e.category){r.push(...s.remediation);break}return e.resource.resourceType.includes("IAM")?r.push("Review IAM policies and trust relationships"):e.resource.resourceType.includes("Lambda")?r.push("Check Lambda function configuration and runtime"):e.resource.resourceType.includes("ECS")&&r.push("Verify ECS task definition and container configuration"),r.length===0&&r.push("Review the CloudFormation console for detailed error messages","Check the resource configuration in your CDK code","Ensure all dependencies are properly defined"),r}generateSummary(e,r){const s=this.simplifyResourceType(e.resource.resourceType),n=r.length;let i=`Deployment failed: ${s} "${e.resource.logicalId}" `;switch(e.category){case"permissions":i+="failed due to insufficient permissions";break;case"validation":i+="failed validation";break;case"dependency":i+="has missing or invalid dependencies";break;case"limit":i+="exceeded AWS service limits";break;case"network":i+="encountered network issues";break;default:i+="failed to create"}return n>1&&(i+=` (${n-1} dependent resources also failed)`),i}simplifyResourceType(e){return e.replace("AWS::","").replace("::"," ")}isFailedStatus(e){return e.includes("FAILED")}}export{a as CloudFormationFailureAnalyser};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** CloudFormation error pattern for stacks that haven't been created yet */
|
|
2
|
+
export declare const STACK_NOT_FOUND_PATTERN = "does not exist";
|
|
3
|
+
/** CDK error string indicating the targeted stack does not exist in the synthesised output */
|
|
4
|
+
export declare const CDK_NO_STACKS_MATCH = "No stacks match the name(s)";
|
|
5
|
+
export interface ResourceEvent {
|
|
6
|
+
logicalId: string;
|
|
7
|
+
physicalId?: string;
|
|
8
|
+
resourceType: string;
|
|
9
|
+
status: string;
|
|
10
|
+
statusReason?: string;
|
|
11
|
+
timestamp: Date;
|
|
12
|
+
/** Topology group derived from construct map (e.g., "monitoring"). */
|
|
13
|
+
group?: string;
|
|
14
|
+
/** CDK construct path (e.g., "/Account/CloudTrail/trail/Resource"). */
|
|
15
|
+
constructPath?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function isResourceEvent(event: unknown): event is ResourceEvent;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const o="does not exist",e="No stacks match the name(s)";function i(t){if(typeof t!="object"||t===null||!("logicalId"in t)||!("resourceType"in t)||!("status"in t)||!("timestamp"in t))return!1;const s=t;return typeof s.logicalId=="string"&&typeof s.resourceType=="string"&&typeof s.status=="string"&&s.timestamp instanceof Date}export{e as CDK_NO_STACKS_MATCH,o as STACK_NOT_FOUND_PATTERN,i as isResourceEvent};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare class AWSError extends Error {
|
|
2
|
+
readonly code?: string | undefined;
|
|
3
|
+
constructor(message: string, code?: string | undefined);
|
|
4
|
+
}
|
|
5
|
+
export declare class NoRolesFoundError extends AWSError {
|
|
6
|
+
constructor(accountId?: string, message?: string);
|
|
7
|
+
}
|
|
8
|
+
export declare class InvalidCredentialsError extends AWSError {
|
|
9
|
+
constructor(message: string);
|
|
10
|
+
}
|
|
11
|
+
export declare class SSOTokenExpiredError extends AWSError {
|
|
12
|
+
constructor();
|
|
13
|
+
}
|
|
14
|
+
export declare class MissingRegionError extends AWSError {
|
|
15
|
+
constructor();
|
|
16
|
+
}
|
|
17
|
+
export declare class ProfileNotFoundError extends AWSError {
|
|
18
|
+
constructor(profileName: string);
|
|
19
|
+
}
|
|
20
|
+
export declare function isAWSError(error: unknown): error is AWSError;
|
|
21
|
+
export declare function isNoRolesFoundError(error: unknown): error is NoRolesFoundError;
|
|
22
|
+
export declare function isSSOUnauthorizedError(error: unknown): boolean;
|
|
23
|
+
export declare class CommandError extends AWSError {
|
|
24
|
+
readonly tip?: string;
|
|
25
|
+
constructor(message: string, tip?: string);
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class e extends Error{code;constructor(t,n){super(t),this.code=n,this.name=this.constructor.name,Object.setPrototypeOf(this,new.target.prototype)}}class s extends e{constructor(t,n){const r=t?`No roles found for account ${t}`:"No roles found for this account";super(n??r,"NO_ROLES_FOUND")}}class i extends e{constructor(t){super(t,"INVALID_CREDENTIALS")}}class c extends e{constructor(){super("SSO token has expired","SSO_TOKEN_EXPIRED")}}class u extends e{constructor(){super("Region is missing in AWS configuration and environment","MISSING_REGION")}}class p extends e{constructor(t){super(`AWS profile '${t}' not found`,"PROFILE_NOT_FOUND")}}function a(o){return o instanceof e}function d(o){return o instanceof s}function E(o){return!o||typeof o!="object"?!1:"name"in o&&o.name==="UnauthorizedException"||"Code"in o&&o.Code==="UnauthorizedException"||"__type"in o&&typeof o.__type=="string"&&o.__type.includes("UnauthorizedException")}class x extends e{tip;constructor(t,n){super(t,"COMMAND_ERROR"),this.tip=n}}export{e as AWSError,x as CommandError,i as InvalidCredentialsError,u as MissingRegionError,s as NoRolesFoundError,p as ProfileNotFoundError,c as SSOTokenExpiredError,a as isAWSError,d as isNoRolesFoundError,E as isSSOUnauthorizedError};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { AWSError, NoRolesFoundError, InvalidCredentialsError, SSOTokenExpiredError, MissingRegionError, ProfileNotFoundError, CommandError, isAWSError, isNoRolesFoundError, isSSOUnauthorizedError } from "./errors.js";
|
|
2
|
+
export { STACK_NOT_FOUND_PATTERN, CDK_NO_STACKS_MATCH, type ResourceEvent, isResourceEvent } from "./cloudformationTypes.js";
|
|
3
|
+
export { CloudFormationFailureAnalyser, type RootCause, type FailureAnalysis } from "./CloudFormationFailureAnalyser.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{AWSError as e,NoRolesFoundError as i,InvalidCredentialsError as n,SSOTokenExpiredError as E,MissingRegionError as s,ProfileNotFoundError as d,CommandError as S,isAWSError as l,isNoRolesFoundError as t,isSSOUnauthorizedError as a}from"./errors.js";import{STACK_NOT_FOUND_PATTERN as A,CDK_NO_STACKS_MATCH as C,isResourceEvent as N}from"./cloudformationTypes.js";import{CloudFormationFailureAnalyser as m}from"./CloudFormationFailureAnalyser.js";export{e as AWSError,C as CDK_NO_STACKS_MATCH,m as CloudFormationFailureAnalyser,S as CommandError,n as InvalidCredentialsError,s as MissingRegionError,i as NoRolesFoundError,d as ProfileNotFoundError,E as SSOTokenExpiredError,A as STACK_NOT_FOUND_PATTERN,l as isAWSError,t as isNoRolesFoundError,N as isResourceEvent,a as isSSOUnauthorizedError};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function o(e){return e.replace(/[-_](.)/g,(a,t)=>t.toUpperCase()).replace(/^./,a=>a.toUpperCase())}function r(e){return e.replace(/([A-Z]+)([A-Z][a-z])/g,"$1-$2").replace(/([a-z\d])([A-Z])/g,"$1-$2").replace(/[\s_]+/g,"-").toLowerCase()}function n(e){return r(e).replace(/-/g,"_")}function p(e){return e.length===0?e:e.charAt(0).toUpperCase()+e.slice(1)}function c(e){return e.split(".").join("")}export{p as capitalise,c as getSafeZoneName,r as toKebab,o as toPascalCase,n as toValidDatabaseName};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Construct-to-topology-group mapping.
|
|
3
|
+
*
|
|
4
|
+
* Maps CDK construct IDs (the first segment below the stack in the construct
|
|
5
|
+
* path) to topology groups. These maps are used to derive which topology node
|
|
6
|
+
* a resource belongs to based on which CDK construct created it.
|
|
7
|
+
*
|
|
8
|
+
* When a new CDK construct is added, add one entry here.
|
|
9
|
+
* When a new AWS resource type is added, nothing changes.
|
|
10
|
+
*/
|
|
11
|
+
/** Manifest file name — single source of truth across CLI, deploy-core, and infrastructure. */
|
|
12
|
+
export declare const FJALL_MANIFEST_FILENAME = "fjall-manifest.json";
|
|
13
|
+
/** Current manifest schema version. Shared between CLI and infrastructure. */
|
|
14
|
+
export declare const MANIFEST_SCHEMA_VERSION: 1;
|
|
15
|
+
import type { ResourceCategory } from "./resourceCategorisation.js";
|
|
16
|
+
/** Entry in the resource map — maps a logical ID to its construct context. */
|
|
17
|
+
export interface ResourceMapEntry {
|
|
18
|
+
/** Full CDK construct path (e.g., "/Account/CloudTrail/managementEventsTrail/Resource") */
|
|
19
|
+
constructPath: string;
|
|
20
|
+
/** Topology group derived from the construct (e.g., "monitoring") */
|
|
21
|
+
group: string;
|
|
22
|
+
/** AWS resource type (e.g., "AWS::KMS::Key") */
|
|
23
|
+
resourceType: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Account stack construct-to-group mapping.
|
|
27
|
+
* Keys are CDK construct IDs (first segment after stack name in construct path).
|
|
28
|
+
*/
|
|
29
|
+
export declare const ACCOUNT_CONSTRUCT_GROUPS: Readonly<Record<string, ResourceCategory>>;
|
|
30
|
+
/**
|
|
31
|
+
* Application stack construct-to-group mapping.
|
|
32
|
+
* Keys are CDK construct IDs (first segment after stack name in construct path).
|
|
33
|
+
*/
|
|
34
|
+
export declare const APP_CONSTRUCT_GROUPS: Readonly<Record<string, ResourceCategory>>;
|
|
35
|
+
/**
|
|
36
|
+
* Derives the topology group from a CDK construct path.
|
|
37
|
+
*
|
|
38
|
+
* The construct path format is: /<StackName>/<ConstructId>/.../<Resource>
|
|
39
|
+
* We extract the first construct below the stack and look it up in the
|
|
40
|
+
* construct group maps.
|
|
41
|
+
*
|
|
42
|
+
* @param constructPath - Full CDK construct path
|
|
43
|
+
* @param constructGroups - Map of construct ID → topology group
|
|
44
|
+
* @returns The topology group or undefined if not mapped
|
|
45
|
+
*/
|
|
46
|
+
export declare function deriveGroupFromPath(constructPath: string, constructGroups: Readonly<Record<string, ResourceCategory>>): ResourceCategory | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* Derives a display name from a CDK construct path.
|
|
49
|
+
* Extracts the most meaningful segment from the path.
|
|
50
|
+
*/
|
|
51
|
+
export declare function deriveDisplayName(constructPath: string): string;
|
|
52
|
+
/**
|
|
53
|
+
* Builds a construct map from CDK's manifest.json metadata.
|
|
54
|
+
*
|
|
55
|
+
* Reads the CDK cloud assembly manifest, inverts the `aws:cdk:logicalId`
|
|
56
|
+
* metadata entries to `logicalId → { constructPath, group, resourceType }`,
|
|
57
|
+
* and derives topology groups from the construct path.
|
|
58
|
+
*
|
|
59
|
+
* @param cdkOutPath - Path to the cdk.out directory
|
|
60
|
+
* @param constructGroups - Construct-to-group mapping (Account or Application)
|
|
61
|
+
* @returns Map of logicalId → ResourceMapEntry
|
|
62
|
+
*/
|
|
63
|
+
export declare function buildConstructMap(cdkOutPath: string, constructGroups: Readonly<Record<string, ResourceCategory>>): Map<string, ResourceMapEntry>;
|
|
64
|
+
/**
|
|
65
|
+
* Converts a construct map to a plain object for JSON serialisation.
|
|
66
|
+
*/
|
|
67
|
+
export declare function constructMapToRecord(map: Map<string, ResourceMapEntry>): Record<string, ResourceMapEntry>;
|
|
68
|
+
/**
|
|
69
|
+
* Converts a plain object back to a construct map.
|
|
70
|
+
*/
|
|
71
|
+
export declare function recordToConstructMap(record: Record<string, ResourceMapEntry> | undefined): Map<string, ResourceMapEntry>;
|
|
72
|
+
/**
|
|
73
|
+
* Enriches a resource event with construct map data.
|
|
74
|
+
* Returns the group, constructPath, and displayName when available.
|
|
75
|
+
*/
|
|
76
|
+
export declare function enrichFromConstructMap(logicalId: string, resourceType: string, constructMap: Map<string, ResourceMapEntry> | undefined): {
|
|
77
|
+
group?: string;
|
|
78
|
+
constructPath?: string;
|
|
79
|
+
displayName: string;
|
|
80
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const R="fjall-manifest.json",S=1;import{readFileSync as p}from"fs";import{join as l}from"path";import{logger as g}from"./logger.js";import{categoriseResource as d,getFriendlyResourceType as y}from"./resourceCategorisation.js";const h=Object.freeze({CloudTrail:"monitoring",MonitoringRole:"monitoring",AuditRole:"security",OidcConnector:"security",EcrDefaultImage:"registry",EventBus:"events",DisasterRecovery:"backup"}),A=Object.freeze({Network:"network",Database:"database",Compute:"compute",Storage:"storage",Messaging:"events",Cdn:"dns"});function C(o,e){const t=o.split("/").filter(Boolean);if(t.length<2)return;const r=t[1];return e[r]}function T(o){const e=o.split("/").filter(Boolean);return e.length<2?o:(e.length>2&&e[e.length-1]==="Resource"?e[e.length-2]:e[e.length-1]).replace(/([a-z])([A-Z])/g,"$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1 $2")}function E(o,e){const t=new Map;let r;try{const n=p(l(o,"manifest.json"),"utf-8"),s=JSON.parse(n);if(typeof s!="object"||s===null)return t;r=s}catch(n){return g.debug("ConstructMap","Could not read CDK manifest",{error:n instanceof Error?n.message:String(n)}),t}if(!r.artifacts)return t;for(const n of Object.values(r.artifacts)){if(n.type!=="aws:cloudformation:stack"||!n.metadata)continue;const s=b(o,n.properties?.templateFile);for(const[a,c]of Object.entries(n.metadata))for(const i of c){if(i.type!=="aws:cdk:logicalId")continue;const u=i.data,f=s.get(u)??"",m=C(a,e)??d(f);t.set(u,{constructPath:a,group:m,resourceType:f})}}return t}function b(o,e){const t=new Map;if(!e)return t;try{const r=p(l(o,e),"utf-8"),n=JSON.parse(r);if(typeof n!="object"||n===null)return t;const s=n;if(s.Resources&&typeof s.Resources=="object")for(const[a,c]of Object.entries(s.Resources))typeof c=="object"&&c!==null&&c.Type&&t.set(a,c.Type)}catch(r){g.debug("ConstructMap","Could not read template file",{templateFile:e,error:r instanceof Error?r.message:String(r)})}return t}function v(o){const e={};for(const[t,r]of o)e[t]=r;return e}function x(o){const e=new Map;if(!o)return e;for(const[t,r]of Object.entries(o))e.set(t,r);return e}function I(o,e,t){const r=t?.get(o);return r?{group:r.group,constructPath:r.constructPath,displayName:T(r.constructPath)}:{displayName:y(e)}}export{h as ACCOUNT_CONSTRUCT_GROUPS,A as APP_CONSTRUCT_GROUPS,R as FJALL_MANIFEST_FILENAME,S as MANIFEST_SCHEMA_VERSION,E as buildConstructMap,v as constructMapToRecord,T as deriveDisplayName,C as deriveGroupFromPath,I as enrichFromConstructMap,x as recordToConstructMap};
|
|
@@ -2,11 +2,13 @@ export declare const DNS_APEX: "@";
|
|
|
2
2
|
/**
|
|
3
3
|
* Compute predictable CloudFormation export names for domain stack outputs.
|
|
4
4
|
* Used by both infrastructure constructs (to set export names) and CLI services
|
|
5
|
-
* (to import zone ID and
|
|
5
|
+
* (to import zone ID, certificate ARN, and delegation role ARN via Fn.importValue).
|
|
6
6
|
*/
|
|
7
7
|
export declare function getDomainExportNames(domainName: string): {
|
|
8
8
|
hostedZoneId: string;
|
|
9
9
|
certificateArn: string;
|
|
10
|
+
delegationRoleArn: string;
|
|
11
|
+
nameservers: string;
|
|
10
12
|
};
|
|
11
13
|
/**
|
|
12
14
|
* Props for importing zone and certificate from a managed domain stack.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const n="@";function t(r){const e=r.replace(/\./g,"-");return{hostedZoneId:`${e}-hosted-zone-id`,certificateArn:`${e}-certificate-arn`,delegationRoleArn:`${e}-delegation-role-arn`,nameservers:`${e}-nameservers`}}export{n as DNS_APEX,t as getDomainExportNames};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard environment constants shared across CLI, webapp, and deploy-core.
|
|
3
|
+
*
|
|
4
|
+
* "root" is implicit — always derived from role === "organisation",
|
|
5
|
+
* not user-selectable.
|
|
6
|
+
*/
|
|
7
|
+
export declare const STANDARD_ENVIRONMENTS: readonly ["production", "staging", "development", "platform", "compliance"];
|
|
8
|
+
export type StandardEnvironment = (typeof STANDARD_ENVIRONMENTS)[number];
|
|
9
|
+
/** Type guard: checks whether a string is a valid standard environment. */
|
|
10
|
+
export declare function isValidEnvironment(value: string): value is StandardEnvironment;
|
|
11
|
+
/**
|
|
12
|
+
* Structural environments used for cascade account partitioning.
|
|
13
|
+
* These are not user-selectable — "root" is implicit (management account),
|
|
14
|
+
* "platform" hosts shared infrastructure (IPAM, Transit Gateway, etc.).
|
|
15
|
+
*/
|
|
16
|
+
export declare const STRUCTURAL_ENVIRONMENTS: {
|
|
17
|
+
readonly ROOT: "root";
|
|
18
|
+
readonly PLATFORM: "platform";
|
|
19
|
+
};
|
|
20
|
+
/** Human-readable labels for each standard environment. */
|
|
21
|
+
export declare const ENVIRONMENT_LABELS: Record<StandardEnvironment, string>;
|
|
22
|
+
/** Returns the human-readable label for an environment, with capitalised fallback. */
|
|
23
|
+
export declare function getEnvironmentLabel(env: string): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const o=["production","staging","development","platform","compliance"];function n(t){return o.includes(t)}const r={ROOT:"root",PLATFORM:"platform"},e={production:"Production",staging:"Staging",development:"Development",platform:"Platform",compliance:"Compliance"};function i(t){return n(t)?e[t]:t.charAt(0).toUpperCase()+t.slice(1)}export{e as ENVIRONMENT_LABELS,o as STANDARD_ENVIRONMENTS,r as STRUCTURAL_ENVIRONMENTS,i as getEnvironmentLabel,n as isValidEnvironment};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function f(n){return n instanceof Error?n:new Error(String(n))}function o(n){return n instanceof Error?n.message:typeof n=="string"?n:n&&typeof n=="object"&&"message"in n?String(n.message):"An unknown error occurred"}function c(n,t){return!n||typeof n!="object"?!1:"code"in n&&n.code===t}function i(n){if(!(!n||typeof n!="object")&&"code"in n&&typeof n.code=="string")return n.code}function u(n){return n instanceof Error?n.stack:void 0}function s(n){const t=o(n),e=i(n);return e&&e!=="UNKNOWN_ERROR"?`[${e}] ${t}`:t}export{s as formatErrorString,i as getErrorCode,o as getErrorMessage,u as getErrorStack,c as hasErrorCode,f as normaliseError};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{access as e,constants as c}from"fs/promises";async function o(t){try{return await e(t,c.F_OK),!0}catch(r){if(r instanceof Error&&"code"in r&&r.code==="ENOENT")return!1;throw r}}export{o as fileExists};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type GitProvider = "github" | "gitlab" | "bitbucket" | "other";
|
|
2
|
+
export interface ParsedGitRemote {
|
|
3
|
+
provider: GitProvider;
|
|
4
|
+
owner: string;
|
|
5
|
+
repo: string;
|
|
6
|
+
host: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function parseGitRemoteUrl(url: string): ParsedGitRemote | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const r={"github.com":"github","gitlab.com":"gitlab","bitbucket.org":"bitbucket"},s=/^https?:\/\/([^/]+)\/(.+?)\/([^/.]+?)(?:\.git)?$/,h=/^git@([^:]+):(.+?)\/([^/.]+?)(?:\.git)?$/,u=/^ssh:\/\/[^@]+@([^:/]+)(?::\d+)?\/(.+?)\/([^/.]+?)(?:\.git)?$/;function g(t){if(!t)return null;const i=t.replace(/^(https?:\/\/)[^@]+@/,"$1"),n=i.match(s)??i.match(h)??t.match(u);if(!n)return null;const[,o,c,e]=n;return!o||!c||!e?null:{provider:r[o]??"other",owner:c,repo:e,host:o}}export{g as parseGitRemoteUrl};
|
package/dist/index.d.ts
CHANGED
|
@@ -1 +1,11 @@
|
|
|
1
|
-
export
|
|
1
|
+
export { DNS_APEX, getDomainExportNames, type ManagedDomainExports } from "./domainExports.js";
|
|
2
|
+
export { toPascalCase, toKebab, toValidDatabaseName, capitalise, getSafeZoneName } from "./caseConversion.js";
|
|
3
|
+
export { normaliseError, getErrorMessage, hasErrorCode, getErrorCode, getErrorStack, formatErrorString } from "./errorUtils.js";
|
|
4
|
+
export { singleton } from "./singleton.js";
|
|
5
|
+
export { DANGEROUS_ENV_VARS, filterDangerousEnvVars, maskSensitiveOutput, parseShellArgs } from "./securityHelpers.js";
|
|
6
|
+
export { sleep } from "./sleep.js";
|
|
7
|
+
export { STANDARD_ENVIRONMENTS, STRUCTURAL_ENVIRONMENTS, type StandardEnvironment, isValidEnvironment, ENVIRONMENT_LABELS, getEnvironmentLabel } from "./environments.js";
|
|
8
|
+
export { RESOURCE_CATEGORIES, type ResourceCategory, categoriseResource, getExpectedDuration, getFriendlyResourceType } from "./resourceCategorisation.js";
|
|
9
|
+
export { parseGitRemoteUrl, type GitProvider, type ParsedGitRemote } from "./gitRemoteParser.js";
|
|
10
|
+
export { abbreviateRegion } from "./regions.js";
|
|
11
|
+
export { deriveRegionsFromOrgConfig, deriveTargets, deriveAllTargets, findTarget, generateTargetName, type OrgConfigRegions, type TargetAccount, type DerivedTarget } from "./targets.js";
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1 @@
|
|
|
1
|
-
"
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./src"), exports);
|
|
18
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsd0NBQXNCIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0ICogZnJvbSAnLi9zcmMnOyJdfQ==
|
|
1
|
+
import{DNS_APEX as o,getDomainExportNames as t}from"./domainExports.js";import{toPascalCase as i,toKebab as s,toValidDatabaseName as E,capitalise as g,getSafeZoneName as m}from"./caseConversion.js";import{normaliseError as p,getErrorMessage as N,hasErrorCode as f,getErrorCode as R,getErrorStack as l,formatErrorString as S}from"./errorUtils.js";import{singleton as T}from"./singleton.js";import{DANGEROUS_ENV_VARS as A,filterDangerousEnvVars as v,maskSensitiveOutput as D,parseShellArgs as O}from"./securityHelpers.js";import{sleep as c}from"./sleep.js";import{STANDARD_ENVIRONMENTS as _,STRUCTURAL_ENVIRONMENTS as b,isValidEnvironment as u,ENVIRONMENT_LABELS as U,getEnvironmentLabel as I}from"./environments.js";import{RESOURCE_CATEGORIES as M,categoriseResource as G,getExpectedDuration as h,getFriendlyResourceType as k}from"./resourceCategorisation.js";import{parseGitRemoteUrl as F}from"./gitRemoteParser.js";import{abbreviateRegion as B}from"./regions.js";import{deriveRegionsFromOrgConfig as X,deriveTargets as Z,deriveAllTargets as j,findTarget as q,generateTargetName as w}from"./targets.js";export{A as DANGEROUS_ENV_VARS,o as DNS_APEX,U as ENVIRONMENT_LABELS,M as RESOURCE_CATEGORIES,_ as STANDARD_ENVIRONMENTS,b as STRUCTURAL_ENVIRONMENTS,B as abbreviateRegion,g as capitalise,G as categoriseResource,j as deriveAllTargets,X as deriveRegionsFromOrgConfig,Z as deriveTargets,v as filterDangerousEnvVars,q as findTarget,S as formatErrorString,w as generateTargetName,t as getDomainExportNames,I as getEnvironmentLabel,R as getErrorCode,N as getErrorMessage,l as getErrorStack,h as getExpectedDuration,k as getFriendlyResourceType,m as getSafeZoneName,f as hasErrorCode,u as isValidEnvironment,D as maskSensitiveOutput,p as normaliseError,F as parseGitRemoteUrl,O as parseShellArgs,T as singleton,c as sleep,s as toKebab,i as toPascalCase,E as toValidDatabaseName};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Input shape for the cross-recurrence pattern fingerprint used by the
|
|
4
|
+
* Auto-snoozed view pill pipeline.
|
|
5
|
+
*
|
|
6
|
+
* See `aiDocs/designs/2026-04-19-auto-snoozed-pill.md` §4.2. The fingerprint
|
|
7
|
+
* collides intentionally across distinct `insight_id`s representing the same
|
|
8
|
+
* finding class within an org, which is what the 3-in-30d dismissal
|
|
9
|
+
* heuristic aggregates over.
|
|
10
|
+
*/
|
|
11
|
+
export declare const FingerprintInputSchema: z.ZodObject<{
|
|
12
|
+
organisationId: z.ZodString;
|
|
13
|
+
category: z.ZodString;
|
|
14
|
+
resourceType: z.ZodString;
|
|
15
|
+
metricName: z.ZodString;
|
|
16
|
+
applicationId: z.ZodString;
|
|
17
|
+
}, z.core.$strict>;
|
|
18
|
+
export type PatternFingerprintInput = z.infer<typeof FingerprintInputSchema>;
|
|
19
|
+
/**
|
|
20
|
+
* Compute a stable SHA-256 hex digest identifying a finding class within an
|
|
21
|
+
* organisation. Deterministic: the same input yields the same digest on every
|
|
22
|
+
* invocation across every runtime (webapp SSR, CLI, monitoring worker).
|
|
23
|
+
*
|
|
24
|
+
* Throws on invalid input so producers fail loudly at the call site rather
|
|
25
|
+
* than writing an empty fingerprint that would be filtered out of the MV.
|
|
26
|
+
*/
|
|
27
|
+
export declare function computePatternFingerprint(input: PatternFingerprintInput): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createHash as p}from"node:crypto";import{z as r}from"zod";const m=r.object({organisationId:r.string().min(1),category:r.string().min(1),resourceType:r.string().min(1),metricName:r.string().min(1),applicationId:r.string().min(1)}).strict();function u(e){const t=m.safeParse(e);if(!t.success)throw new Error(`Invalid pattern fingerprint input: ${t.error.message}`);const{organisationId:n,category:i,resourceType:a,metricName:o,applicationId:s}=t.data,c=`${n}|${i}|${a}|${o}|${s}`;return p("sha256").update(c).digest("hex")}export{m as FingerprintInputSchema,u as computePatternFingerprint};
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared logger for @fjall/util and packages that depend on it (deploy-core, generator).
|
|
3
|
+
*
|
|
4
|
+
* By default, output goes to stderr. Consumers that need richer behaviour (Ink UI
|
|
5
|
+
* suppression, file logging, structured JSONL) call {@link configureLogWriter} at
|
|
6
|
+
* startup to redirect all output through their own logging infrastructure.
|
|
7
|
+
*
|
|
8
|
+
* Level gating (debug requires FJALL_DEBUG, trace requires FJALL_TRACE) is handled
|
|
9
|
+
* here — the writer is only invoked for levels that pass the gate.
|
|
10
|
+
*/
|
|
11
|
+
export type LogLevel = "info" | "warn" | "error" | "debug" | "trace";
|
|
12
|
+
/**
|
|
13
|
+
* Function that receives a log entry. The CLI replaces the default writer so
|
|
14
|
+
* deploy-core output is routed through the CLI logger (which handles Ink
|
|
15
|
+
* suppression, file rotation, and console formatting).
|
|
16
|
+
*/
|
|
17
|
+
export type LogWriter = (level: LogLevel, component: string, message: string, data?: Record<string, unknown>) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Replace the log writer. Call once at startup — typically the CLI calls this
|
|
20
|
+
* to route deploy-core output through its own logger.
|
|
21
|
+
*/
|
|
22
|
+
export declare function configureLogWriter(writer: LogWriter): void;
|
|
23
|
+
/** Reset to the default stderr writer (useful in tests). */
|
|
24
|
+
export declare function resetLogWriter(): void;
|
|
25
|
+
export declare const logger: {
|
|
26
|
+
info(component: string, message: string, data?: Record<string, unknown>): void;
|
|
27
|
+
warn(component: string, message: string, data?: Record<string, unknown>): void;
|
|
28
|
+
error(component: string, message: string, data?: Record<string, unknown>): void;
|
|
29
|
+
debug(component: string, message: string, data?: Record<string, unknown>): void;
|
|
30
|
+
trace(component: string, message: string, data?: Record<string, unknown>): void;
|
|
31
|
+
isDebugEnabled(): boolean;
|
|
32
|
+
};
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const u=r=>{try{return JSON.stringify(r)}catch{return"[unserializable]"}},i=(r,e,t,o)=>{const s=new Date().toISOString(),c=r.toUpperCase(),a=o?` ${u(o)}`:"";process.stderr.write(`[${s}] [${c}] [${e}] ${t}${a}
|
|
2
|
+
`)};let n=i;function g(r){n=r}function f(){n=i}const p={info(r,e,t){n("info",r,e,t)},warn(r,e,t){n("warn",r,e,t)},error(r,e,t){n("error",r,e,t)},debug(r,e,t){process.env.FJALL_DEBUG==="true"&&n("debug",r,e,t)},trace(r,e,t){process.env.FJALL_TRACE==="true"&&n("trace",r,e,t)},isDebugEnabled(){return process.env.FJALL_DEBUG==="true"}};export{g as configureLogWriter,p as logger,f as resetLogWriter};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Region abbreviation utilities shared across CLI, webapp, and deploy-core.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Abbreviate an AWS region name to a compact form.
|
|
6
|
+
* e.g. "us-east-1" → "use1", "eu-west-1" → "euw1"
|
|
7
|
+
*/
|
|
8
|
+
export declare function abbreviateRegion(region: string): string;
|
package/dist/regions.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function n(e){const t=e.split("-");return t.length===3&&t[0]&&t[1]&&t[1].length>0&&t[2]?`${t[0]}${t[1][0]}${t[2]}`:e}export{n as abbreviateRegion};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared resource categorisation — single source of truth for mapping
|
|
3
|
+
* AWS resource types to topology categories, expected durations, and
|
|
4
|
+
* friendly display names.
|
|
5
|
+
*
|
|
6
|
+
* Consumed by: CLI, deploy-core, webapp (via deploy-core re-export).
|
|
7
|
+
*/
|
|
8
|
+
export declare const RESOURCE_CATEGORIES: readonly ["security", "network", "compute", "database", "storage", "monitoring", "dns", "identity", "bootstrap", "events", "registry", "backup"];
|
|
9
|
+
export type ResourceCategory = (typeof RESOURCE_CATEGORIES)[number];
|
|
10
|
+
/**
|
|
11
|
+
* Maps an AWS resource type to a topology category.
|
|
12
|
+
* Falls back to "compute" for unknown types (neutral default).
|
|
13
|
+
*/
|
|
14
|
+
export declare function categoriseResource(resourceType: string): ResourceCategory;
|
|
15
|
+
/** Returns the set of resource types that fell through to the default category. */
|
|
16
|
+
export declare function getUnmappedResourceTypes(): ReadonlySet<string>;
|
|
17
|
+
/** Clears the unmapped resource types set (for testing). */
|
|
18
|
+
export declare function clearUnmappedResourceTypes(): void;
|
|
19
|
+
/**
|
|
20
|
+
* Returns the expected creation duration in seconds for a given AWS resource type.
|
|
21
|
+
* Falls back to 30s for unmapped types.
|
|
22
|
+
*/
|
|
23
|
+
export declare function getExpectedDuration(resourceType: string): number;
|
|
24
|
+
/**
|
|
25
|
+
* Returns a friendly display name for an AWS resource type.
|
|
26
|
+
* Falls back to the last segment after `::` for unknown types.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getFriendlyResourceType(resourceType: string): string;
|