@fjall/util 0.96.0 → 0.99.3

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 (76) hide show
  1. package/dist/.minified +1 -1
  2. package/dist/Config.d.ts +1 -1
  3. package/dist/appPath.d.ts +29 -0
  4. package/dist/appPath.js +1 -0
  5. package/dist/constructMap.d.ts +9 -13
  6. package/dist/constructMap.js +1 -1
  7. package/dist/deriveContentHashTag.d.ts +34 -0
  8. package/dist/deriveContentHashTag.js +1 -0
  9. package/dist/docker/DockerCli.build.d.ts +42 -0
  10. package/dist/docker/DockerCli.build.js +1 -0
  11. package/dist/docker/DockerCli.d.ts +135 -0
  12. package/dist/docker/DockerCli.daemon.d.ts +24 -0
  13. package/dist/docker/DockerCli.daemon.js +1 -0
  14. package/dist/docker/DockerCli.js +1 -0
  15. package/dist/docker/DockerCli.registry.d.ts +19 -0
  16. package/dist/docker/DockerCli.registry.js +3 -0
  17. package/dist/docker/abortHelpers.d.ts +18 -0
  18. package/dist/docker/abortHelpers.js +1 -0
  19. package/dist/docker/buildxArgvBuilder.d.ts +15 -0
  20. package/dist/docker/buildxArgvBuilder.js +1 -0
  21. package/dist/docker/dockerCliConstants.d.ts +15 -0
  22. package/dist/docker/dockerCliConstants.js +1 -0
  23. package/dist/docker/dockerCliSchemas.d.ts +88 -0
  24. package/dist/docker/dockerCliSchemas.js +1 -0
  25. package/dist/docker/index.d.ts +10 -0
  26. package/dist/docker/index.js +1 -0
  27. package/dist/docker/metadataFileParser.d.ts +17 -0
  28. package/dist/docker/metadataFileParser.js +1 -0
  29. package/dist/docker/projectBuildxResult.d.ts +35 -0
  30. package/dist/docker/projectBuildxResult.js +1 -0
  31. package/dist/docker/rawjsonParser.d.ts +56 -0
  32. package/dist/docker/rawjsonParser.js +1 -0
  33. package/dist/docker/rawjsonToVertexEvent.d.ts +64 -0
  34. package/dist/docker/rawjsonToVertexEvent.js +1 -0
  35. package/dist/docker/result.d.ts +34 -0
  36. package/dist/docker/result.js +1 -0
  37. package/dist/errorUtils.d.ts +14 -4
  38. package/dist/findInfrastructurePaths.d.ts +62 -0
  39. package/dist/findInfrastructurePaths.js +1 -0
  40. package/dist/findRepoRoot.d.ts +12 -0
  41. package/dist/findRepoRoot.js +1 -0
  42. package/dist/fsHelpers.d.ts +15 -0
  43. package/dist/fsHelpers.js +1 -1
  44. package/dist/fsScan.d.ts +15 -0
  45. package/dist/fsScan.js +1 -0
  46. package/dist/index.d.ts +8 -0
  47. package/dist/index.js +1 -1
  48. package/dist/inferContainerFromCandidates.d.ts +23 -0
  49. package/dist/inferContainerFromCandidates.js +1 -0
  50. package/dist/manifest/index.d.ts +10 -0
  51. package/dist/manifest/index.js +1 -0
  52. package/dist/manifest/io.d.ts +60 -0
  53. package/dist/manifest/io.js +1 -0
  54. package/dist/manifest/schemas.d.ts +163 -0
  55. package/dist/manifest/schemas.js +1 -0
  56. package/dist/mcpProtocol/index.d.ts +362 -0
  57. package/dist/mcpProtocol/index.js +1 -0
  58. package/dist/migration/clickhouseSqlUsers.d.ts +72 -0
  59. package/dist/migration/clickhouseSqlUsers.js +1 -0
  60. package/dist/migration/constants.d.ts +34 -0
  61. package/dist/migration/constants.js +1 -0
  62. package/dist/migration/index.d.ts +3 -0
  63. package/dist/migration/index.js +1 -0
  64. package/dist/migration/pickLatestPrismaMigration.d.ts +10 -0
  65. package/dist/migration/pickLatestPrismaMigration.js +1 -0
  66. package/dist/reservedAppNames.d.ts +30 -0
  67. package/dist/reservedAppNames.js +1 -0
  68. package/dist/scanLocalRepository.d.ts +24 -0
  69. package/dist/scanLocalRepository.js +1 -0
  70. package/dist/scanTypes.d.ts +17 -0
  71. package/dist/scanTypes.js +0 -0
  72. package/dist/securityHelpers.d.ts +11 -1
  73. package/dist/securityHelpers.js +1 -1
  74. package/dist/tokenScopes.d.ts +11 -0
  75. package/dist/tokenScopes.js +1 -0
  76. package/package.json +43 -9
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Returns the lexicographically-latest Prisma migration directory name
3
+ * under `prismaRoot` (e.g. `"20260201000000_add_users"`). Lexicographic
4
+ * order equals chronological order because of the fixed-width timestamp.
5
+ *
6
+ * Synchronous I/O — safe at both CDK synth time (the original use site) and
7
+ * inside the migration runner script (the new use site). Throws when the
8
+ * directory contains no entries matching `PRISMA_MIGRATION_DIR_RE`.
9
+ */
10
+ export declare function pickLatestPrismaMigration(prismaRoot: string): string;
@@ -0,0 +1 @@
1
+ import{readdirSync as n}from"node:fs";import{PRISMA_MIGRATION_DIR_RE as o}from"./constants.js";function d(t){const i=n(t,{withFileTypes:!0}).filter(r=>r.isDirectory()).map(r=>r.name).filter(r=>o.test(r)).sort(),e=i[i.length-1];if(e===void 0)throw new Error(`No Prisma migration directories found under ${t}`);return e}export{d as pickLatestPrismaMigration};
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Reserved application names (cross-system, application-scoped).
3
+ *
4
+ * Both the CLI scaffold schema (`CreateApplicationSchema` in
5
+ * `@fjall/cli/src/validation/commandSchemas.ts`) and the webapp scaffold
6
+ * schema (`ScaffoldRequestSchema` in `webapp/app/routes/api/github/scaffold.ts`)
7
+ * reject any application name whose **lowercase form** appears in this list.
8
+ *
9
+ * The reserved name `"fjall"` is gated here because it denotes the
10
+ * organisation-tier marker under the `fjall/` marker convention (P1) — a
11
+ * customer-owned application sharing that name would collide structurally
12
+ * with the organisation entry under `[container/]fjall/fjall/infrastructure.ts`.
13
+ *
14
+ * **Boundary with `isReservedSlug`** — `webapp/app/.server/utils/reservedSlugs.ts`
15
+ * guards organisation slugs (URL-scoped, webapp-only). This list is
16
+ * application-scoped and ships from `@fjall/util` for cross-system reuse
17
+ * (CLI + webapp + worker). Both coexist; neither references the other.
18
+ *
19
+ * All entries MUST be lowercase. Membership checks lowercase the input
20
+ * before testing, so `"Fjall"`, `"FJALL"`, and `"fjALL"` all reject
21
+ * identically.
22
+ */
23
+ export declare const RESERVED_APP_NAMES: readonly ["fjall"];
24
+ export type ReservedAppName = (typeof RESERVED_APP_NAMES)[number];
25
+ /**
26
+ * Case-insensitive membership check. The canonical entrypoint — consumers
27
+ * MUST route through this helper rather than calling `.includes(name.toLowerCase())`
28
+ * inline, so the lowercasing discipline cannot drift across CLI/webapp/worker.
29
+ */
30
+ export declare function isReservedAppName(name: string): boolean;
@@ -0,0 +1 @@
1
+ const o=["fjall"];function r(e){return o.includes(e.toLowerCase())}export{o as RESERVED_APP_NAMES,r as isReservedAppName};
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Filesystem counterpart to the webapp's `scanInfrastructureFiles`.
3
+ *
4
+ * Walks `repoRoot` recursively, gathers every `infrastructure.ts` file it
5
+ * sees, and hands the resulting iterable to the shared `findInfrastructurePaths`
6
+ * resolver. The resolver owns the marker/boundary rules; this module owns
7
+ * the I/O concerns (symlink loop guard, walk recursion). The exclusion set
8
+ * is declared here but enforced by the shared resolver — `walk()` skips
9
+ * excluded directories at I/O time as an optimisation; the resolver enforces
10
+ * the same set as a defence-in-depth contract.
11
+ *
12
+ * Returns the same `{ paths }` shape as the webapp scan so the helpers are
13
+ * consumer-substitutable (`aiDocs/patterns/cross-system-parity-pattern.md`).
14
+ *
15
+ * Walk discipline:
16
+ * - Symlinks are skipped entirely via `Dirent.isSymbolicLink()` (loop guard).
17
+ * - Excluded directory names: node_modules, .git, dist, build, coverage,
18
+ * .next, .turbo, .vite, .cache.
19
+ */
20
+ import type { ScanPath } from "./scanTypes.js";
21
+ export interface ScanLocalRepositoryResult {
22
+ paths: ScanPath[];
23
+ }
24
+ export declare function scanLocalRepository(repoRoot: string): Promise<ScanLocalRepositoryResult>;
@@ -0,0 +1 @@
1
+ import{readdir as u}from"fs/promises";import{join as f,relative as l,sep as s}from"path";import{findInfrastructurePaths as m,isInfrastructureFile as h}from"./findInfrastructurePaths.js";const o=new Set(["node_modules",".git","dist","build","coverage",".next",".turbo",".vite",".cache"]);async function E(t){const i=[];return await c(t,t,i),{paths:m(i,{excludedPathSegments:o})}}async function c(t,i,n){let r;try{r=await u(i,{withFileTypes:!0})}catch{return}for(const e of r){if(e.isSymbolicLink())continue;const a=f(i,e.name);if(e.isDirectory()){if(o.has(e.name))continue;await c(t,a,n);continue}e.isFile()&&h(e.name)&&n.push({path:d(t,a)})}}function d(t,i){const n=l(t,i);return s==="/"?n:n.split(s).join("/")}export{E as scanLocalRepository};
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Cross-system scan-result types shared by the CLI's `scanLocalRepository`
3
+ * (filesystem) and the webapp's `scanInfrastructureFiles` (GitHub tree API).
4
+ *
5
+ * Both producers MUST emit the same `{ paths }` shape so the helpers are
6
+ * consumer-substitutable. See
7
+ * `aiDocs/patterns/cross-system-parity-pattern.md`.
8
+ *
9
+ * `ScanPath` carries path information only — no name, no kind. The linker
10
+ * is solely responsible for mapping paths to applications and tier kinds.
11
+ */
12
+ export interface ScanPath {
13
+ /** Folder containing `infrastructure.ts` relative to the repo root. */
14
+ configPath: string;
15
+ /** Path to the nearest `fjall/` ancestor (the boundary). */
16
+ boundaryPath: string;
17
+ }
File without changes
@@ -23,11 +23,21 @@ export declare function filterDangerousEnvVars(env: Record<string, string | unde
23
23
  * Matches the FULL scoped agent token: prefix (16 base32) + separator (.) + secret (40 base32).
24
24
  * Base32 alphabet: A-Z2-7. The `.` separator MUST be included or the secret leaks unmasked.
25
25
  * Single source of truth — all masking call sites import this.
26
+ *
27
+ * Public form has NO `g` flag — `.test()` consumers must not share `lastIndex` across calls.
28
+ * The global form lives in `SCOPED_TOKEN_GLOBAL_REGEX` below for the iteration site.
26
29
  */
27
30
  export declare const SCOPED_TOKEN_REGEX: RegExp;
28
31
  /**
29
32
  * Mask sensitive information in output strings to prevent credential leakage.
30
- * Patterns: postgres://user:pass@host, password=xxx, secret=xxx, apikey=xxx
33
+ * Patterns: postgres://user:pass@host, password=xxx, secret=xxx, apikey=xxx,
34
+ * GitHub tokens (ghu_/ghs_/ghp_/gho_/github_pat_), bare AWS access-key IDs
35
+ * (AKIA-prefixed and ASIA-prefixed), AWS secret keys, ARN account IDs,
36
+ * scoped agent tokens.
37
+ *
38
+ * Single source of truth — consumer loggers (CLI, worker, webapp) MUST
39
+ * NOT re-implement these patterns inline. See
40
+ * .claude/rules/security-standards.md § "Masking Patterns Live in @fjall/util".
31
41
  */
32
42
  export declare function maskSensitiveOutput(output: string): string;
33
43
  /**
@@ -1 +1 @@
1
- const i=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 c(e){return Object.fromEntries(Object.entries(e).filter(([s])=>!i.has(s.toUpperCase())))}const o=/fjall_ak_[A-Z2-7]{16}\.[A-Z2-7]{40}/;function l(e){return e.replace(/(\w+:\/\/[^:]+:)[^@]+(@)/gi,"$1***$2").replace(/(?<![a-zA-Z])(password|passwd|secret|api[_-]?key|token|auth|credential)([=:])["']?[^\s"']+/gi,"$1$2***").replace(/\b(ghu_|ghs_|ghp_|github_pat_)[A-Za-z0-9_]+/g,"***").replace(/(?<=Authorization:\s*Bearer\s+)[A-Za-z0-9._~+/=-]+/gi,"***").replace(/(?<=AWS_SECRET_ACCESS_KEY=|SecretAccessKey[=:]\s*|"secretAccessKey":\s*")[A-Za-z0-9/+=]{40,}/g,"***").replace(new RegExp(o.source,"g"),"fjall_ak_***")}function A(e){const s=[];let t="",r=!1,n=!1,E=!1,a=!1;for(const _ of e){if(E){t+=_,E=!1;continue}if(_==="\\"&&!r){E=!0;continue}if(_==="'"&&!n){r=!r,a=!0;continue}if(_==='"'&&!r){n=!n,a=!0;continue}if(_===" "&&!r&&!n){(t||a)&&(s.push(t),t="",a=!1);continue}t+=_}if(r||n)throw new Error(`Unbalanced ${r?"single":"double"} quote in command: ${e.slice(0,80)}`);if(E)throw new Error(`Trailing backslash in command: ${e.slice(0,80)}`);return(t||a)&&s.push(t),s}export{i as DANGEROUS_ENV_VARS,o as SCOPED_TOKEN_REGEX,c as filterDangerousEnvVars,l as maskSensitiveOutput,A as parseShellArgs};
1
+ const E=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 A(e){return Object.fromEntries(Object.entries(e).filter(([s])=>!E.has(s.toUpperCase())))}const c=/fjall_ak_[A-Z2-7]{16}\.[A-Z2-7]{40}/,i=/fjall_ak_[A-Z2-7]{16}\.[A-Z2-7]{40}/g;function o(e){return e.replace(/(\w+:\/\/[^:]+:)[^@]+(@)/gi,"$1***$2").replace(/(?<![a-zA-Z])(password|passwd|secret|api[_-]?key|token|auth|credential)([=:])["']?[^\s"']+/gi,"$1$2***").replace(/\b(ghu_|ghs_|ghp_|gho_|github_pat_)[A-Za-z0-9_]+/g,"$1***").replace(/(?<=Authorization:\s*Bearer\s+)[A-Za-z0-9._~+/=-]+/gi,"***").replace(/\b(AKIA|ASIA)[A-Z0-9]{12,}\b/g,"$1***").replace(/(?<=AWS_SECRET_ACCESS_KEY=|SecretAccessKey[=:]\s*|"secretAccessKey":\s*")[A-Za-z0-9/+=]{40,}/g,"***").replace(/(arn:aws[^:]*:[^:]*:[^:]*:)(\d{12})(:[^\s]*)/g,"$1***$3").replace(/(?<="(aws)?[Ss]essionToken":\s*")[^"]+/g,"***").replace(/(?<="(internal[Aa]piKey|fjallCallbackToken)":\s*")[^"]+/g,"***").replace(i,"fjall_ak_***")}function u(e){const s=[];let r="",t=!1,_=!1,l=!1,n=!1;for(const a of e){if(l){r+=a,l=!1;continue}if(a==="\\"&&!t){l=!0;continue}if(a==="'"&&!_){t=!t,n=!0;continue}if(a==='"'&&!t){_=!_,n=!0;continue}if(a===" "&&!t&&!_){(r||n)&&(s.push(r),r="",n=!1);continue}r+=a}if(t||_)throw new Error(`Unbalanced ${t?"single":"double"} quote in command: ${e.slice(0,80)}`);if(l)throw new Error(`Trailing backslash in command: ${e.slice(0,80)}`);return(r||n)&&s.push(r),s}export{E as DANGEROUS_ENV_VARS,c as SCOPED_TOKEN_REGEX,A as filterDangerousEnvVars,o as maskSensitiveOutput,u as parseShellArgs};
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Single source of truth for scoped-token scope values shared across
3
+ * webapp (`/api/tokens` schemas + `scopedAuth.ts`) and CLI (agent output
4
+ * layer's `getRequiredScopes()` lookup).
5
+ *
6
+ * Drift between the two sides previously meant a webapp role granting
7
+ * a scope the CLI did not know how to request, or vice versa. Both
8
+ * consumers now import `SCOPE_VALUES` / `TokenScope` from here.
9
+ */
10
+ export declare const SCOPE_VALUES: readonly ["read", "write", "deploy", "secrets:read", "secrets:write", "destroy", "admin", "applications:read", "applications:deploy"];
11
+ export type TokenScope = (typeof SCOPE_VALUES)[number];
@@ -0,0 +1 @@
1
+ const e=["read","write","deploy","secrets:read","secrets:write","destroy","admin","applications:read","applications:deploy"];export{e as SCOPE_VALUES};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fjall/util",
3
- "version": "0.96.0",
3
+ "version": "0.99.3",
4
4
  "description": "Common utility methods",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -18,6 +18,22 @@
18
18
  "types": "./dist/constructMap.d.ts",
19
19
  "default": "./dist/constructMap.js"
20
20
  },
