@fjall/util 25.0.0 → 26.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.
- package/dist/.build-source-hash +11 -5
- package/dist/.minified +1 -1
- package/dist/naming/targetExportNames.d.ts +1 -1
- package/dist/naming/targetExportNames.js +1 -1
- package/dist/organisationBinding/apiBaseUrl.d.ts +13 -0
- package/dist/organisationBinding/apiBaseUrl.js +1 -0
- package/dist/organisationBinding/binding.d.ts +141 -0
- package/dist/organisationBinding/binding.js +1 -0
- package/dist/organisationBinding/cachedCredentialIdentity.d.ts +18 -0
- package/dist/organisationBinding/cachedCredentialIdentity.js +1 -0
- package/dist/organisationBinding/credentialFingerprint.d.ts +14 -0
- package/dist/organisationBinding/credentialFingerprint.js +1 -0
- package/dist/organisationBinding/index.d.ts +5 -0
- package/dist/organisationBinding/index.js +1 -0
- package/dist/staticSiteOutput.d.ts +49 -0
- package/dist/staticSiteOutput.js +1 -0
- package/package.json +9 -2
package/dist/.build-source-hash
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"package.json",
|
|
10
10
|
"tsconfig.json"
|
|
11
11
|
],
|
|
12
|
-
"hash": "
|
|
12
|
+
"hash": "e47727a02e0f94f917edc9280bffb0add6e4edd8c08bf131c378e2ac516adbe4",
|
|
13
13
|
"files": {
|
|
14
14
|
"src/async/concurrency.ts": "3c7ebe0222a720f9",
|
|
15
15
|
"src/async/singleton.ts": "7bcc21b0fb920561",
|
|
@@ -103,9 +103,14 @@
|
|
|
103
103
|
"src/naming/caseConversion.ts": "8b8984f2b58b8ab7",
|
|
104
104
|
"src/naming/connectedAccountName.ts": "3cd3bc7dba0a2f0b",
|
|
105
105
|
"src/naming/reservedAppNames.ts": "7f63dd0b84c1972e",
|
|
106
|
-
"src/naming/targetExportNames.ts": "
|
|
106
|
+
"src/naming/targetExportNames.ts": "da78a19d8b2d4a6f",
|
|
107
107
|
"src/onboarding/index.ts": "7999117c5a7a18d1",
|
|
108
108
|
"src/onboarding/versionCurrency.ts": "19695f38b09cfc46",
|
|
109
|
+
"src/organisationBinding/apiBaseUrl.ts": "ad71ded9b42c95c6",
|
|
110
|
+
"src/organisationBinding/binding.ts": "d542bb92999d50ef",
|
|
111
|
+
"src/organisationBinding/cachedCredentialIdentity.ts": "f370024fe414949f",
|
|
112
|
+
"src/organisationBinding/credentialFingerprint.ts": "ae7fff52b70ba09a",
|
|
113
|
+
"src/organisationBinding/index.ts": "db0c6c2b73b6ea22",
|
|
109
114
|
"src/patterns/frameworkPatterns.ts": "0f843cb043eb11fc",
|
|
110
115
|
"src/patterns/index.ts": "8c765d60d672f7f7",
|
|
111
116
|
"src/patterns/patternTypes.ts": "549b06ec2a3a696e",
|
|
@@ -123,11 +128,12 @@
|
|
|
123
128
|
"src/scaffold/gitignore.ts": "87c66797a4bd05f2",
|
|
124
129
|
"src/secrets.ts": "50258d463e4e67c3",
|
|
125
130
|
"src/securityHelpers.ts": "1e68e1330c6da971",
|
|
131
|
+
"src/staticSiteOutput.ts": "cc81ed32acc597f1",
|
|
126
132
|
"src/targets.ts": "126280f0a5762e21",
|
|
127
133
|
"../scripts/minify-dist.mjs": "6b2e4b0df8aec601",
|
|
128
|
-
"package.json": "
|
|
134
|
+
"package.json": "6787681e299f8f1a",
|
|
129
135
|
"tsconfig.json": "db759831ad95f396"
|
|
130
136
|
},
|
|
131
|
-
"outputHash": "
|
|
132
|
-
"outputFileCount":
|
|
137
|
+
"outputHash": "9b7525915d572ed98c2615ea12d627ee8b201d035afef490308cb1184d04a6d0",
|
|
138
|
+
"outputFileCount": 247
|
|
133
139
|
}
|
package/dist/.minified
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
123 files minified at 2026-08-31T04:05:15.125Z
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare function albDnsExportName(appName: string, computeName?: string): string;
|
|
2
2
|
export declare function albHostedZoneIdExportName(appName: string, computeName?: string): string;
|
|
3
|
-
export declare function cdnDomainExportName(appName: string): string;
|
|
3
|
+
export declare function cdnDomainExportName(appName: string, cdnName?: string): string;
|
|
4
4
|
export declare function bucketWebsiteEndpointExportName(appName: string, bucketName: string): string;
|
|
5
5
|
export declare function bucketWebsiteHostedZoneIdExportName(appName: string, bucketName: string): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var u=Object.defineProperty;var n=(e
|
|
1
|
+
var u=Object.defineProperty;var n=(t,e)=>u(t,"name",{value:e,configurable:!0});import{toPascalCase as c}from"./caseConversion.js";function o(t){return c(t.replace(/[^A-Za-z0-9-]/g,""))}n(o,"safeExportToken");function p(t,e){const r=o(t);return e===void 0?r:`${r}-${o(e)}-`}n(p,"albExportScope");function x(t,e){return`${p(t,e)}AlbDnsName`}n(x,"albDnsExportName");function b(t,e){return`${p(t,e)}AlbHostedZoneId`}n(b,"albHostedZoneIdExportName");function a(t,e){const r=o(t);return e===void 0?r:`${r}-${o(e)}-`}n(a,"cdnExportScope");function f(t,e){return`${a(t,e)}CdnDistributionDomainName`}n(f,"cdnDomainExportName");function i(t,e){return`${o(t)}-${o(e)}-`}n(i,"bucketExportScope");function E(t,e){return`${i(t,e)}WebsiteEndpoint`}n(E,"bucketWebsiteEndpointExportName");function $(t,e){return`${i(t,e)}WebsiteHostedZoneId`}n($,"bucketWebsiteHostedZoneIdExportName");export{x as albDnsExportName,b as albHostedZoneIdExportName,E as bucketWebsiteEndpointExportName,$ as bucketWebsiteHostedZoneIdExportName,f as cdnDomainExportName};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONE resolution of the Fjall API base URL: `FJALL_API_URL` when set to a
|
|
3
|
+
* non-empty value (CI shells and env-files legitimately export `X=""`, which
|
|
4
|
+
* must fall through), otherwise production. The CLI's `DEFAULT_BASE_URL` and
|
|
5
|
+
* the MCP server's mirror each captured this shape independently — a coupled
|
|
6
|
+
* value at two occurrences whose drift would silently point the two consumers
|
|
7
|
+
* at different control planes, and whose fingerprints
|
|
8
|
+
* (`credentialFingerprint(apiKey, baseUrl)`) would then disagree about the
|
|
9
|
+
* same credential.
|
|
10
|
+
*/
|
|
11
|
+
export declare const FJALL_PRODUCTION_API_URL = "https://fjall.io";
|
|
12
|
+
/** Resolved per call — consumers choose their own capture semantics. */
|
|
13
|
+
export declare function resolveApiBaseUrl(): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var r=Object.defineProperty;var n=(e,o)=>r(e,"name",{value:o,configurable:!0});const t="https://fjall.io";function p(){const e=process.env.FJALL_API_URL;return e!==void 0&&e!==""?e:t}n(p,"resolveApiBaseUrl");export{t as FJALL_PRODUCTION_API_URL,p as resolveApiBaseUrl};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { type OrganisationBinding } from "../config.js";
|
|
2
|
+
/**
|
|
3
|
+
* Project ↔ organisation binding: the offline-pure half.
|
|
4
|
+
*
|
|
5
|
+
* Sibling of `enforceDomainAccountPin` (the CLI's
|
|
6
|
+
* `operations/domain/domainConfigRegistration`) one level up the stack. That guard pins a domain to the AWS ACCOUNT it was
|
|
7
|
+
* created in; this one pins a project to the control-plane ORGANISATION it
|
|
8
|
+
* belongs to. Same skeleton — committed config records a home, a resolved
|
|
9
|
+
* identity is compared, an unreadable config fails closed, genuine absence
|
|
10
|
+
* fails open — because the failure it prevents is the same shape: an
|
|
11
|
+
* operation that silently lands somewhere nobody intended.
|
|
12
|
+
*
|
|
13
|
+
* Everything here is pure: it takes an already-resolved identity and reads
|
|
14
|
+
* one file. Resolving the identity (and deciding what to do about each
|
|
15
|
+
* verdict) belongs to the callers — the CLI's
|
|
16
|
+
* `commands/organisationPrerequisite.ts` and the MCP server's
|
|
17
|
+
* `organisationGate.ts` — which is why this lives in `@fjall/util` (its own
|
|
18
|
+
* subpath, not the root barrel: `Config` reads the filesystem).
|
|
19
|
+
*/
|
|
20
|
+
/** The organisation the ambient credential actually names. */
|
|
21
|
+
export interface ResolvedOrganisationIdentity {
|
|
22
|
+
readonly id: string;
|
|
23
|
+
readonly name?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Five arms, not a boolean, for the reason `authPrerequisite` is three-valued:
|
|
27
|
+
* "could not check" and "checked and it does not match" are different facts
|
|
28
|
+
* with opposite correct responses, and collapsing them yields either a gate
|
|
29
|
+
* that refuses on a flaky network or one that approves a real mismatch.
|
|
30
|
+
*
|
|
31
|
+
* `unverifiable` is not a hole. A refusal here is destructive to the
|
|
32
|
+
* operator's intent — it stops work that may be entirely correct — so it
|
|
33
|
+
* needs POSITIVE proof of mismatch, never mere absence of proof of match.
|
|
34
|
+
*/
|
|
35
|
+
export type OrganisationBindingVerdict =
|
|
36
|
+
/**
|
|
37
|
+
* Nothing to enforce. `projectFound` distinguishes "a project exists here
|
|
38
|
+
* and records no binding" — the one case worth nudging — from "there is no
|
|
39
|
+
* project here at all", where a nudge would name a project that does not
|
|
40
|
+
* exist yet. `identity` is the organisation the caller resolved for the
|
|
41
|
+
* comparison (null when it could not be, or was not needed): carried so a
|
|
42
|
+
* creation flow can stamp the binding at birth from the SAME identity the
|
|
43
|
+
* gate compared, rather than re-resolving and possibly stamping an
|
|
44
|
+
* organisation the gate never saw.
|
|
45
|
+
*/
|
|
46
|
+
{
|
|
47
|
+
readonly kind: "unbound";
|
|
48
|
+
readonly projectFound: boolean;
|
|
49
|
+
readonly identity: ResolvedOrganisationIdentity | null;
|
|
50
|
+
} | {
|
|
51
|
+
readonly kind: "matched";
|
|
52
|
+
readonly bound: OrganisationBinding;
|
|
53
|
+
} | {
|
|
54
|
+
readonly kind: "mismatched";
|
|
55
|
+
readonly bound: OrganisationBinding;
|
|
56
|
+
readonly actual: ResolvedOrganisationIdentity;
|
|
57
|
+
}
|
|
58
|
+
/** Bound, but the credential's organisation could not be resolved. */
|
|
59
|
+
| {
|
|
60
|
+
readonly kind: "unverifiable";
|
|
61
|
+
readonly bound: OrganisationBinding;
|
|
62
|
+
readonly reason: string;
|
|
63
|
+
}
|
|
64
|
+
/** A config file exists but its contents are not readable. */
|
|
65
|
+
| {
|
|
66
|
+
readonly kind: "unreadable";
|
|
67
|
+
readonly reason: string;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* What the project on disk says, before any identity is involved.
|
|
71
|
+
*
|
|
72
|
+
* Split from the comparison so the gate can decide whether resolving the
|
|
73
|
+
* credential's organisation is worth doing at all: outside a project there is
|
|
74
|
+
* nothing to compare and nothing to nudge about, so no identity is needed —
|
|
75
|
+
* and an unreadable config is refused before a network call is spent on it.
|
|
76
|
+
*/
|
|
77
|
+
export type ProjectBindingRead =
|
|
78
|
+
/** No fjall-config.json here or in any parent. */
|
|
79
|
+
{
|
|
80
|
+
readonly kind: "no_project";
|
|
81
|
+
}
|
|
82
|
+
/** A project exists and records no organisation. */
|
|
83
|
+
| {
|
|
84
|
+
readonly kind: "unbound";
|
|
85
|
+
} | {
|
|
86
|
+
readonly kind: "bound";
|
|
87
|
+
readonly bound: OrganisationBinding;
|
|
88
|
+
}
|
|
89
|
+
/** A config file exists but its contents are not readable. */
|
|
90
|
+
| {
|
|
91
|
+
readonly kind: "unreadable";
|
|
92
|
+
readonly reason: string;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Read this project's recorded organisation. `startDir` overrides the
|
|
96
|
+
* config-discovery start directory (tests).
|
|
97
|
+
*/
|
|
98
|
+
export declare function readProjectBinding(startDir?: string): ProjectBindingRead;
|
|
99
|
+
/**
|
|
100
|
+
* Turn what the project records and what the credential resolved to into a
|
|
101
|
+
* verdict. `resolved === null` means the identity could not be established at
|
|
102
|
+
* all — the caller says why via `unresolvedReason`.
|
|
103
|
+
*/
|
|
104
|
+
export declare function resolveBindingVerdict(read: ProjectBindingRead, resolved: ResolvedOrganisationIdentity | null, unresolvedReason?: string): OrganisationBindingVerdict;
|
|
105
|
+
/**
|
|
106
|
+
* Compare this project's recorded organisation against the one the ambient
|
|
107
|
+
* credential names — the read and the comparison in one call, for callers
|
|
108
|
+
* that already hold the identity.
|
|
109
|
+
*/
|
|
110
|
+
export declare function checkProjectOrganisation(resolved: ResolvedOrganisationIdentity | null, options?: {
|
|
111
|
+
readonly startDir?: string;
|
|
112
|
+
readonly unresolvedReason?: string;
|
|
113
|
+
}): OrganisationBindingVerdict;
|
|
114
|
+
/** How the bound organisation reads in a message: "Acme (org_abc)" or "org_abc". */
|
|
115
|
+
export declare function describeOrganisation(organisation: OrganisationBinding | ResolvedOrganisationIdentity): string;
|
|
116
|
+
/**
|
|
117
|
+
* The refusal.
|
|
118
|
+
*
|
|
119
|
+
* ONE cure, deliberately. The domain pin's inherited copy offers a second
|
|
120
|
+
* ("update or remove the keys in fjall-config.json … a successful deploy
|
|
121
|
+
* re-records them"), which reads as a bypass manual and — worse — teaches
|
|
122
|
+
* exactly the ambient re-record that put a wrong organisation in the file in
|
|
123
|
+
* the first place. Here the only documented move is `fjall org bind --force`,
|
|
124
|
+
* which requires the operator to NAME the organisation they mean.
|
|
125
|
+
*/
|
|
126
|
+
export declare function buildWrongOrganisationMessage(action: string, bound: OrganisationBinding, actual: ResolvedOrganisationIdentity): string;
|
|
127
|
+
/** The refusal when a config file exists but cannot be trusted to be empty. */
|
|
128
|
+
export declare function buildUnreadableConfigMessage(action: string, reason: string): string;
|
|
129
|
+
/**
|
|
130
|
+
* The notice printed when a bound project's organisation could not be
|
|
131
|
+
* resolved. Availability-first, matching `requireAuthenticated`'s posture on
|
|
132
|
+
* the same failure class: the operator is told exactly what was not checked.
|
|
133
|
+
*/
|
|
134
|
+
export declare function buildUnverifiableNotice(bound: OrganisationBinding, reason: string): string;
|
|
135
|
+
/**
|
|
136
|
+
* The one-line nudge shown on a gated surface in an UNBOUND project whose
|
|
137
|
+
* organisation did resolve. This is the whole adoption path: existing
|
|
138
|
+
* projects are unbound, and nothing else would ever tell their owners the
|
|
139
|
+
* guard exists.
|
|
140
|
+
*/
|
|
141
|
+
export declare function buildUnboundNotice(actual: ResolvedOrganisationIdentity): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var a=Object.defineProperty;var t=(n,e)=>a(n,"name",{value:e,configurable:!0});import{Config as d}from"../config.js";import{maskSensitiveOutput as u}from"../securityHelpers.js";import{getErrorMessage as s}from"../errorUtils.js";function c(n){let e;try{e=d.loadConfig(n)}catch(r){return{kind:"unreadable",reason:u(s(r))}}if(e.isContentUnavailable())return{kind:"unreadable",reason:"fjall-config.json exists but its contents could not be loaded (the preceding warning names the problem)"};const o=e.getOrganisation();return o!==void 0?{kind:"bound",bound:o}:e.getConfigPath()===null?{kind:"no_project"}:{kind:"unbound"}}t(c,"readProjectBinding");function l(n,e,o){switch(n.kind){case"unreadable":return n;case"no_project":return{kind:"unbound",projectFound:!1,identity:e};case"unbound":return{kind:"unbound",projectFound:!0,identity:e};case"bound":return e===null?{kind:"unverifiable",bound:n.bound,reason:o??"the organisation could not be resolved"}:n.bound.id===e.id?{kind:"matched",bound:n.bound}:{kind:"mismatched",bound:n.bound,actual:e}}}t(l,"resolveBindingVerdict");function p(n,e={}){return l(c(e.startDir),n,e.unresolvedReason)}t(p,"checkProjectOrganisation");function i(n){return n.name!==void 0&&n.name!==""?`${n.name} (${n.id})`:n.id}t(i,"describeOrganisation");function m(n,e,o){return`Refusing to ${n}: this project's fjall-config.json records organisation ${i(e)} as its home, but the credential in use belongs to ${i(o)}. Nothing was created, synthesised or deployed. Cure: authenticate as ${i(e)} ('fjall login', or set FJALL_API_KEY to a key for that organisation), or \u2014 if this project really is moving \u2014 run 'fjall org bind --org ${o.id} --force' to re-bind it.`}t(m,"buildWrongOrganisationMessage");function j(n,e){return`Refusing to ${n}: ${e}, so this project's organisation binding cannot be checked. Nothing was created, synthesised or deployed. Cure: fix fjall-config.json (permissions or contents), then re-run.`}t(j,"buildUnreadableConfigMessage");function k(n,e){return`Could not confirm which organisation your credential belongs to (${e}). Proceeding \u2014 this project is bound to ${i(n)}, and a mismatch would not have been caught.`}t(k,"buildUnverifiableNotice");function y(n){return`This project is not bound to an organisation. Run 'fjall org bind' to pin it to ${i(n)} \u2014 afterwards, any command run here with another organisation's credential is refused instead of silently landing there.`}t(y,"buildUnboundNotice");export{y as buildUnboundNotice,j as buildUnreadableConfigMessage,k as buildUnverifiableNotice,m as buildWrongOrganisationMessage,p as checkProjectOrganisation,i as describeOrganisation,c as readProjectBinding,l as resolveBindingVerdict};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const ORG_CONFIG_CACHE_FILENAME = "org-config-cache.json";
|
|
2
|
+
/** Resolved per call, never captured at module load (`FJALL_CONFIG_DIR`). */
|
|
3
|
+
export declare function orgConfigCachePath(): string;
|
|
4
|
+
/** The identity fields every cache record carries (`GET /api/identity`). */
|
|
5
|
+
export interface CachedCredentialIdentity {
|
|
6
|
+
readonly organisationId: string;
|
|
7
|
+
readonly organisationName: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function isCachedCredentialIdentity(value: unknown): value is CachedCredentialIdentity;
|
|
10
|
+
/**
|
|
11
|
+
* The cached identity, whatever its age — but ONLY when the credential in
|
|
12
|
+
* hand is the one that wrote the record. A fingerprint match is PROOF that
|
|
13
|
+
* the cached identity is this credential's identity, which is what makes
|
|
14
|
+
* this a sound zero-network oracle for an organisation gate rather than a
|
|
15
|
+
* display-only hint. A record with no fingerprint (written before the field
|
|
16
|
+
* existed) or another credential's fingerprint is a miss by construction.
|
|
17
|
+
*/
|
|
18
|
+
export declare function readCachedCredentialIdentity(credentialFingerprint: string): CachedCredentialIdentity | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var a=Object.defineProperty;var e=(t,r)=>a(t,"name",{value:r,configurable:!0});import*as i from"node:fs";import{fjallStatePath as c}from"../fjallHome.js";import{logger as s}from"../logger.js";import{maskSensitiveOutput as f}from"../securityHelpers.js";import{getErrorMessage as d}from"../errorUtils.js";const u="org-config-cache.json";function g(){return c(u)}e(g,"orgConfigCachePath");function o(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}e(o,"isObject");function l(t){return o(t)&&typeof t.organisationId=="string"&&t.organisationId!==""&&typeof t.organisationName=="string"}e(l,"isCachedCredentialIdentity");function I(t){try{const r=g();if(!i.existsSync(r))return null;const n=JSON.parse(i.readFileSync(r,"utf-8"));return!o(n)||n.credentialFingerprint!==t?null:l(n.identity)?n.identity:null}catch(r){return s.debug("cachedCredentialIdentity","Cache read failed, treating as a miss",{error:f(d(r))}),null}}e(I,"readCachedCredentialIdentity");export{u as ORG_CONFIG_CACHE_FILENAME,l as isCachedCredentialIdentity,g as orgConfigCachePath,I as readCachedCredentialIdentity};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A stable, non-reversible identity for ONE (apiKey, baseUrl) pair.
|
|
3
|
+
*
|
|
4
|
+
* Two consumers, one derivation. The entitlements cache uses it as its key;
|
|
5
|
+
* the org-config cache stores it so a later read can prove the record was
|
|
6
|
+
* written by the credential now in hand. Both need exactly the same answer to
|
|
7
|
+
* "is this the same credential?", and a second derivation of that answer is
|
|
8
|
+
* the drift this module exists to prevent — the two caches sit in the same
|
|
9
|
+
* state directory and would silently disagree at the edge.
|
|
10
|
+
*
|
|
11
|
+
* A hash, never the key itself: the org-config cache is a plaintext file on
|
|
12
|
+
* disk, and the fingerprint is written into it.
|
|
13
|
+
*/
|
|
14
|
+
export declare function credentialFingerprint(apiKey: string, baseUrl: string): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var i=Object.defineProperty;var t=(e,r)=>i(e,"name",{value:r,configurable:!0});import{createHash as n}from"node:crypto";function p(e,r){return n("sha256").update(`${e}${r}`).digest("hex")}t(p,"credentialFingerprint");export{p as credentialFingerprint};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { readProjectBinding, resolveBindingVerdict, checkProjectOrganisation, describeOrganisation, buildWrongOrganisationMessage, buildUnreadableConfigMessage, buildUnverifiableNotice, buildUnboundNotice, type ResolvedOrganisationIdentity, type OrganisationBindingVerdict, type ProjectBindingRead } from "./binding.js";
|
|
2
|
+
export type { OrganisationBinding } from "../config.js";
|
|
3
|
+
export { credentialFingerprint } from "./credentialFingerprint.js";
|
|
4
|
+
export { ORG_CONFIG_CACHE_FILENAME, orgConfigCachePath, isCachedCredentialIdentity, readCachedCredentialIdentity, type CachedCredentialIdentity } from "./cachedCredentialIdentity.js";
|
|
5
|
+
export { FJALL_PRODUCTION_API_URL, resolveApiBaseUrl } from "./apiBaseUrl.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readProjectBinding as r,resolveBindingVerdict as n,checkProjectOrganisation as o,describeOrganisation as a,buildWrongOrganisationMessage as t,buildUnreadableConfigMessage as d,buildUnverifiableNotice as c,buildUnboundNotice as g}from"./binding.js";import{credentialFingerprint as s}from"./credentialFingerprint.js";import{ORG_CONFIG_CACHE_FILENAME as b,orgConfigCachePath as f,isCachedCredentialIdentity as O,readCachedCredentialIdentity as p}from"./cachedCredentialIdentity.js";import{FJALL_PRODUCTION_API_URL as U,resolveApiBaseUrl as _}from"./apiBaseUrl.js";export{U as FJALL_PRODUCTION_API_URL,b as ORG_CONFIG_CACHE_FILENAME,g as buildUnboundNotice,d as buildUnreadableConfigMessage,c as buildUnverifiableNotice,t as buildWrongOrganisationMessage,o as checkProjectOrganisation,s as credentialFingerprint,a as describeOrganisation,O as isCachedCredentialIdentity,f as orgConfigCachePath,p as readCachedCredentialIdentity,r as readProjectBinding,_ as resolveApiBaseUrl,n as resolveBindingVerdict};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static-site build-output vocabulary and the emptiness probe over it.
|
|
3
|
+
*
|
|
4
|
+
* Node-only, and deliberately NOT under `patterns/`: that directory is the
|
|
5
|
+
* browser-safe subpath (`@fjall/util/patterns`), and neither it nor the root
|
|
6
|
+
* barrel may reach a module carrying a static `node:*` import — a named
|
|
7
|
+
* builtin import binds to the bundler's throwing stub at evaluation time and
|
|
8
|
+
* kills webapp hydration on every page, while prod tree-shaking hides it from
|
|
9
|
+
* CI. Reachable only via the `@fjall/util/staticSiteOutput` subpath, whose
|
|
10
|
+
* two consumers (the `StaticSite` construct, `staticSiteBuilder`) are both
|
|
11
|
+
* Node-only.
|
|
12
|
+
*
|
|
13
|
+
* `barrelBrowserSafety.test.ts` walks the ROOT barrel's import graph for
|
|
14
|
+
* exactly this edge, so re-exporting this module from `index.ts` fails the
|
|
15
|
+
* util suite rather than the webapp dev server. It does NOT walk
|
|
16
|
+
* `patterns/index.ts` — the root barrel imports that directory's leaf modules
|
|
17
|
+
* directly and never the barrel itself — which is the second reason this
|
|
18
|
+
* module lives at the src root: under `patterns/` its `node:fs` import would
|
|
19
|
+
* be guarded by nothing.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Entries that do not make a static-site build output non-empty.
|
|
23
|
+
*
|
|
24
|
+
* The emptiness gate exists to stop a pruning upload wiping a live site, and
|
|
25
|
+
* counting raw directory entries defeats it for the two cases that actually
|
|
26
|
+
* occur: an OS file browser leaves `.DS_Store`/`Thumbs.db` in an empty output
|
|
27
|
+
* directory, and a repo that tracks `dist/` commits a `.gitkeep` to keep it.
|
|
28
|
+
* Either one makes a failed build look like a successful one, and the upload
|
|
29
|
+
* then deletes every object already in the bucket.
|
|
30
|
+
*
|
|
31
|
+
* Deliberately narrow: `.well-known/` and other dot-entries are real site
|
|
32
|
+
* content, so this is a fixed noise set, not a "skip dotfiles" rule.
|
|
33
|
+
*/
|
|
34
|
+
export declare const BUILD_OUTPUT_NOISE: ReadonlySet<string>;
|
|
35
|
+
/**
|
|
36
|
+
* Whether a build output directory holds anything worth uploading.
|
|
37
|
+
*
|
|
38
|
+
* Shared by the two gates that must agree about it — `staticSiteBuilder`
|
|
39
|
+
* during `fjall deploy` and the `StaticSite` construct at synth. They run in
|
|
40
|
+
* different packages and at different times, so a private copy in either one
|
|
41
|
+
* drifts silently: the deploy-time gate would refuse what the construct
|
|
42
|
+
* permits, or permit what the construct refuses, and only one of them prints
|
|
43
|
+
* the remedy the user is told to follow.
|
|
44
|
+
*
|
|
45
|
+
* Throws whatever `readdirSync` throws — a missing or unreadable directory is
|
|
46
|
+
* a different failure with a different message, and each caller checks for it
|
|
47
|
+
* first.
|
|
48
|
+
*/
|
|
49
|
+
export declare function hasDeployableContent(directory: string): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var r=Object.defineProperty;var o=(e,t)=>r(e,"name",{value:t,configurable:!0});import{readdirSync as n}from"node:fs";const p=new Set([".DS_Store","Thumbs.db",".gitkeep",".keep"]);function a(e){return n(e).some(t=>!p.has(t))}o(a,"hasDeployableContent");export{p as BUILD_OUTPUT_NOISE,a as hasDeployableContent};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fjall/util",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "26.0.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/fjall-tech/fjall.git",
|
|
@@ -59,6 +59,10 @@
|
|
|
59
59
|
"types": "./dist/manifest/io.d.ts",
|
|
60
60
|
"default": "./dist/manifest/io.js"
|
|
61
61
|
},
|
|
62
|
+
"./organisationBinding": {
|
|
63
|
+
"types": "./dist/organisationBinding/index.d.ts",
|
|
64
|
+
"default": "./dist/organisationBinding/index.js"
|
|
65
|
+
},
|
|
62
66
|
"./environments": {
|
|
63
67
|
"types": "./dist/environments.d.ts",
|
|
64
68
|
"default": "./dist/environments.js"
|
|
@@ -67,6 +71,10 @@
|
|
|
67
71
|
"types": "./dist/fsHelpers.d.ts",
|
|
68
72
|
"default": "./dist/fsHelpers.js"
|
|
69
73
|
},
|
|
74
|
+
"./staticSiteOutput": {
|
|
75
|
+
"types": "./dist/staticSiteOutput.d.ts",
|
|
76
|
+
"default": "./dist/staticSiteOutput.js"
|
|
77
|
+
},
|
|
70
78
|
"./httpKeepAlive": {
|
|
71
79
|
"types": "./dist/httpKeepAlive.d.ts",
|
|
72
80
|
"default": "./dist/httpKeepAlive.js"
|
|
@@ -158,7 +166,6 @@
|
|
|
158
166
|
"vitest": "^4.1.10"
|
|
159
167
|
},
|
|
160
168
|
"dependencies": {
|
|
161
|
-
"@aws-sdk/client-organizations": "^3.1098.0",
|
|
162
169
|
"zod": "^4.4.3"
|
|
163
170
|
},
|
|
164
171
|
"overrides": {
|