@fjall/util 16.0.0 → 17.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,7 +9,7 @@
9
9
  "package.json",
10
10
  "tsconfig.json"
11
11
  ],
12
- "hash": "ba916d435ac03ff7958c55ae2c73af54bede72354249dfb4ef1113181eb540e9",
12
+ "hash": "f422feb49012fb05b3f9d2fd2b45cc3ee0208221d45ca326feaf9670defe9b94",
13
13
  "files": {
14
14
  "src/async/concurrency.ts": "3c7ebe0222a720f9",
15
15
  "src/async/singleton.ts": "7bcc21b0fb920561",
@@ -30,6 +30,7 @@
30
30
  "src/capacityIdentity.ts": "9e59bfc92a5ba361",
31
31
  "src/cdkTmpdirCleanup.ts": "4574b3f10e3ab0f9",
32
32
  "src/cfn/physicalNameProperties.ts": "35d3c6f28079c593",
33
+ "src/clickhouse.ts": "655c74cdba292c29",
33
34
  "src/config.ts": "11c577414a91c542",
34
35
  "src/constructMap.ts": "b81fc8e90015467f",
35
36
  "src/deploy/approvalTokenOutput.ts": "4be7b52825d88a37",
@@ -107,12 +108,12 @@
107
108
  "src/repo/scanTypes.ts": "1fda805a7d8de93b",
108
109
  "src/resourceCategorisation.ts": "a140d1b3cb38be33",
109
110
  "src/secrets.ts": "50258d463e4e67c3",
110
- "src/securityHelpers.ts": "3f78606079c8f45e",
111
+ "src/securityHelpers.ts": "2632215f57e1528c",
111
112
  "src/targets.ts": "126280f0a5762e21",
112
113
  "../scripts/minify-dist.mjs": "6b2e4b0df8aec601",
113
- "package.json": "7f76f69d5332a72a",
114
+ "package.json": "9348c162dc98ac3a",
114
115
  "tsconfig.json": "db759831ad95f396"
115
116
  },
116
- "outputHash": "32a62eaf77c356111bdfe94533cbc82386b4e9160408ec27934c7edd00cf0b5c",
117
- "outputFileCount": 203
117
+ "outputHash": "be2acc2cb4cb76c6e5e473419fe138f97f0153de67327ce411e137a10c62f3a3",
118
+ "outputFileCount": 205
118
119
  }