21
+ "./docker": {
22
+ "types": "./dist/docker/index.d.ts",
23
+ "default": "./dist/docker/index.js"
24
+ },
25
+ "./manifest": {
26
+ "types": "./dist/manifest/index.d.ts",
27
+ "default": "./dist/manifest/index.js"
28
+ },
29
+ "./manifest/schemas": {
30
+ "types": "./dist/manifest/schemas.d.ts",
31
+ "default": "./dist/manifest/schemas.js"
32
+ },
33
+ "./manifest/io": {
34
+ "types": "./dist/manifest/io.d.ts",
35
+ "default": "./dist/manifest/io.js"
36
+ },
21
37
  "./environments": {
22
38
  "types": "./dist/environments.d.ts",
23
39
  "default": "./dist/environments.js"
@@ -26,6 +42,14 @@
26
42
  "types": "./dist/fsHelpers.d.ts",
27
43
  "default": "./dist/fsHelpers.js"
28
44
  },
45
+ "./migration": {
46
+ "types": "./dist/migration/index.d.ts",
47
+ "default": "./dist/migration/index.js"
48
+ },
49
+ "./fsScan": {
50
+ "types": "./dist/fsScan.d.ts",
51
+ "default": "./dist/fsScan.js"
52
+ },
29
53
  "./targets": {
30
54
  "types": "./dist/targets.d.ts",
31
55
  "default": "./dist/targets.js"
@@ -45,6 +69,14 @@
45
69
  "./insights/computePatternFingerprint": {
46
70
  "types": "./dist/insights/computePatternFingerprint.d.ts",
47
71
  "default": "./dist/insights/computePatternFingerprint.js"
72
+ },
73
+ "./mcpProtocol": {
74
+ "types": "./dist/mcpProtocol/index.d.ts",
75
+ "default": "./dist/mcpProtocol/index.js"
76
+ },
77
+ "./securityHelpers": {
78
+ "types": "./dist/securityHelpers.d.ts",
79
+ "default": "./dist/securityHelpers.js"
48
80
  }
49
81
  },
