@floegence/floe-webapp-boot 0.40.21 → 0.41.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/acquisition-lifecycle.d.ts +12 -0
- package/dist/acquisition-lifecycle.js +20 -0
- package/dist/acquisition.d.ts +111 -0
- package/dist/acquisition.js +361 -0
- package/dist/artifact-source.d.ts +18 -2
- package/dist/artifact-source.js +109 -48
- package/dist/connection.d.ts +18 -4
- package/dist/connection.js +20 -9
- package/dist/hash.d.ts +0 -2
- package/dist/hash.js +8 -25
- package/dist/index.d.ts +11 -4
- package/dist/index.js +40 -26
- package/dist/proxy-bootstrap.d.ts +39 -0
- package/dist/proxy-bootstrap.js +97 -0
- package/dist/scope.d.ts +11 -2
- package/dist/scope.js +22 -15
- package/package.json +2 -2
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ArtifactSource, ConnectionSnapshot } from '@floegence/flowersec-core';
|
|
2
|
+
import { ConnectedAcquisition } from './acquisition';
|
|
3
|
+
import { ProxyBootstrapOwner } from './proxy-bootstrap';
|
|
4
|
+
export interface AcquisitionConnectionLifecycle {
|
|
5
|
+
synchronize(snapshot: ConnectionSnapshot): void;
|
|
6
|
+
dispose(): void;
|
|
7
|
+
}
|
|
8
|
+
export type AcquisitionConnectionLifecycleOptions = Readonly<{
|
|
9
|
+
proxyBootstrap?: ProxyBootstrapOwner;
|
|
10
|
+
onConnected?: (acquisition: ConnectedAcquisition) => void;
|
|
11
|
+
}>;
|
|
12
|
+
export declare function createAcquisitionConnectionLifecycle(source: ArtifactSource, options?: AcquisitionConnectionLifecycleOptions): AcquisitionConnectionLifecycle;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { clearAcquisitionSource as s, AcquisitionError as c, synchronizeAcquisitionSourceSnapshot as u } from "./acquisition.js";
|
|
2
|
+
import { closeProxyBootstrap as l, synchronizeProxyBootstrap as a } from "./proxy-bootstrap.js";
|
|
3
|
+
function y(t, i = {}) {
|
|
4
|
+
let e = !1, r = null;
|
|
5
|
+
return Object.freeze({
|
|
6
|
+
synchronize(n) {
|
|
7
|
+
if (e) throw new c("acquisition_lifecycle_disposed");
|
|
8
|
+
const o = u(t, n);
|
|
9
|
+
if (n.state === "connected" && o === null)
|
|
10
|
+
throw new c("connected_acquisition_missing");
|
|
11
|
+
i.proxyBootstrap !== void 0 && a(i.proxyBootstrap, o), o !== null && o !== r && i.onConnected?.(o), r = o;
|
|
12
|
+
},
|
|
13
|
+
dispose() {
|
|
14
|
+
e || (e = !0, r = null, i.proxyBootstrap !== void 0 && l(i.proxyBootstrap), s(t));
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
export {
|
|
19
|
+
y as createAcquisitionConnectionLifecycle
|
|
20
|
+
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { type ArtifactLease, type ArtifactSource, type ConnectionSnapshot, type JsonValue, type Session } from '@floegence/flowersec-core';
|
|
2
|
+
import type { ConnectionControllerOptions, SessionOptions } from '@floegence/flowersec-core/browser';
|
|
3
|
+
import { type ProxyRuntimeScope } from '@floegence/flowersec-core/proxy';
|
|
4
|
+
export type CriticalScopeProjectionV1 = Readonly<{
|
|
5
|
+
scope: 'proxy.runtime';
|
|
6
|
+
scope_version: 2;
|
|
7
|
+
critical: true;
|
|
8
|
+
payload: JsonValue;
|
|
9
|
+
}>;
|
|
10
|
+
export type SpendScopeV1 = Readonly<{
|
|
11
|
+
v: 1;
|
|
12
|
+
receipt: string;
|
|
13
|
+
artifact_digest_b64u: string;
|
|
14
|
+
projection_digest_b64u: string;
|
|
15
|
+
launcher_origin: string;
|
|
16
|
+
runtime_origin: string;
|
|
17
|
+
app_origin: string;
|
|
18
|
+
consumer: 'trusted' | 'isolated';
|
|
19
|
+
target_binding: JsonValue;
|
|
20
|
+
expires_at: string;
|
|
21
|
+
}>;
|
|
22
|
+
export type SerializedAcquisitionEnvelopeV1 = Readonly<{
|
|
23
|
+
v: 1;
|
|
24
|
+
connect_artifact: string;
|
|
25
|
+
critical_scope_projection_json: string;
|
|
26
|
+
spend_scope: SpendScopeV1;
|
|
27
|
+
}>;
|
|
28
|
+
export type RuntimeBootInitPayloadV6 = Readonly<{
|
|
29
|
+
v: 6;
|
|
30
|
+
env_public_id: string;
|
|
31
|
+
floe_app: string;
|
|
32
|
+
code_space_id: string;
|
|
33
|
+
app_path: string;
|
|
34
|
+
launcher_kind: 'cs' | 'pf';
|
|
35
|
+
launcher_id: string;
|
|
36
|
+
launcher_origin: string;
|
|
37
|
+
runtime_origin: string;
|
|
38
|
+
app_origin: string;
|
|
39
|
+
acquisition: SerializedAcquisitionEnvelopeV1;
|
|
40
|
+
}>;
|
|
41
|
+
export type SpendBindingView = Readonly<{
|
|
42
|
+
artifactDigestB64u: string;
|
|
43
|
+
projectionDigestB64u: string;
|
|
44
|
+
launcherOrigin: string;
|
|
45
|
+
runtimeOrigin: string;
|
|
46
|
+
appOrigin: string;
|
|
47
|
+
consumer: 'trusted' | 'isolated';
|
|
48
|
+
targetBinding: JsonValue;
|
|
49
|
+
expiresAt: string;
|
|
50
|
+
}>;
|
|
51
|
+
export type SpendCommitRequest = SpendBindingView & Readonly<{
|
|
52
|
+
attemptId: string;
|
|
53
|
+
receipt: string;
|
|
54
|
+
}>;
|
|
55
|
+
export type CommitSpend = (request: SpendCommitRequest, signal?: AbortSignal) => Promise<void>;
|
|
56
|
+
export type ValidateSpendBinding = (binding: SpendBindingView) => string | void;
|
|
57
|
+
export declare class AcquisitionError extends Error {
|
|
58
|
+
readonly code: string;
|
|
59
|
+
constructor(code: string);
|
|
60
|
+
}
|
|
61
|
+
export declare class ConnectedAcquisition {
|
|
62
|
+
#private;
|
|
63
|
+
private constructor();
|
|
64
|
+
static create(): ConnectedAcquisition;
|
|
65
|
+
static assertAuthentic(value: ConnectedAcquisition): void;
|
|
66
|
+
}
|
|
67
|
+
export declare class IsolatedOneShotAcquisition {
|
|
68
|
+
#private;
|
|
69
|
+
private constructor();
|
|
70
|
+
static create(): IsolatedOneShotAcquisition;
|
|
71
|
+
static assertAuthentic(value: IsolatedOneShotAcquisition): void;
|
|
72
|
+
}
|
|
73
|
+
export type ConnectedAcquisitionDetails = Readonly<{
|
|
74
|
+
session: Session;
|
|
75
|
+
scope: ProxyRuntimeScope;
|
|
76
|
+
bindingIdentity: string;
|
|
77
|
+
attempt: number;
|
|
78
|
+
}>;
|
|
79
|
+
type MaterializeOptions = Readonly<{
|
|
80
|
+
commitSpend: CommitSpend;
|
|
81
|
+
validateSpendBinding: ValidateSpendBinding;
|
|
82
|
+
expectedConsumer: 'trusted' | 'isolated';
|
|
83
|
+
}>;
|
|
84
|
+
export declare function registerAcquisitionSource(source: ArtifactSource): void;
|
|
85
|
+
export declare function materializeAcquisitionForSource(source: ArtifactSource, value: unknown, options: MaterializeOptions): Promise<ArtifactLease>;
|
|
86
|
+
export declare function synchronizeAcquisitionSourceSnapshot(source: ArtifactSource, snapshot: ConnectionSnapshot): ConnectedAcquisition | null;
|
|
87
|
+
export declare function clearAcquisitionSource(source: ArtifactSource): void;
|
|
88
|
+
export declare function connectedAcquisitionDetails(acquisition: ConnectedAcquisition): ConnectedAcquisitionDetails;
|
|
89
|
+
export type IsolatedHandoffValidationContext = Readonly<{
|
|
90
|
+
envPublicId: string;
|
|
91
|
+
floeApp: string;
|
|
92
|
+
codeSpaceId: string;
|
|
93
|
+
appPath: string;
|
|
94
|
+
launcherKind: 'cs' | 'pf';
|
|
95
|
+
launcherId: string;
|
|
96
|
+
launcherOrigin: string;
|
|
97
|
+
runtimeOrigin: string;
|
|
98
|
+
appOrigin: string;
|
|
99
|
+
validateTargetBinding: (targetBinding: JsonValue) => void;
|
|
100
|
+
}>;
|
|
101
|
+
export type MaterializeIsolatedOneShotOptions = Readonly<{
|
|
102
|
+
rawHandoff: string;
|
|
103
|
+
clearSensitiveLocation: () => boolean;
|
|
104
|
+
navigateToLauncher: () => void;
|
|
105
|
+
validationContext: IsolatedHandoffValidationContext;
|
|
106
|
+
commitSpend: CommitSpend;
|
|
107
|
+
}>;
|
|
108
|
+
export declare function materializeIsolatedOneShot(options: MaterializeIsolatedOneShotOptions): Promise<IsolatedOneShotAcquisition>;
|
|
109
|
+
export declare function connectIsolatedOneShot(acquisition: IsolatedOneShotAcquisition, options?: SessionOptions): Promise<ConnectedAcquisition>;
|
|
110
|
+
export type BrowserControllerOptions = ConnectionControllerOptions;
|
|
111
|
+
export {};
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import { parseArtifact as R, createArtifactLease as $ } from "@floegence/flowersec-core";
|
|
2
|
+
import { assertProxyRuntimeScope as C } from "@floegence/flowersec-core/proxy";
|
|
3
|
+
const j = 32, F = 16384, L = "#redeven=";
|
|
4
|
+
class n extends Error {
|
|
5
|
+
constructor(i) {
|
|
6
|
+
super(`Floe acquisition failed (code=${i})`), this.code = i, this.name = "AcquisitionError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
class h {
|
|
10
|
+
#e = void 0;
|
|
11
|
+
constructor() {
|
|
12
|
+
Object.freeze(this);
|
|
13
|
+
}
|
|
14
|
+
static create() {
|
|
15
|
+
return new h();
|
|
16
|
+
}
|
|
17
|
+
static assertAuthentic(i) {
|
|
18
|
+
i.#e;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
class m {
|
|
22
|
+
#e = void 0;
|
|
23
|
+
constructor() {
|
|
24
|
+
Object.freeze(this);
|
|
25
|
+
}
|
|
26
|
+
static create() {
|
|
27
|
+
return new m();
|
|
28
|
+
}
|
|
29
|
+
static assertAuthentic(i) {
|
|
30
|
+
i.#e;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const g = /* @__PURE__ */ new WeakMap(), S = /* @__PURE__ */ new WeakMap(), z = /* @__PURE__ */ new WeakMap(), O = /* @__PURE__ */ new Set();
|
|
34
|
+
function X(e) {
|
|
35
|
+
if (g.has(e)) throw new n("duplicate_acquisition_source");
|
|
36
|
+
g.set(e, { pending: [] });
|
|
37
|
+
}
|
|
38
|
+
async function Y(e, i, t) {
|
|
39
|
+
const r = g.get(e);
|
|
40
|
+
if (r === void 0) throw new n("invalid_acquisition_source");
|
|
41
|
+
const c = await x(i, t);
|
|
42
|
+
return D(c, t.commitSpend, (o) => {
|
|
43
|
+
r.pending.push(o);
|
|
44
|
+
}, (o) => {
|
|
45
|
+
r.pending = r.pending.filter((a) => a !== o);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
function K(e, i) {
|
|
49
|
+
const t = g.get(e);
|
|
50
|
+
if (t === void 0) return null;
|
|
51
|
+
if (i.state !== "connected" || i.currentSession === void 0)
|
|
52
|
+
return (i.state === "idle" || i.state === "waiting" || i.state === "failed" || i.state === "closed") && (t.pending = [], t.connected = void 0, t.connectedSession = void 0), null;
|
|
53
|
+
if (t.connected !== void 0 && t.connectedSession === i.currentSession) return t.connected;
|
|
54
|
+
const r = t.pending.filter((a) => a.committed);
|
|
55
|
+
if (r.length !== 1 || t.pending.length !== 1)
|
|
56
|
+
throw t.pending = [], new n("connected_acquisition_mismatch");
|
|
57
|
+
const c = r[0];
|
|
58
|
+
if (c === void 0) throw new n("connected_acquisition_mismatch");
|
|
59
|
+
const o = h.create();
|
|
60
|
+
return S.set(o, Object.freeze({
|
|
61
|
+
session: i.currentSession,
|
|
62
|
+
scope: c.scope,
|
|
63
|
+
bindingIdentity: c.bindingIdentity,
|
|
64
|
+
attempt: i.attempt
|
|
65
|
+
})), t.pending = [], t.connected = o, t.connectedSession = i.currentSession, o;
|
|
66
|
+
}
|
|
67
|
+
function Q(e) {
|
|
68
|
+
const i = g.get(e);
|
|
69
|
+
i !== void 0 && (i.pending = [], i.connected = void 0, i.connectedSession = void 0);
|
|
70
|
+
}
|
|
71
|
+
function ee(e) {
|
|
72
|
+
h.assertAuthentic(e);
|
|
73
|
+
const i = S.get(e);
|
|
74
|
+
if (i === void 0) throw new n("invalid_connected_acquisition");
|
|
75
|
+
return i;
|
|
76
|
+
}
|
|
77
|
+
async function te(e) {
|
|
78
|
+
const i = e.rawHandoff, t = typeof i != "string" || Z(`${L}${i}`) > F;
|
|
79
|
+
let r = !1;
|
|
80
|
+
try {
|
|
81
|
+
r = e.clearSensitiveLocation() === !0;
|
|
82
|
+
} catch {
|
|
83
|
+
r = !1;
|
|
84
|
+
}
|
|
85
|
+
if (!r)
|
|
86
|
+
throw e.navigateToLauncher(), new n("isolated_location_clear_failed");
|
|
87
|
+
if (t) throw new n("isolated_handoff_too_large");
|
|
88
|
+
const c = y(i, "invalid_isolated_handoff"), o = await V(c);
|
|
89
|
+
if (O.has(o)) throw new n("isolated_handoff_consumed");
|
|
90
|
+
O.add(o);
|
|
91
|
+
let a;
|
|
92
|
+
try {
|
|
93
|
+
a = JSON.parse(new TextDecoder("utf-8", { fatal: !0 }).decode(c));
|
|
94
|
+
} catch {
|
|
95
|
+
throw new n("invalid_isolated_handoff");
|
|
96
|
+
}
|
|
97
|
+
const _ = P(a, e.validationContext), p = await x(_.acquisition, {
|
|
98
|
+
commitSpend: e.commitSpend,
|
|
99
|
+
expectedConsumer: "isolated",
|
|
100
|
+
validateSpendBinding: (d) => (k(_, d, e.validationContext), e.validationContext.validateTargetBinding(d.targetBinding), `${d.artifactDigestB64u}.${d.projectionDigestB64u}`)
|
|
101
|
+
});
|
|
102
|
+
if (p.scope.appBasePath !== _.app_path) throw new n("isolated_app_path_mismatch");
|
|
103
|
+
if (p.scope.mode === "controller_bridge") {
|
|
104
|
+
const d = p.scope.controllerBridge.allowedOrigins;
|
|
105
|
+
if (d.length !== 1 || d[0] !== _.app_origin)
|
|
106
|
+
throw new n("isolated_allowed_origins_mismatch");
|
|
107
|
+
}
|
|
108
|
+
let s;
|
|
109
|
+
const l = D(p, e.commitSpend, (d) => {
|
|
110
|
+
s = d;
|
|
111
|
+
}, () => {
|
|
112
|
+
});
|
|
113
|
+
if (s === void 0 || s.lease !== l) throw new n("isolated_materialization_failed");
|
|
114
|
+
const u = m.create();
|
|
115
|
+
return z.set(u, { pending: s, state: "ready" }), u;
|
|
116
|
+
}
|
|
117
|
+
async function ie(e, i = {}) {
|
|
118
|
+
m.assertAuthentic(e);
|
|
119
|
+
const t = z.get(e);
|
|
120
|
+
if (t === void 0 || t.state !== "ready") throw new n("isolated_acquisition_consumed");
|
|
121
|
+
t.state = "connecting";
|
|
122
|
+
try {
|
|
123
|
+
const { connect: r } = await import("@floegence/flowersec-core/browser"), c = await r(t.pending.lease, i);
|
|
124
|
+
if (!t.pending.committed)
|
|
125
|
+
throw await c.close().catch(() => {
|
|
126
|
+
}), new n("isolated_spend_not_committed");
|
|
127
|
+
const o = h.create();
|
|
128
|
+
return S.set(o, Object.freeze({
|
|
129
|
+
session: c,
|
|
130
|
+
scope: t.pending.scope,
|
|
131
|
+
bindingIdentity: t.pending.bindingIdentity,
|
|
132
|
+
attempt: 1
|
|
133
|
+
})), t.state = "consumed", o;
|
|
134
|
+
} catch (r) {
|
|
135
|
+
throw t.state = "consumed", r;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async function x(e, i) {
|
|
139
|
+
const t = w(e, ["v", "connect_artifact", "critical_scope_projection_json", "spend_scope"], "invalid_acquisition_envelope");
|
|
140
|
+
if (t.v !== 1 || typeof t.connect_artifact != "string" || t.connect_artifact.length === 0 || typeof t.critical_scope_projection_json != "string" || t.critical_scope_projection_json.length === 0)
|
|
141
|
+
throw new n("invalid_acquisition_envelope");
|
|
142
|
+
const r = U(t.spend_scope, i.expectedConsumer), c = new TextEncoder().encode(t.connect_artifact), o = new TextEncoder().encode(t.critical_scope_projection_json);
|
|
143
|
+
await q(c, r.artifact_digest_b64u, "artifact_digest_mismatch"), await q(o, r.projection_digest_b64u, "projection_digest_mismatch");
|
|
144
|
+
let a;
|
|
145
|
+
try {
|
|
146
|
+
a = JSON.parse(t.critical_scope_projection_json);
|
|
147
|
+
} catch {
|
|
148
|
+
throw new n("invalid_critical_scope_projection");
|
|
149
|
+
}
|
|
150
|
+
const _ = w(a, ["scope", "scope_version", "critical", "payload"], "invalid_critical_scope_projection");
|
|
151
|
+
if (_.scope !== "proxy.runtime" || _.scope_version !== 2 || _.critical !== !0)
|
|
152
|
+
throw new n("invalid_critical_scope_projection");
|
|
153
|
+
let p;
|
|
154
|
+
try {
|
|
155
|
+
p = C(_.payload);
|
|
156
|
+
} catch {
|
|
157
|
+
throw new n("invalid_proxy_runtime_scope");
|
|
158
|
+
}
|
|
159
|
+
let s;
|
|
160
|
+
try {
|
|
161
|
+
s = R(t.connect_artifact);
|
|
162
|
+
} catch {
|
|
163
|
+
throw new n("invalid_connect_artifact");
|
|
164
|
+
}
|
|
165
|
+
const l = N(r), u = i.validateSpendBinding(l);
|
|
166
|
+
if (u !== void 0 && (u.trim() === "" || u !== u.trim()))
|
|
167
|
+
throw new n("invalid_spend_binding_identity");
|
|
168
|
+
return Object.freeze({
|
|
169
|
+
artifact: s,
|
|
170
|
+
scope: p,
|
|
171
|
+
binding: l,
|
|
172
|
+
bindingIdentity: u ?? `${l.artifactDigestB64u}.${l.projectionDigestB64u}`,
|
|
173
|
+
receipt: r.receipt
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
function D(e, i, t, r) {
|
|
177
|
+
const c = e.artifact, o = e.scope, a = e.binding, _ = e.bindingIdentity;
|
|
178
|
+
let p = e.receipt, s;
|
|
179
|
+
const l = $(c, async (u) => {
|
|
180
|
+
const d = p;
|
|
181
|
+
if (p = void 0, d === void 0) throw new n("spend_binding_consumed");
|
|
182
|
+
const T = Object.freeze({
|
|
183
|
+
...a,
|
|
184
|
+
attemptId: J(),
|
|
185
|
+
receipt: d
|
|
186
|
+
});
|
|
187
|
+
try {
|
|
188
|
+
await i(T, u), s.committed = !0;
|
|
189
|
+
} catch (E) {
|
|
190
|
+
throw r(s), E;
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
return s = { lease: l, scope: o, bindingIdentity: _, committed: !1 }, t(s), l;
|
|
194
|
+
}
|
|
195
|
+
function U(e, i) {
|
|
196
|
+
const t = w(e, [
|
|
197
|
+
"v",
|
|
198
|
+
"receipt",
|
|
199
|
+
"artifact_digest_b64u",
|
|
200
|
+
"projection_digest_b64u",
|
|
201
|
+
"launcher_origin",
|
|
202
|
+
"runtime_origin",
|
|
203
|
+
"app_origin",
|
|
204
|
+
"consumer",
|
|
205
|
+
"target_binding",
|
|
206
|
+
"expires_at"
|
|
207
|
+
], "invalid_spend_scope");
|
|
208
|
+
if (t.v !== 1 || typeof t.receipt != "string" || !H(t.receipt) || typeof t.artifact_digest_b64u != "string" || !B(t.artifact_digest_b64u) || typeof t.projection_digest_b64u != "string" || !B(t.projection_digest_b64u) || t.consumer !== i || typeof t.expires_at != "string" || !M(t.expires_at) || !b(t.target_binding))
|
|
209
|
+
throw new n("invalid_spend_scope");
|
|
210
|
+
const r = f(t.launcher_origin), c = f(t.runtime_origin), o = f(t.app_origin);
|
|
211
|
+
if (Date.parse(t.expires_at) <= Date.now()) throw new n("expired_spend_scope");
|
|
212
|
+
return Object.freeze({
|
|
213
|
+
v: 1,
|
|
214
|
+
receipt: t.receipt,
|
|
215
|
+
artifact_digest_b64u: t.artifact_digest_b64u,
|
|
216
|
+
projection_digest_b64u: t.projection_digest_b64u,
|
|
217
|
+
launcher_origin: r,
|
|
218
|
+
runtime_origin: c,
|
|
219
|
+
app_origin: o,
|
|
220
|
+
consumer: i,
|
|
221
|
+
target_binding: v(t.target_binding),
|
|
222
|
+
expires_at: t.expires_at
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
function N(e) {
|
|
226
|
+
return Object.freeze({
|
|
227
|
+
artifactDigestB64u: e.artifact_digest_b64u,
|
|
228
|
+
projectionDigestB64u: e.projection_digest_b64u,
|
|
229
|
+
launcherOrigin: e.launcher_origin,
|
|
230
|
+
runtimeOrigin: e.runtime_origin,
|
|
231
|
+
appOrigin: e.app_origin,
|
|
232
|
+
consumer: e.consumer,
|
|
233
|
+
targetBinding: e.target_binding,
|
|
234
|
+
expiresAt: e.expires_at
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
function P(e, i) {
|
|
238
|
+
const t = w(e, [
|
|
239
|
+
"v",
|
|
240
|
+
"env_public_id",
|
|
241
|
+
"floe_app",
|
|
242
|
+
"code_space_id",
|
|
243
|
+
"app_path",
|
|
244
|
+
"launcher_kind",
|
|
245
|
+
"launcher_id",
|
|
246
|
+
"launcher_origin",
|
|
247
|
+
"runtime_origin",
|
|
248
|
+
"app_origin",
|
|
249
|
+
"acquisition"
|
|
250
|
+
], "invalid_isolated_handoff");
|
|
251
|
+
if (t.v !== 6 || t.env_public_id !== i.envPublicId || t.floe_app !== i.floeApp || t.code_space_id !== i.codeSpaceId || t.app_path !== i.appPath || t.launcher_kind !== i.launcherKind || t.launcher_id !== i.launcherId || t.launcher_origin !== i.launcherOrigin || t.runtime_origin !== i.runtimeOrigin || t.app_origin !== i.appOrigin)
|
|
252
|
+
throw new n("isolated_context_mismatch");
|
|
253
|
+
return f(t.launcher_origin), f(t.runtime_origin), f(t.app_origin), t;
|
|
254
|
+
}
|
|
255
|
+
function k(e, i, t) {
|
|
256
|
+
if (i.consumer !== "isolated" || i.launcherOrigin !== e.launcher_origin || i.runtimeOrigin !== e.runtime_origin || i.appOrigin !== e.app_origin || e.runtime_origin !== t.runtimeOrigin || e.app_origin !== t.appOrigin)
|
|
257
|
+
throw new n("isolated_spend_binding_mismatch");
|
|
258
|
+
}
|
|
259
|
+
function w(e, i, t) {
|
|
260
|
+
if (e === null || typeof e != "object" || Array.isArray(e)) throw new n(t);
|
|
261
|
+
const r = e, c = new Set(i), o = Object.keys(r);
|
|
262
|
+
if (o.length !== i.length || o.some((a) => !c.has(a))) throw new n(t);
|
|
263
|
+
return r;
|
|
264
|
+
}
|
|
265
|
+
function f(e) {
|
|
266
|
+
if (typeof e != "string") throw new n("invalid_spend_scope");
|
|
267
|
+
let i;
|
|
268
|
+
try {
|
|
269
|
+
i = new URL(e);
|
|
270
|
+
} catch {
|
|
271
|
+
throw new n("invalid_spend_scope");
|
|
272
|
+
}
|
|
273
|
+
if (i.protocol !== "https:" && i.protocol !== "http:" || i.origin !== e || i.username !== "" || i.password !== "")
|
|
274
|
+
throw new n("invalid_spend_scope");
|
|
275
|
+
return e;
|
|
276
|
+
}
|
|
277
|
+
function H(e) {
|
|
278
|
+
const i = /^r1\.([A-Za-z0-9_-]{1,64})\.([A-Za-z0-9_-]+)$/u.exec(e);
|
|
279
|
+
if (i === null || i[2] === void 0) return !1;
|
|
280
|
+
try {
|
|
281
|
+
return y(i[2], "invalid_spend_scope").length === j;
|
|
282
|
+
} catch {
|
|
283
|
+
return !1;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
function B(e) {
|
|
287
|
+
try {
|
|
288
|
+
return y(e, "invalid_spend_scope").length === j;
|
|
289
|
+
} catch {
|
|
290
|
+
return !1;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
function M(e) {
|
|
294
|
+
return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/u.test(e) ? Number.isFinite(Date.parse(e)) : !1;
|
|
295
|
+
}
|
|
296
|
+
function b(e, i = 0) {
|
|
297
|
+
return i > 32 ? !1 : e === null || typeof e == "string" || typeof e == "boolean" ? !0 : typeof e == "number" ? Number.isFinite(e) : Array.isArray(e) ? e.every((t) => b(t, i + 1)) : typeof e != "object" ? !1 : Object.values(e).every((t) => b(t, i + 1));
|
|
298
|
+
}
|
|
299
|
+
function v(e) {
|
|
300
|
+
if (Array.isArray(e)) {
|
|
301
|
+
for (const i of e) v(i);
|
|
302
|
+
return Object.freeze(e);
|
|
303
|
+
}
|
|
304
|
+
if (e !== null && typeof e == "object") {
|
|
305
|
+
for (const i of Object.values(e)) v(i);
|
|
306
|
+
return Object.freeze(e);
|
|
307
|
+
}
|
|
308
|
+
return e;
|
|
309
|
+
}
|
|
310
|
+
async function q(e, i, t) {
|
|
311
|
+
const r = await I(e), c = y(i, t);
|
|
312
|
+
if (r.length !== c.length) throw new n(t);
|
|
313
|
+
let o = 0;
|
|
314
|
+
for (let a = 0; a < r.length; a += 1) o |= r[a] ^ c[a];
|
|
315
|
+
if (o !== 0) throw new n(t);
|
|
316
|
+
}
|
|
317
|
+
async function V(e) {
|
|
318
|
+
return A(await I(e));
|
|
319
|
+
}
|
|
320
|
+
async function I(e) {
|
|
321
|
+
if (globalThis.crypto?.subtle === void 0) throw new n("acquisition_crypto_unavailable");
|
|
322
|
+
const i = e.buffer.slice(e.byteOffset, e.byteOffset + e.byteLength), t = await globalThis.crypto.subtle.digest("SHA-256", i);
|
|
323
|
+
return new Uint8Array(t);
|
|
324
|
+
}
|
|
325
|
+
function J() {
|
|
326
|
+
if (globalThis.crypto?.getRandomValues === void 0) throw new n("acquisition_entropy_unavailable");
|
|
327
|
+
return A(globalThis.crypto.getRandomValues(new Uint8Array(j)));
|
|
328
|
+
}
|
|
329
|
+
function y(e, i) {
|
|
330
|
+
if (!/^[A-Za-z0-9_-]*$/u.test(e) || e.length % 4 === 1) throw new n(i);
|
|
331
|
+
const t = e.replace(/-/gu, "+").replace(/_/gu, "/") + "=".repeat((4 - e.length % 4) % 4);
|
|
332
|
+
let r;
|
|
333
|
+
try {
|
|
334
|
+
r = atob(t);
|
|
335
|
+
} catch {
|
|
336
|
+
throw new n(i);
|
|
337
|
+
}
|
|
338
|
+
const c = Uint8Array.from(r, (o) => o.charCodeAt(0));
|
|
339
|
+
if (A(c) !== e) throw new n(i);
|
|
340
|
+
return c;
|
|
341
|
+
}
|
|
342
|
+
function A(e) {
|
|
343
|
+
let i = "";
|
|
344
|
+
for (const t of e) i += String.fromCharCode(t);
|
|
345
|
+
return btoa(i).replace(/\+/gu, "-").replace(/\//gu, "_").replace(/=+$/u, "");
|
|
346
|
+
}
|
|
347
|
+
function Z(e) {
|
|
348
|
+
return new TextEncoder().encode(e).length;
|
|
349
|
+
}
|
|
350
|
+
export {
|
|
351
|
+
n as AcquisitionError,
|
|
352
|
+
h as ConnectedAcquisition,
|
|
353
|
+
m as IsolatedOneShotAcquisition,
|
|
354
|
+
Q as clearAcquisitionSource,
|
|
355
|
+
ie as connectIsolatedOneShot,
|
|
356
|
+
ee as connectedAcquisitionDetails,
|
|
357
|
+
Y as materializeAcquisitionForSource,
|
|
358
|
+
te as materializeIsolatedOneShot,
|
|
359
|
+
X as registerAcquisitionSource,
|
|
360
|
+
K as synchronizeAcquisitionSourceSnapshot
|
|
361
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ArtifactSource, JsonObject, RetryDisposition } from '@floegence/flowersec-core';
|
|
2
|
+
import { type CommitSpend, type ValidateSpendBinding } from './acquisition';
|
|
2
3
|
export type ControlplaneArtifactSourceOptions = Readonly<{
|
|
3
4
|
baseUrl: string;
|
|
4
5
|
endpointId: string;
|
|
@@ -9,10 +10,25 @@ export type ControlplaneArtifactSourceOptions = Readonly<{
|
|
|
9
10
|
entryTicket?: string;
|
|
10
11
|
allowLoopbackHTTP?: boolean;
|
|
11
12
|
fetch?: typeof globalThis.fetch;
|
|
13
|
+
commitSpend: CommitSpend;
|
|
14
|
+
validateSpendBinding: ValidateSpendBinding;
|
|
15
|
+
retryableBusinessCodes?: readonly string[];
|
|
12
16
|
}>;
|
|
13
17
|
export declare class ControlplaneRequestError extends Error {
|
|
14
18
|
readonly status: number;
|
|
15
19
|
readonly code: string;
|
|
16
|
-
constructor(status: number, code: string
|
|
20
|
+
constructor(status: number, code: string);
|
|
17
21
|
}
|
|
22
|
+
export type ControlplaneFailureInput = Readonly<{
|
|
23
|
+
status: number;
|
|
24
|
+
code: string;
|
|
25
|
+
retryAfter?: string | null;
|
|
26
|
+
retryableBusinessCodes?: readonly string[];
|
|
27
|
+
nowUnixMilliseconds?: number;
|
|
28
|
+
}>;
|
|
29
|
+
export type ClassifiedControlplaneFailure = Readonly<{
|
|
30
|
+
code: string;
|
|
31
|
+
disposition: RetryDisposition;
|
|
32
|
+
}>;
|
|
33
|
+
export declare function classifyControlplaneFailure(input: ControlplaneFailureInput): ClassifiedControlplaneFailure;
|
|
18
34
|
export declare function createControlplaneArtifactSource(options: ControlplaneArtifactSourceOptions): ArtifactSource;
|
package/dist/artifact-source.js
CHANGED
|
@@ -1,75 +1,136 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
super(c), this.name = "ControlplaneRequestError", this.status = e, this.code = r;
|
|
1
|
+
import { registerAcquisitionSource as p, materializeAcquisitionForSource as b, AcquisitionError as m } from "./acquisition.js";
|
|
2
|
+
const l = /^[a-z][a-z0-9_]{0,63}$/u;
|
|
3
|
+
class d extends Error {
|
|
4
|
+
constructor(t, r) {
|
|
5
|
+
super(`Floe controlplane request failed (code=${r})`), this.status = t, this.code = r, this.name = "ControlplaneRequestError";
|
|
7
6
|
}
|
|
8
7
|
}
|
|
9
|
-
function
|
|
10
|
-
const
|
|
11
|
-
return
|
|
8
|
+
function h(e) {
|
|
9
|
+
const t = e.toLowerCase();
|
|
10
|
+
return t === "localhost" || t === "::1" || t === "127.0.0.1" || t.startsWith("127.");
|
|
12
11
|
}
|
|
13
|
-
function
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
function w(e, t) {
|
|
13
|
+
let r;
|
|
14
|
+
try {
|
|
15
|
+
r = new URL(e);
|
|
16
|
+
} catch {
|
|
17
|
+
throw new d(0, "transport_policy_denied");
|
|
18
|
+
}
|
|
19
|
+
if (r.protocol !== "https:" && !(t && r.protocol === "http:" && h(r.hostname)))
|
|
20
|
+
throw new d(0, "transport_policy_denied");
|
|
17
21
|
return r.pathname = r.pathname.replace(/\/+$/u, ""), r;
|
|
18
22
|
}
|
|
19
|
-
function
|
|
20
|
-
const r =
|
|
21
|
-
return new URL(`${
|
|
23
|
+
function j(e, t) {
|
|
24
|
+
const r = t === void 0 ? "/v1/connect/artifact" : "/v1/connect/artifact/entry";
|
|
25
|
+
return new URL(`${e.pathname}${r}`, e).toString();
|
|
22
26
|
}
|
|
23
|
-
function
|
|
24
|
-
if (!
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
function z(e) {
|
|
28
|
+
if (!l.test(e.code))
|
|
29
|
+
return Object.freeze({ code: "invalid_error_code", disposition: Object.freeze({ kind: "terminal" }) });
|
|
30
|
+
if (new Set(e.retryableBusinessCodes ?? []).has(e.code))
|
|
31
|
+
return Object.freeze({ code: e.code, disposition: Object.freeze({ kind: "retryable" }) });
|
|
32
|
+
if (e.status === 429) {
|
|
33
|
+
const r = S(e.retryAfter, e.nowUnixMilliseconds ?? Date.now());
|
|
34
|
+
return Object.freeze({
|
|
35
|
+
code: e.code,
|
|
36
|
+
disposition: Object.freeze(r === void 0 ? { kind: "retryable" } : { kind: "retry_after", notBeforeUnixMilliseconds: r })
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return e.status === 408 || e.status === 425 || e.status >= 500 ? Object.freeze({ code: e.code, disposition: Object.freeze({ kind: "retryable" }) }) : Object.freeze({ code: e.code, disposition: Object.freeze({ kind: "terminal" }) });
|
|
27
40
|
}
|
|
28
|
-
function
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
41
|
+
function _(e) {
|
|
42
|
+
if (typeof e.commitSpend != "function") throw new TypeError("commitSpend is required");
|
|
43
|
+
if (typeof e.validateSpendBinding != "function") throw new TypeError("validateSpendBinding is required");
|
|
44
|
+
const t = e.fetch ?? globalThis.fetch;
|
|
45
|
+
if (typeof t != "function") throw new TypeError("fetch is required");
|
|
46
|
+
const r = w(e.baseUrl, e.allowLoopbackHTTP === !0), n = j(r, e.entryTicket), c = {
|
|
47
|
+
acquire: async ({ signal: i }) => {
|
|
32
48
|
try {
|
|
33
49
|
const o = {
|
|
34
|
-
endpoint_id:
|
|
35
|
-
...
|
|
36
|
-
...
|
|
37
|
-
},
|
|
50
|
+
endpoint_id: e.endpointId,
|
|
51
|
+
...e.payload === void 0 ? {} : { payload: e.payload },
|
|
52
|
+
...e.entryTicket === void 0 && e.correlation !== void 0 ? { correlation: { trace_id: e.correlation.traceId } } : {}
|
|
53
|
+
}, a = await t(n, {
|
|
38
54
|
method: "POST",
|
|
39
55
|
headers: {
|
|
40
56
|
accept: "application/json",
|
|
41
57
|
"content-type": "application/json",
|
|
42
|
-
...
|
|
58
|
+
...e.entryTicket === void 0 ? {} : { authorization: `Bearer ${e.entryTicket}` }
|
|
43
59
|
},
|
|
44
60
|
body: JSON.stringify(o),
|
|
45
61
|
credentials: "omit",
|
|
46
62
|
redirect: "error",
|
|
47
|
-
signal:
|
|
63
|
+
signal: i
|
|
48
64
|
});
|
|
49
|
-
if (!
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
65
|
+
if (!a.ok) {
|
|
66
|
+
const y = await O(a);
|
|
67
|
+
return { kind: "failure", ...z({
|
|
68
|
+
status: a.status,
|
|
69
|
+
code: y,
|
|
70
|
+
retryAfter: a.headers.get("retry-after"),
|
|
71
|
+
retryableBusinessCodes: e.retryableBusinessCodes
|
|
72
|
+
}) };
|
|
73
|
+
}
|
|
74
|
+
let f;
|
|
75
|
+
try {
|
|
76
|
+
f = await a.json();
|
|
77
|
+
} catch {
|
|
78
|
+
return s("invalid_controlplane_response");
|
|
56
79
|
}
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
};
|
|
80
|
+
const u = await b(c, f, {
|
|
81
|
+
commitSpend: e.commitSpend,
|
|
82
|
+
validateSpendBinding: e.validateSpendBinding,
|
|
83
|
+
expectedConsumer: "trusted"
|
|
84
|
+
});
|
|
85
|
+
return Object.freeze({ kind: "lease", lease: u });
|
|
63
86
|
} catch (o) {
|
|
87
|
+
if (i.aborted) throw i.reason ?? new DOMException("Aborted", "AbortError");
|
|
64
88
|
if (o instanceof DOMException && o.name === "AbortError") throw o;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
89
|
+
return o instanceof m || o instanceof d ? s(o.code) : Object.freeze({
|
|
90
|
+
kind: "failure",
|
|
91
|
+
code: "network_error",
|
|
92
|
+
disposition: Object.freeze({ kind: "retryable" })
|
|
93
|
+
});
|
|
68
94
|
}
|
|
69
95
|
}
|
|
70
96
|
};
|
|
97
|
+
return p(c), c;
|
|
98
|
+
}
|
|
99
|
+
async function O(e) {
|
|
100
|
+
try {
|
|
101
|
+
const t = await e.json();
|
|
102
|
+
if (t !== null && typeof t == "object" && !Array.isArray(t)) {
|
|
103
|
+
const r = t.error;
|
|
104
|
+
if (r !== null && typeof r == "object" && !Array.isArray(r)) {
|
|
105
|
+
const n = r.code;
|
|
106
|
+
if (typeof n == "string") return n;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
} catch {
|
|
110
|
+
}
|
|
111
|
+
return "request_failed";
|
|
112
|
+
}
|
|
113
|
+
function S(e, t) {
|
|
114
|
+
if (e == null) return;
|
|
115
|
+
const r = e.trim();
|
|
116
|
+
if (/^\d+$/u.test(r)) {
|
|
117
|
+
const c = Number(r);
|
|
118
|
+
if (!Number.isSafeInteger(c)) return;
|
|
119
|
+
const i = t + c * 1e3;
|
|
120
|
+
return Number.isSafeInteger(i) && i > t ? i : void 0;
|
|
121
|
+
}
|
|
122
|
+
const n = Date.parse(r);
|
|
123
|
+
return Number.isSafeInteger(n) && n > t ? n : void 0;
|
|
124
|
+
}
|
|
125
|
+
function s(e) {
|
|
126
|
+
return Object.freeze({
|
|
127
|
+
kind: "failure",
|
|
128
|
+
code: l.test(e) ? e : "invalid_error_code",
|
|
129
|
+
disposition: Object.freeze({ kind: "terminal" })
|
|
130
|
+
});
|
|
71
131
|
}
|
|
72
132
|
export {
|
|
73
|
-
|
|
74
|
-
|
|
133
|
+
d as ControlplaneRequestError,
|
|
134
|
+
z as classifyControlplaneFailure,
|
|
135
|
+
_ as createControlplaneArtifactSource
|
|
75
136
|
};
|
package/dist/connection.d.ts
CHANGED
|
@@ -1,10 +1,24 @@
|
|
|
1
|
-
import type { ArtifactSource
|
|
1
|
+
import type { ArtifactSource } from '@floegence/flowersec-core';
|
|
2
|
+
import type { ConnectionControllerOptions } from '@floegence/flowersec-core/browser';
|
|
3
|
+
import { type AcquisitionConnectionLifecycle } from './acquisition-lifecycle';
|
|
4
|
+
import type { ConnectedAcquisition } from './acquisition';
|
|
5
|
+
import type { ProxyBootstrapOwner } from './proxy-bootstrap';
|
|
2
6
|
export type FlowersecConnectionConfig = Readonly<{
|
|
3
7
|
source: ArtifactSource;
|
|
4
8
|
controller?: ConnectionControllerOptions;
|
|
9
|
+
lifecycle: AcquisitionConnectionLifecycle;
|
|
10
|
+
}>;
|
|
11
|
+
type AcquisitionConnectionOptions = Readonly<{
|
|
12
|
+
source: ArtifactSource;
|
|
13
|
+
controller?: ConnectionControllerOptions;
|
|
14
|
+
onConnected?: (acquisition: ConnectedAcquisition) => void;
|
|
15
|
+
}>;
|
|
16
|
+
export type TunnelArtifactConnectionOptions = AcquisitionConnectionOptions;
|
|
17
|
+
export type DirectArtifactConnectionOptions = AcquisitionConnectionOptions;
|
|
18
|
+
export type ProxyRuntimeTunnelConnectionOptions = AcquisitionConnectionOptions & Readonly<{
|
|
19
|
+
proxyBootstrap: ProxyBootstrapOwner;
|
|
5
20
|
}>;
|
|
6
|
-
export type TunnelArtifactConnectionOptions = FlowersecConnectionConfig;
|
|
7
|
-
export type DirectArtifactConnectionOptions = FlowersecConnectionConfig;
|
|
8
21
|
export declare function createArtifactTunnelConnectionConfig(options: TunnelArtifactConnectionOptions): FlowersecConnectionConfig;
|
|
9
|
-
export declare function createProxyRuntimeTunnelConnectionConfig(options:
|
|
22
|
+
export declare function createProxyRuntimeTunnelConnectionConfig(options: ProxyRuntimeTunnelConnectionOptions): FlowersecConnectionConfig;
|
|
10
23
|
export declare function createArtifactDirectConnectionConfig(options: DirectArtifactConnectionOptions): FlowersecConnectionConfig;
|
|
24
|
+
export {};
|
package/dist/connection.js
CHANGED
|
@@ -1,14 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { createAcquisitionConnectionLifecycle as r } from "./acquisition-lifecycle.js";
|
|
2
|
+
function o(e) {
|
|
3
|
+
return n(e);
|
|
3
4
|
}
|
|
4
|
-
function e
|
|
5
|
-
return n;
|
|
5
|
+
function i(e) {
|
|
6
|
+
return n(e, e.proxyBootstrap);
|
|
6
7
|
}
|
|
7
|
-
function
|
|
8
|
-
return n;
|
|
8
|
+
function u(e) {
|
|
9
|
+
return n(e);
|
|
10
|
+
}
|
|
11
|
+
function n(e, c) {
|
|
12
|
+
return Object.freeze({
|
|
13
|
+
source: e.source,
|
|
14
|
+
...e.controller === void 0 ? {} : { controller: e.controller },
|
|
15
|
+
lifecycle: r(e.source, {
|
|
16
|
+
...c === void 0 ? {} : { proxyBootstrap: c },
|
|
17
|
+
...e.onConnected === void 0 ? {} : { onConnected: e.onConnected }
|
|
18
|
+
})
|
|
19
|
+
});
|
|
9
20
|
}
|
|
10
21
|
export {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
22
|
+
u as createArtifactDirectConnectionConfig,
|
|
23
|
+
o as createArtifactTunnelConnectionConfig,
|
|
24
|
+
i as createProxyRuntimeTunnelConnectionConfig
|
|
14
25
|
};
|
package/dist/hash.d.ts
CHANGED
package/dist/hash.js
CHANGED
|
@@ -1,39 +1,22 @@
|
|
|
1
|
-
function l(r) {
|
|
2
|
-
let t = String(r ?? "").replace(/-/g, "+").replace(/_/g, "/");
|
|
3
|
-
for (; t.length % 4 !== 0; ) t += "=";
|
|
4
|
-
return t;
|
|
5
|
-
}
|
|
6
1
|
function o(r) {
|
|
7
|
-
const
|
|
8
|
-
if (!
|
|
9
|
-
const
|
|
10
|
-
if (!
|
|
11
|
-
try {
|
|
12
|
-
const a = new URLSearchParams(e);
|
|
13
|
-
return String(a.get(t) ?? "").trim() || null;
|
|
14
|
-
} catch {
|
|
15
|
-
return null;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
function i(r) {
|
|
19
|
-
const t = o(r);
|
|
20
|
-
if (!t) return null;
|
|
2
|
+
const n = String(r ?? "").trim();
|
|
3
|
+
if (!n) return null;
|
|
4
|
+
const t = String(window.location.hash ?? "").trim(), a = t.startsWith("#") ? t.slice(1) : t;
|
|
5
|
+
if (!a) return null;
|
|
21
6
|
try {
|
|
22
|
-
const
|
|
23
|
-
return n
|
|
7
|
+
const e = new URLSearchParams(a);
|
|
8
|
+
return String(e.get(n) ?? "").trim() || null;
|
|
24
9
|
} catch {
|
|
25
10
|
return null;
|
|
26
11
|
}
|
|
27
12
|
}
|
|
28
|
-
function
|
|
13
|
+
function c() {
|
|
29
14
|
try {
|
|
30
15
|
history.replaceState(null, document.title, window.location.pathname + window.location.search);
|
|
31
16
|
} catch {
|
|
32
17
|
}
|
|
33
18
|
}
|
|
34
19
|
export {
|
|
35
|
-
|
|
36
|
-
s as clearLocationHash,
|
|
37
|
-
i as parseBase64UrlJsonFromHash,
|
|
20
|
+
c as clearLocationHash,
|
|
38
21
|
o as parseHashParam
|
|
39
22
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { clearLocationHash, parseHashParam, } from './hash';
|
|
2
2
|
export type { WaitForMessageOptions } from './messaging';
|
|
3
3
|
export { postMessageToOrigins, waitForMessage } from './messaging';
|
|
4
4
|
export { getSessionStorage, removeSessionStorage, setSessionStorage } from './storage';
|
|
5
5
|
export type { ArtifactSource, ArtifactSourceResult } from '@floegence/flowersec-core';
|
|
6
6
|
export { createControlplaneArtifactSource, ControlplaneRequestError } from './artifact-source';
|
|
7
|
-
export
|
|
8
|
-
export type {
|
|
7
|
+
export { classifyControlplaneFailure } from './artifact-source';
|
|
8
|
+
export type { ClassifiedControlplaneFailure, ControlplaneArtifactSourceOptions, ControlplaneFailureInput, } from './artifact-source';
|
|
9
|
+
export { AcquisitionError, clearAcquisitionSource, connectIsolatedOneShot, ConnectedAcquisition, IsolatedOneShotAcquisition, materializeIsolatedOneShot, synchronizeAcquisitionSourceSnapshot, } from './acquisition';
|
|
10
|
+
export type { CommitSpend, CriticalScopeProjectionV1, IsolatedHandoffValidationContext, MaterializeIsolatedOneShotOptions, RuntimeBootInitPayloadV6, SerializedAcquisitionEnvelopeV1, SpendBindingView, SpendCommitRequest, SpendScopeV1, ValidateSpendBinding, } from './acquisition';
|
|
11
|
+
export { createAcquisitionConnectionLifecycle } from './acquisition-lifecycle';
|
|
12
|
+
export type { AcquisitionConnectionLifecycle, AcquisitionConnectionLifecycleOptions, } from './acquisition-lifecycle';
|
|
13
|
+
export type { DirectArtifactConnectionOptions, FlowersecConnectionConfig, ProxyRuntimeTunnelConnectionOptions, TunnelArtifactConnectionOptions, } from './connection';
|
|
9
14
|
export { createArtifactDirectConnectionConfig, createArtifactTunnelConnectionConfig, createProxyRuntimeTunnelConnectionConfig, } from './connection';
|
|
10
15
|
export type { FetchServerSentEventsOptions, ServerSentEvent, ServerSentEventStreamErrorCode, } from './server-sent-events';
|
|
11
16
|
export { fetchServerSentEvents, ServerSentEventStreamError, } from './server-sent-events';
|
|
12
|
-
export
|
|
17
|
+
export { closeProxyBootstrap, createProxyBootstrapOwner, ProxyBootstrapOwner, synchronizeProxyBootstrap, } from './proxy-bootstrap';
|
|
18
|
+
export type { ControllerBridgeProxyBootstrapContext, ProxyBootstrapBinding, ProxyBootstrapOwnerOptions, ProxyBootstrapSnapshot, ServiceWorkerProxyBootstrapContext, } from './proxy-bootstrap';
|
|
19
|
+
export type { ScopeEnvelope, ScopeResolver, ScopeResolverMap, ValidatedCriticalScopeProjection, } from './scope';
|
|
13
20
|
export { createBootstrapScopeResolvers, FLOWERSEC_BOOTSTRAP_SCOPE_RESOLVERS, PROXY_RUNTIME_SCOPE_NAME, validateProxyRuntimeScopeEntry, } from './scope';
|
package/dist/index.js
CHANGED
|
@@ -1,29 +1,43 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { postMessageToOrigins as i, waitForMessage as
|
|
3
|
-
import { getSessionStorage as
|
|
4
|
-
import { ControlplaneRequestError as
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
1
|
+
import { clearLocationHash as r, parseHashParam as t } from "./hash.js";
|
|
2
|
+
import { postMessageToOrigins as i, waitForMessage as a } from "./messaging.js";
|
|
3
|
+
import { getSessionStorage as c, removeSessionStorage as S, setSessionStorage as p } from "./storage.js";
|
|
4
|
+
import { ControlplaneRequestError as f, classifyControlplaneFailure as m, createControlplaneArtifactSource as x } from "./artifact-source.js";
|
|
5
|
+
import { AcquisitionError as C, ConnectedAcquisition as E, IsolatedOneShotAcquisition as O, clearAcquisitionSource as y, connectIsolatedOneShot as A, materializeIsolatedOneShot as P, synchronizeAcquisitionSourceSnapshot as g } from "./acquisition.js";
|
|
6
|
+
import { createAcquisitionConnectionLifecycle as h } from "./acquisition-lifecycle.js";
|
|
7
|
+
import { createArtifactDirectConnectionConfig as v, createArtifactTunnelConnectionConfig as B, createProxyRuntimeTunnelConnectionConfig as T } from "./connection.js";
|
|
8
|
+
import { ServerSentEventStreamError as d, fetchServerSentEvents as I } from "./server-sent-events.js";
|
|
9
|
+
import { ProxyBootstrapOwner as M, closeProxyBootstrap as w, createProxyBootstrapOwner as z, synchronizeProxyBootstrap as F } from "./proxy-bootstrap.js";
|
|
10
|
+
import { FLOWERSEC_BOOTSTRAP_SCOPE_RESOLVERS as N, PROXY_RUNTIME_SCOPE_NAME as D, createBootstrapScopeResolvers as U, validateProxyRuntimeScopeEntry as V } from "./scope.js";
|
|
8
11
|
export {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
12
|
+
C as AcquisitionError,
|
|
13
|
+
E as ConnectedAcquisition,
|
|
14
|
+
f as ControlplaneRequestError,
|
|
15
|
+
N as FLOWERSEC_BOOTSTRAP_SCOPE_RESOLVERS,
|
|
16
|
+
O as IsolatedOneShotAcquisition,
|
|
17
|
+
D as PROXY_RUNTIME_SCOPE_NAME,
|
|
18
|
+
M as ProxyBootstrapOwner,
|
|
19
|
+
d as ServerSentEventStreamError,
|
|
20
|
+
m as classifyControlplaneFailure,
|
|
21
|
+
y as clearAcquisitionSource,
|
|
22
|
+
r as clearLocationHash,
|
|
23
|
+
w as closeProxyBootstrap,
|
|
24
|
+
A as connectIsolatedOneShot,
|
|
25
|
+
h as createAcquisitionConnectionLifecycle,
|
|
26
|
+
v as createArtifactDirectConnectionConfig,
|
|
27
|
+
B as createArtifactTunnelConnectionConfig,
|
|
28
|
+
U as createBootstrapScopeResolvers,
|
|
29
|
+
x as createControlplaneArtifactSource,
|
|
30
|
+
z as createProxyBootstrapOwner,
|
|
31
|
+
T as createProxyRuntimeTunnelConnectionConfig,
|
|
32
|
+
I as fetchServerSentEvents,
|
|
33
|
+
c as getSessionStorage,
|
|
34
|
+
P as materializeIsolatedOneShot,
|
|
35
|
+
t as parseHashParam,
|
|
24
36
|
i as postMessageToOrigins,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
37
|
+
S as removeSessionStorage,
|
|
38
|
+
p as setSessionStorage,
|
|
39
|
+
g as synchronizeAcquisitionSourceSnapshot,
|
|
40
|
+
F as synchronizeProxyBootstrap,
|
|
41
|
+
V as validateProxyRuntimeScopeEntry,
|
|
42
|
+
a as waitForMessage
|
|
29
43
|
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type ProxyRuntime } from '@floegence/flowersec-core/proxy';
|
|
2
|
+
import { ConnectedAcquisition } from './acquisition';
|
|
3
|
+
export type ProxyBootstrapBinding = Readonly<{
|
|
4
|
+
dispose(): void;
|
|
5
|
+
}>;
|
|
6
|
+
export type ServiceWorkerProxyBootstrapContext = Readonly<{
|
|
7
|
+
runtime: ProxyRuntime;
|
|
8
|
+
generation: number;
|
|
9
|
+
bindingIdentity: string;
|
|
10
|
+
scriptUrl: string;
|
|
11
|
+
serviceWorkerScope: string;
|
|
12
|
+
appBasePath?: string;
|
|
13
|
+
}>;
|
|
14
|
+
export type ControllerBridgeProxyBootstrapContext = Readonly<{
|
|
15
|
+
runtime: ProxyRuntime;
|
|
16
|
+
generation: number;
|
|
17
|
+
bindingIdentity: string;
|
|
18
|
+
allowedOrigins: readonly string[];
|
|
19
|
+
appBasePath?: string;
|
|
20
|
+
capabilityNonce: string;
|
|
21
|
+
}>;
|
|
22
|
+
export type ProxyBootstrapOwnerOptions = Readonly<{
|
|
23
|
+
serviceWorker?: (context: ServiceWorkerProxyBootstrapContext) => ProxyBootstrapBinding;
|
|
24
|
+
controllerBridge?: (context: ControllerBridgeProxyBootstrapContext) => ProxyBootstrapBinding;
|
|
25
|
+
}>;
|
|
26
|
+
export type ProxyBootstrapSnapshot = Readonly<{
|
|
27
|
+
generation: number;
|
|
28
|
+
mode: 'service_worker' | 'controller_bridge';
|
|
29
|
+
bindingIdentity: string;
|
|
30
|
+
}>;
|
|
31
|
+
export declare class ProxyBootstrapOwner {
|
|
32
|
+
#private;
|
|
33
|
+
private constructor();
|
|
34
|
+
static create(): ProxyBootstrapOwner;
|
|
35
|
+
static assertAuthentic(value: ProxyBootstrapOwner): void;
|
|
36
|
+
}
|
|
37
|
+
export declare function createProxyBootstrapOwner(options: ProxyBootstrapOwnerOptions): ProxyBootstrapOwner;
|
|
38
|
+
export declare function synchronizeProxyBootstrap(owner: ProxyBootstrapOwner, acquisition: ConnectedAcquisition | null): ProxyBootstrapSnapshot | null;
|
|
39
|
+
export declare function closeProxyBootstrap(owner: ProxyBootstrapOwner): void;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { createProxyRuntime as f } from "@floegence/flowersec-core/proxy";
|
|
2
|
+
import { connectedAcquisitionDetails as g, AcquisitionError as a } from "./acquisition.js";
|
|
3
|
+
class d {
|
|
4
|
+
#e = void 0;
|
|
5
|
+
constructor() {
|
|
6
|
+
Object.freeze(this);
|
|
7
|
+
}
|
|
8
|
+
static create() {
|
|
9
|
+
return new d();
|
|
10
|
+
}
|
|
11
|
+
static assertAuthentic(t) {
|
|
12
|
+
t.#e;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const b = /* @__PURE__ */ new WeakMap();
|
|
16
|
+
function m(e) {
|
|
17
|
+
const t = d.create();
|
|
18
|
+
return b.set(t, { options: e, generation: 0 }), t;
|
|
19
|
+
}
|
|
20
|
+
function B(e, t) {
|
|
21
|
+
const o = y(e);
|
|
22
|
+
if (t === null)
|
|
23
|
+
return l(o), null;
|
|
24
|
+
if (o.acquisition === t && o.snapshot !== void 0) return o.snapshot;
|
|
25
|
+
const r = g(t);
|
|
26
|
+
l(o);
|
|
27
|
+
const c = o.generation + 1;
|
|
28
|
+
o.generation = c;
|
|
29
|
+
const n = r.scope, p = f({
|
|
30
|
+
session: r.session,
|
|
31
|
+
...n.limits ?? {},
|
|
32
|
+
...n.appBasePath === void 0 ? {} : { pathPolicy: { allowedPathPrefixes: Object.freeze([n.appBasePath]) } }
|
|
33
|
+
});
|
|
34
|
+
try {
|
|
35
|
+
let i;
|
|
36
|
+
if (n.mode === "service_worker") {
|
|
37
|
+
const s = o.options.serviceWorker;
|
|
38
|
+
if (s === void 0) throw new a("service_worker_bootstrap_unavailable");
|
|
39
|
+
i = s(Object.freeze({
|
|
40
|
+
runtime: p,
|
|
41
|
+
generation: c,
|
|
42
|
+
bindingIdentity: r.bindingIdentity,
|
|
43
|
+
scriptUrl: n.serviceWorker.scriptUrl,
|
|
44
|
+
serviceWorkerScope: n.serviceWorker.scope,
|
|
45
|
+
...n.appBasePath === void 0 ? {} : { appBasePath: n.appBasePath }
|
|
46
|
+
}));
|
|
47
|
+
} else {
|
|
48
|
+
const s = o.options.controllerBridge;
|
|
49
|
+
if (s === void 0) throw new a("controller_bridge_bootstrap_unavailable");
|
|
50
|
+
i = s(Object.freeze({
|
|
51
|
+
runtime: p,
|
|
52
|
+
generation: c,
|
|
53
|
+
bindingIdentity: r.bindingIdentity,
|
|
54
|
+
allowedOrigins: n.controllerBridge.allowedOrigins,
|
|
55
|
+
...n.appBasePath === void 0 ? {} : { appBasePath: n.appBasePath },
|
|
56
|
+
capabilityNonce: h()
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
59
|
+
if (i === null || typeof i != "object" || typeof i.dispose != "function")
|
|
60
|
+
throw new a("invalid_proxy_bootstrap_binding");
|
|
61
|
+
const u = Object.freeze({ generation: c, mode: n.mode, bindingIdentity: r.bindingIdentity });
|
|
62
|
+
return o.acquisition = t, o.runtime = p, o.binding = i, o.snapshot = u, u;
|
|
63
|
+
} catch (i) {
|
|
64
|
+
throw p.dispose(), i;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function P(e) {
|
|
68
|
+
l(y(e));
|
|
69
|
+
}
|
|
70
|
+
function y(e) {
|
|
71
|
+
d.assertAuthentic(e);
|
|
72
|
+
const t = b.get(e);
|
|
73
|
+
if (t === void 0) throw new a("invalid_proxy_bootstrap_owner");
|
|
74
|
+
return t;
|
|
75
|
+
}
|
|
76
|
+
function l(e) {
|
|
77
|
+
const t = e.binding, o = e.runtime;
|
|
78
|
+
e.acquisition = void 0, e.binding = void 0, e.runtime = void 0, e.snapshot = void 0;
|
|
79
|
+
try {
|
|
80
|
+
t?.dispose();
|
|
81
|
+
} finally {
|
|
82
|
+
o?.dispose();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function h() {
|
|
86
|
+
if (globalThis.crypto?.getRandomValues === void 0) throw new a("acquisition_entropy_unavailable");
|
|
87
|
+
const e = globalThis.crypto.getRandomValues(new Uint8Array(32));
|
|
88
|
+
let t = "";
|
|
89
|
+
for (const o of e) t += String.fromCharCode(o);
|
|
90
|
+
return btoa(t).replace(/\+/gu, "-").replace(/\//gu, "_").replace(/=+$/u, "");
|
|
91
|
+
}
|
|
92
|
+
export {
|
|
93
|
+
d as ProxyBootstrapOwner,
|
|
94
|
+
P as closeProxyBootstrap,
|
|
95
|
+
m as createProxyBootstrapOwner,
|
|
96
|
+
B as synchronizeProxyBootstrap
|
|
97
|
+
};
|
package/dist/scope.d.ts
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
|
+
import { type ProxyRuntimeScope } from '@floegence/flowersec-core/proxy';
|
|
1
2
|
export declare const PROXY_RUNTIME_SCOPE_NAME = "proxy.runtime";
|
|
2
3
|
export type ScopeEnvelope = Readonly<{
|
|
4
|
+
scope: string;
|
|
3
5
|
scope_version: number;
|
|
6
|
+
critical: boolean;
|
|
4
7
|
payload: unknown;
|
|
5
8
|
}>;
|
|
6
|
-
export type
|
|
9
|
+
export type ValidatedCriticalScopeProjection = Readonly<{
|
|
10
|
+
scope: 'proxy.runtime';
|
|
11
|
+
scope_version: 2;
|
|
12
|
+
critical: true;
|
|
13
|
+
payload: ProxyRuntimeScope;
|
|
14
|
+
}>;
|
|
15
|
+
export type ScopeResolver = (entry: ScopeEnvelope) => ValidatedCriticalScopeProjection;
|
|
7
16
|
export type ScopeResolverMap = Readonly<Record<string, ScopeResolver>>;
|
|
8
|
-
export declare function validateProxyRuntimeScopeEntry(entry: ScopeEnvelope):
|
|
17
|
+
export declare function validateProxyRuntimeScopeEntry(entry: ScopeEnvelope): ValidatedCriticalScopeProjection;
|
|
9
18
|
export declare const FLOWERSEC_BOOTSTRAP_SCOPE_RESOLVERS: Readonly<{
|
|
10
19
|
"proxy.runtime": typeof validateProxyRuntimeScopeEntry;
|
|
11
20
|
}>;
|
package/dist/scope.js
CHANGED
|
@@ -1,22 +1,29 @@
|
|
|
1
|
-
import { PROXY_RUNTIME_SCOPE as
|
|
2
|
-
const
|
|
3
|
-
function
|
|
4
|
-
if (e.
|
|
5
|
-
throw new
|
|
6
|
-
|
|
1
|
+
import { PROXY_RUNTIME_SCOPE as o, assertProxyRuntimeScope as t } from "@floegence/flowersec-core/proxy";
|
|
2
|
+
const r = "proxy.runtime";
|
|
3
|
+
function c(e) {
|
|
4
|
+
if (e === null || typeof e != "object" || Array.isArray(e) || Object.keys(e).length !== 4 || !Object.keys(e).every((i) => ["scope", "scope_version", "critical", "payload"].includes(i)))
|
|
5
|
+
throw new TypeError(`invalid ${r} projection envelope`);
|
|
6
|
+
if (e.scope !== o.name || e.scope_version !== o.version || e.critical !== !0)
|
|
7
|
+
throw new TypeError(`unsupported ${r} critical scope projection`);
|
|
8
|
+
return Object.freeze({
|
|
9
|
+
scope: o.name,
|
|
10
|
+
scope_version: o.version,
|
|
11
|
+
critical: !0,
|
|
12
|
+
payload: t(e.payload)
|
|
13
|
+
});
|
|
7
14
|
}
|
|
8
|
-
const
|
|
9
|
-
[
|
|
15
|
+
const p = Object.freeze({
|
|
16
|
+
[r]: c
|
|
10
17
|
});
|
|
11
|
-
function
|
|
18
|
+
function n(e) {
|
|
12
19
|
return e ? Object.freeze({
|
|
13
20
|
...e,
|
|
14
|
-
[
|
|
15
|
-
}) :
|
|
21
|
+
[r]: c
|
|
22
|
+
}) : p;
|
|
16
23
|
}
|
|
17
24
|
export {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
25
|
+
p as FLOWERSEC_BOOTSTRAP_SCOPE_RESOLVERS,
|
|
26
|
+
r as PROXY_RUNTIME_SCOPE_NAME,
|
|
27
|
+
n as createBootstrapScopeResolvers,
|
|
28
|
+
c as validateProxyRuntimeScopeEntry
|
|
22
29
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@floegence/floe-webapp-boot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"clean": "rm -rf dist *.tsbuildinfo"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@floegence/flowersec-core": "2.
|
|
37
|
+
"@floegence/flowersec-core": "2.5.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"typescript": "^5.9.3",
|