package/dist/.minified CHANGED
@@ -1 +1 @@
1
- 101 files minified at 2026-08-22T02:34:24.365Z
1
+ 102 files minified at 2026-08-23T02:03:02.114Z
@@ -0,0 +1,94 @@
1
+ /**
2
+ * ClickHouse sizing bounds shared by the two packages that enforce them.
3
+ *
4
+ * `@fjall/generator` validates a caller-supplied volume size when scaffolding
5
+ * or modifying an `infrastructure.ts` declaration; `@fjall/components-
6
+ * infrastructure` validates the same value again at synth, where it becomes a
7
+ * real EBS volume. Neither can import the other's bound — the construct
8
+ * package depends on the generator, not the reverse, and the generator must
9
+ * stay free of CDK — so a bound declared in either place is a bound the other
10
+ * copies.
11
+ *
12
+ * That copy went wrong once already. `backupRetentionDays` reused the RDS
13
+ * schema, capping at 35 (the AWS ceiling on RDS *automated* backups) while
14
+ * the ClickHouse construct allowed 3650 (an S3 lifecycle) — so a legal
15
+ * retention was rejected before it reached the construct that would have
16
+ * accepted it. Both knobs now take their bounds from here, which is the only
17
+ * arrangement in which the two enforcement sites cannot disagree.
18
+ */
19
+ /** Smallest ClickHouse data volume, in GiB.
20
+ *
21
+ * Below this the disk-critical alarm's free-space floor stops being a slice
22
+ * of the volume and becomes most of it, so the alarm reports "this disk was
23
+ * always too small" rather than "the disk is nearly gone". The construct
24
+ * enforces the floor-to-volume ratio directly (`resolveClickHouseStorage`);
25
+ * this is the same constraint expressed as a flat minimum, for the schema
26
+ * layer that has no view of the alarm configuration. */
27
+ export declare const CLICKHOUSE_MIN_STORAGE_GB = 100;
28
+ /** Largest ClickHouse data volume, in GiB — the gp3 ceiling (16 TiB). Beyond
29
+ * it EBS rejects the CreateVolume outright, so catching it early turns a
30
+ * mid-deploy rollback into a message. */
31
+ export declare const CLICKHOUSE_MAX_STORAGE_GB = 16384;
32
+ /** Lowest provisioned IOPS on a ClickHouse data volume — the gp3 baseline.
33
+ *
34
+ * gp3 includes 3,000 IOPS in the volume price regardless of size; EBS
35
+ * rejects anything lower, so there is no cheaper setting to express. */
36
+ export declare const CLICKHOUSE_MIN_IOPS = 3000;
37
+ /** Highest provisioned IOPS on a ClickHouse data volume — the gp3 ceiling.
38
+ *
39
+ * Above 16,000 the instance's own EBS bandwidth, not the volume, is usually
40
+ * the binding limit; the ceiling is still the service's, because the
41
+ * instance type is the caller's choice and a bound that tracked it would
42
+ * reject a legal volume on a large host. */
43
+ export declare const CLICKHOUSE_MAX_IOPS = 80000;
44
+ /** Most provisioned IOPS EBS allows per GiB of gp3 volume (500 IOPS/GiB).
45
+ *
46
+ * Coupled to `storageGb`: 80,000 IOPS needs a 160 GiB volume, so a small
47
+ * volume cannot carry the top of the IOPS band. The construct enforces the
48
+ * pair; this is the constant both its check and its message read. */
49
+ export declare const CLICKHOUSE_MAX_IOPS_PER_GB = 500;
50
+ /** Lowest provisioned throughput on a ClickHouse data volume, in MiB/s — the
51
+ * gp3 baseline, included in the volume price like the baseline IOPS. */
52
+ export declare const CLICKHOUSE_MIN_THROUGHPUT_MBPS = 125;
53
+ /** Highest provisioned throughput on a ClickHouse data volume, in MiB/s —
54
+ * the gp3 ceiling. */
55
+ export declare const CLICKHOUSE_MAX_THROUGHPUT_MBPS = 2000;
56
+ /** IOPS EBS requires per MiB/s of gp3 throughput (a 4:1 ratio — the service
57
+ * quotes it as 0.25 MiB/s per IOPS).
58
+ *
59
+ * Coupled to `iops`: 1,000 MiB/s needs 4,000 IOPS, so raising throughput on
60
+ * a baseline-IOPS volume is rejected by EBS until IOPS rises with it. The
61
+ * construct enforces the pair and names the IOPS the throughput needs. */
62
+ export declare const CLICKHOUSE_IOPS_PER_THROUGHPUT_MBPS = 4;
63
+ /** Shortest ClickHouse backup retention, in days.
64
+ *
65
+ * One day is the floor because the backup task runs daily: a retention below
66
+ * the backup interval expires each backup before its successor exists, which
67
+ * is a window with no backup at all rather than a short history. */
68
+ export declare const CLICKHOUSE_MIN_BACKUP_RETENTION_DAYS = 1;
69
+ /** Longest ClickHouse backup retention, in days — ten years.
70
+ *
71
+ * ClickHouse backups are S3 objects aged out by a lifecycle rule, so the
72
+ * ceiling is a sanity bound rather than a service limit. It is deliberately
73
+ * NOT the 35 the RDS knob carries: that is the AWS cap on RDS *automated*
74
+ * backups, a mechanism ClickHouse does not use. Sharing one schema across
75
+ * both is what rejected a legal ClickHouse retention before it reached the
76
+ * construct that would have accepted it. */
77
+ export declare const CLICKHOUSE_MAX_BACKUP_RETENTION_DAYS = 3650;
78
+ /** Every EC2 instance type the ClickHouse construct supports, as data.
79
+ *
80
+ * Two packages enforce this vocabulary and neither can import the other
81
+ * (same dependency shape as the bounds above): the construct keys its
82
+ * hardware-spec table on it (`CLICKHOUSE_INSTANCE_SPECS` is a
83
+ * `Record<ClickHouseInstanceType, …>`, so a type added here without a spec
84
+ * entry — or vice versa — fails to compile), and the generator validates a
85
+ * caller's `instanceType` against it at scaffold time, where the error
86
+ * names the legal values instead of surfacing as a synth throw mid-deploy.
87
+ *
88
+ * This tuple exists because the two sites DID drift: the resilient and
89
+ * enterprise tier presets shipped `m6g.large`/`m6g.xlarge` — types the
90
+ * construct never supported — so both tiers scaffolded ClickHouse
91
+ * declarations that could not synthesise. A shared vocabulary makes that
92
+ * class of preset unrepresentable. */
93
+ export declare const CLICKHOUSE_INSTANCE_TYPES: readonly ["t4g.medium", "m7g.medium", "m8g.medium", "r7g.medium", "r8g.medium", "m7g.large", "m8g.large", "r7g.large", "r8g.large", "m7g.xlarge", "m8g.xlarge", "r7g.xlarge", "r8g.xlarge"];
94
+ export type ClickHouseInstanceType = (typeof CLICKHOUSE_INSTANCE_TYPES)[number];
@@ -0,0 +1 @@
1
+ const _=100,r=16384,C=3e3,e=8e4,S=500,t=125,o=2e3,I=4,O=1,g=3650,E=["t4g.medium","m7g.medium","m8g.medium","r7g.medium","r8g.medium","m7g.large","m8g.large","r7g.large","r8g.large","m7g.xlarge","m8g.xlarge","r7g.xlarge","r8g.xlarge"];export{E as CLICKHOUSE_INSTANCE_TYPES,I as CLICKHOUSE_IOPS_PER_THROUGHPUT_MBPS,g as CLICKHOUSE_MAX_BACKUP_RETENTION_DAYS,e as CLICKHOUSE_MAX_IOPS,S as CLICKHOUSE_MAX_IOPS_PER_GB,r as CLICKHOUSE_MAX_STORAGE_GB,o as CLICKHOUSE_MAX_THROUGHPUT_MBPS,O as CLICKHOUSE_MIN_BACKUP_RETENTION_DAYS,C as CLICKHOUSE_MIN_IOPS,_ as CLICKHOUSE_MIN_STORAGE_GB,t as CLICKHOUSE_MIN_THROUGHPUT_MBPS};
@@ -1 +1 @@
1
- var o=Object.defineProperty;var n=(t,_)=>o(t,"name",{value:_,configurable:!0});const p=new Set(["NODE_OPTIONS","NODE_PATH","NODE_EXTRA_CA_CERTS","NODE_DEBUG","NODE_PRESERVE_SYMLINKS","LD_PRELOAD","LD_LIBRARY_PATH","LD_AUDIT","LD_BIND_NOW","DYLD_INSERT_LIBRARIES","DYLD_LIBRARY_PATH","DYLD_FRAMEWORK_PATH","PYTHONPATH","PYTHONSTARTUP","PERL5LIB","PERL5OPT","RUBYLIB","RUBYOPT","HOME","XDG_CONFIG_HOME","AWS_SHARED_CREDENTIALS_FILE","AWS_CONFIG_FILE","SHELL","BASH_ENV","ENV","ZDOTDIR"]);function D(t){return Object.fromEntries(Object.entries(t).filter(([_])=>!p.has(_.toUpperCase())))}n(D,"filterDangerousEnvVars");const R=/fjall_(?:ak|dk)_[A-Z2-7]{16}\.[A-Z2-7]{40}/,h=/(fjall_(?:ak|dk)_)[A-Z2-7]{16}\.[A-Z2-7]{40}/g,s="***",i="[REDACTED]",l=`-----BEGIN ${i} PRIVATE KEY-----...-----END ${i} PRIVATE KEY-----`;function u(t){return t.replace(/-----BEGIN [A-Z ]*PRIVATE KEY-----(?:[^"\\]|\\[\\nrt"])*?-----END [A-Z ]*PRIVATE KEY-----/g,()=>l).replace(/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,()=>l).replace(/(\w+:\/\/[^:]+:)[^@]+(@)/gi,(_,e,a)=>`${e}${s}${a}`).replace(/(?<![a-zA-Z])(password|passwd|secret|api[_-]?key|token|auth|credential)([=:])["']?[^\s"']+/gi,(_,e,a)=>`${e}${a}${s}`).replace(/(?<="[a-zA-Z0-9_-]*(?:password|passwd|secret|api[_-]?key|token|auth|credential)":\s*")(?!ghu_|ghs_|ghp_|gho_|github_pat_|(?:sk|rk)_(?:live|test)_|whsec_|fjall_(?:ak|dk)_|AKIA|ASIA|%[a-zA-Z])[^"]+/gi,s).replace(/(?<=(?<!\/)(?:password|passwd|secret|api[_-]?key|token|auth|credential)\s*[=:]\s*["']?)(?!ghu_|ghs_|ghp_|gho_|github_pat_|(?:sk|rk)_(?:live|test)_|whsec_|fjall_(?:ak|dk)_|AKIA|ASIA|%[a-zA-Z])[^\s"']+/gi,s).replace(/\b(ghu_|ghs_|ghp_|gho_|github_pat_)[A-Za-z0-9_]+/g,(_,e)=>`${e}${s}`).replace(/\b(sk|rk)_(live|test)_[A-Za-z0-9]{8,}/g,(_,e,a)=>`${e}_${a}_${s}`).replace(/\bwhsec_[A-Za-z0-9]{8,}/g,()=>`whsec_${s}`).replace(/(?<=Authorization:\s*Bearer\s+)[A-Za-z0-9._~+/=-]+/gi,s).replace(/\b(AKIA|ASIA)[A-Z0-9]{12,}\b/g,(_,e)=>`${e}${s}`).replace(/(?<=aws_secret_access_key\s*=\s*["']?|SecretAccessKey[=:]\s*|"(aws)?secretAccessKey":\s*"|"aws_secret_access_key":\s*")[A-Za-z0-9/+=]{40,}/gi,s).replace(/(?<="(aws)?sessionToken":\s*"|"aws_session_token":\s*")[^"]+/gi,s).replace(/(?<=aws_session_token\s*[=:]\s*["']?|SessionToken[=:]\s*["']?)[^\s"']+/gi,s).replace(/(?<="(internal[Aa]piKey|fjallCallbackToken)":\s*")[^"]+/g,s).replace(h,(_,e)=>`${e}${s}`)}n(u,"maskSensitiveOutput");function T(t){const _=[];let e="",a=!1,E=!1,A=!1,c=!1;for(const r of t){if(A){e+=r,A=!1;continue}if(r==="\\"&&!a){A=!0;continue}if(r==="'"&&!E){a=!a,c=!0;continue}if(r==='"'&&!a){E=!E,c=!0;continue}if(r===" "&&!a&&!E){(e||c)&&(_.push(e),e="",c=!1);continue}e+=r}if(a||E)throw new Error(`Unbalanced ${a?"single":"double"} quote in command: ${t.slice(0,80)}`);if(A)throw new Error(`Trailing backslash in command: ${t.slice(0,80)}`);return(e||c)&&_.push(e),_}n(T,"parseShellArgs");export{p as DANGEROUS_ENV_VARS,s as MASKED_VALUE_MARKER,i as REDACTED_KEY_SENTINEL,R as SCOPED_TOKEN_REGEX,D as filterDangerousEnvVars,u as maskSensitiveOutput,T as parseShellArgs};
1
+ var g=Object.defineProperty;var A=(_,t)=>g(_,"name",{value:t,configurable:!0});const R=new Set(["NODE_OPTIONS","NODE_PATH","NODE_EXTRA_CA_CERTS","NODE_DEBUG","NODE_PRESERVE_SYMLINKS","LD_PRELOAD","LD_LIBRARY_PATH","LD_AUDIT","LD_BIND_NOW","DYLD_INSERT_LIBRARIES","DYLD_LIBRARY_PATH","DYLD_FRAMEWORK_PATH","PYTHONPATH","PYTHONSTARTUP","PERL5LIB","PERL5OPT","RUBYLIB","RUBYOPT","HOME","XDG_CONFIG_HOME","AWS_SHARED_CREDENTIALS_FILE","AWS_CONFIG_FILE","SHELL","BASH_ENV","ENV","ZDOTDIR"]);function T(_){return Object.fromEntries(Object.entries(_).filter(([t])=>!R.has(t.toUpperCase())))}A(T,"filterDangerousEnvVars");const u=/fjall_(?:ak|dk)_[A-Z2-7]{16}\.[A-Z2-7]{40}/,S=/(fjall_(?:ak|dk)_)[A-Z2-7]{16}\.[A-Z2-7]{40}/g,s="***",i="[REDACTED]",o=`-----BEGIN ${i} PRIVATE KEY-----...-----END ${i} PRIVATE KEY-----`,l=String.raw`(?:next|continuation|pagination|page|forward|backward)+token`,p=String.raw`(?!ghu_|ghs_|ghp_|gho_|github_pat_|(?:sk|rk)_(?:live|test)_|whsec_|fjall_(?:ak|dk)_|AKIA|ASIA|%[a-zA-Z])`,D=new RegExp(String.raw`(?<!"${l}":\s*")`+String.raw`(?<="[a-zA-Z0-9_-]*(?:password|passwd|secret|api[_-]?key|token|auth|credential)":\s*")`+p+String.raw`[^"]+`,"gi"),I=new RegExp(String.raw`(?<!${l}\s*[=:]\s*["']?)`+String.raw`(?<=(?<!/)(?:password|passwd|secret|api[_-]?key|token|auth|credential)\s*[=:]\s*["']?)`+p+String.raw`[^\s"']+`,"gi");function w(_){return _.replace(/-----BEGIN [A-Z ]*PRIVATE KEY-----(?:[^"\\]|\\[\\nrt"])*?-----END [A-Z ]*PRIVATE KEY-----/g,()=>o).replace(/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,()=>o).replace(/(\w+:\/\/[^:]+:)[^@]+(@)/gi,(t,e,a)=>`${e}${s}${a}`).replace(/(?<![a-zA-Z])(password|passwd|secret|api[_-]?key|token|auth|credential)([=:])["']?[^\s"']+/gi,(t,e,a)=>`${e}${a}${s}`).replace(D,s).replace(I,s).replace(/\b(ghu_|ghs_|ghp_|gho_|github_pat_)[A-Za-z0-9_]+/g,(t,e)=>`${e}${s}`).replace(/\b(sk|rk)_(live|test)_[A-Za-z0-9]{8,}/g,(t,e,a)=>`${e}_${a}_${s}`).replace(/\bwhsec_[A-Za-z0-9]{8,}/g,()=>`whsec_${s}`).replace(/(?<=Authorization:\s*Bearer\s+)[A-Za-z0-9._~+/=-]+/gi,s).replace(/\b(AKIA|ASIA)[A-Z0-9]{12,}\b/g,(t,e)=>`${e}${s}`).replace(/(?<=aws_secret_access_key\s*=\s*["']?|SecretAccessKey[=:]\s*|"(aws)?secretAccessKey":\s*"|"aws_secret_access_key":\s*")[A-Za-z0-9/+=]{40,}/gi,s).replace(/(?<="(aws)?sessionToken":\s*"|"aws_session_token":\s*")[^"]+/gi,s).replace(/(?<=aws_session_token\s*[=:]\s*["']?|SessionToken[=:]\s*["']?)[^\s"']+/gi,s).replace(/(?<="(internal[Aa]piKey|fjallCallbackToken)":\s*")[^"]+/g,s).replace(S,(t,e)=>`${e}${s}`)}A(w,"maskSensitiveOutput");function h(_){const t=[];let e="",a=!1,E=!1,c=!1,n=!1;for(const r of _){if(c){e+=r,c=!1;continue}if(r==="\\"&&!a){c=!0;continue}if(r==="'"&&!E){a=!a,n=!0;continue}if(r==='"'&&!a){E=!E,n=!0;continue}if(r===" "&&!a&&!E){(e||n)&&(t.push(e),e="",n=!1);continue}e+=r}if(a||E)throw new Error(`Unbalanced ${a?"single":"double"} quote in command: ${_.slice(0,80)}`);if(c)throw new Error(`Trailing backslash in command: ${_.slice(0,80)}`);return(e||n)&&t.push(e),t}A(h,"parseShellArgs");export{R as DANGEROUS_ENV_VARS,s as MASKED_VALUE_MARKER,i as REDACTED_KEY_SENTINEL,u as SCOPED_TOKEN_REGEX,T as filterDangerousEnvVars,w as maskSensitiveOutput,h as parseShellArgs};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fjall/util",
3
- "version": "16.0.0",
3
+ "version": "17.0.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/fjall-tech/fjall.git",
@@ -23,6 +23,10 @@
23
23
  "types": "./dist/config.d.ts",
24
24
  "default": "./dist/config.js"
25
25
  },
26
+ "./clickhouse": {
27
+ "types": "./dist/clickhouse.d.ts",
28
+ "default": "./dist/clickhouse.js"
29
+ },
26
30
  "./constructMap": {
27
31
  "types": "./dist/constructMap.d.ts",
28
32
  "default": "./dist/constructMap.js"