50
82
  "files": [
@@ -58,21 +90,23 @@
58
90
  "watch:only": "npx tsc-watch",
59
91
  "format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json}\"",
60
92
  "format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json}\"",
93
+ "lint": "eslint src/",
94
+ "lint:fix": "eslint src/ --fix",
61
95
  "test": "vitest run",
62
96
  "typecheck": "npx tsc --noEmit"
63
97
  },
64
98
  "author": "",
65
99
  "license": "SEE LICENSE IN LICENSE",
66
100
  "devDependencies": {
67
- "@types/node": "^22.13.10",
68
- "prettier": "^3.2.5",
69
- "tsc-watch": "^7.0.0",
70
- "typescript": "^5.8.2",
71
- "vitest": "^4.1.0"
101
+ "@types/node": "^25.6.0",
102
+ "prettier": "^3.8.3",
103
+ "tsc-watch": "^7.2.0",
104
+ "typescript": "^6.0.3",
105
+ "vitest": "^4.1.5"
72
106
  },
73
107
  "dependencies": {
74
- "@aws-sdk/client-organizations": "^3.997.0",
75
- "zod": "^4.3.6"
108
+ "@aws-sdk/client-organizations": "^3.1038.0",
109
+ "zod": "^4.4.3"
76
110
  },
77
111
  "overrides": {
78
112
  "@smithy/core": "2.5.5"
@@ -80,5 +114,5 @@
80
114
  "engines": {
81
115
  "node": ">=22.0.0"
82
116
  },
83
- "gitHead": "bfbd3625ab029ba77a6571630e0edb85f9d53380"
117
+ "gitHead": "e50d25185d5eab618e2a90622466296fa0cbffe8"
84
118
  }