@fjall/util 0.89.4 → 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.
Files changed (55) hide show
  1. package/LICENSE +50 -21
  2. package/README.md +22 -0
  3. package/dist/.minified +1 -0
  4. package/dist/Config.d.ts +76 -0
  5. package/dist/Config.js +1 -0
  6. package/dist/aws/CloudFormationFailureAnalyser.d.ts +32 -0
  7. package/dist/aws/CloudFormationFailureAnalyser.js +1 -0
  8. package/dist/aws/cloudformationTypes.d.ts +17 -0
  9. package/dist/aws/cloudformationTypes.js +1 -0
  10. package/dist/aws/errors.d.ts +26 -0
  11. package/dist/aws/errors.js +1 -0
  12. package/dist/aws/index.d.ts +3 -0
  13. package/dist/aws/index.js +1 -0
  14. package/dist/caseConversion.js +1 -0
  15. package/dist/constructMap.d.ts +80 -0
  16. package/dist/constructMap.js +1 -0
  17. package/dist/{src/domainExports.d.ts → domainExports.d.ts} +3 -1
  18. package/dist/domainExports.js +1 -0
  19. package/dist/environments.d.ts +23 -0
  20. package/dist/environments.js +1 -0
  21. package/dist/errorUtils.js +1 -0
  22. package/dist/fsHelpers.d.ts +4 -0
  23. package/dist/fsHelpers.js +1 -0
  24. package/dist/gitRemoteParser.d.ts +8 -0
  25. package/dist/gitRemoteParser.js +1 -0
  26. package/dist/index.d.ts +11 -1
  27. package/dist/index.js +1 -18
  28. package/dist/insights/computePatternFingerprint.d.ts +27 -0
  29. package/dist/insights/computePatternFingerprint.js +1 -0
  30. package/dist/logger.d.ts +32 -0
  31. package/dist/logger.js +2 -0
  32. package/dist/regions.d.ts +8 -0
  33. package/dist/regions.js +1 -0
  34. package/dist/resourceCategorisation.d.ts +28 -0
  35. package/dist/resourceCategorisation.js +1 -0
  36. package/dist/securityHelpers.d.ts +38 -0
  37. package/dist/securityHelpers.js +1 -0
  38. package/dist/singleton.d.ts +2 -0
  39. package/dist/singleton.js +1 -0
  40. package/dist/sleep.d.ts +4 -0
  41. package/dist/sleep.js +1 -0
  42. package/dist/targets.d.ts +57 -0
  43. package/dist/targets.js +1 -0
  44. package/package.json +55 -8
  45. package/dist/src/Config.d.ts +0 -114
  46. package/dist/src/Config.js +0 -276
  47. package/dist/src/caseConversion.js +0 -59
  48. package/dist/src/domainExports.js +0 -18
  49. package/dist/src/errorUtils.js +0 -69
  50. package/dist/src/index.d.ts +0 -5
  51. package/dist/src/index.js +0 -37
  52. package/dist/src/logger.d.ts +0 -12
  53. package/dist/src/logger.js +0 -36
  54. /package/dist/{src/caseConversion.d.ts → caseConversion.d.ts} +0 -0
  55. /package/dist/{src/errorUtils.d.ts → errorUtils.d.ts} +0 -0
package/LICENSE CHANGED
@@ -1,21 +1,50 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Fjall
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
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
@@ -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 certificate ARN via Fn.importValue).
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,4 @@
1
+ /**
2
+ * Async check for file existence. Replaces blocking existsSync().
3
+ */
4
+ export declare function fileExists(filePath: string): Promise<boolean>;
@@ -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 * from './src';
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
- "use strict";
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};
@@ -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;
@@ -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;