@auersperg/flare 0.3.8 → 0.3.10

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 (34) hide show
  1. package/README.md +3 -1
  2. package/dist/cli/{chunk-5s6zv1h9.js → chunk-jrptpjmm.js} +1 -1
  3. package/dist/cli/{chunk-y6e2h40n.js → chunk-m0698kap.js} +37 -12
  4. package/dist/cli/{chunk-5yg1zj5n.js → chunk-znktbexz.js} +6 -6
  5. package/dist/cli/{chunk-vx7kmczm.js → chunk-zt96ghm7.js} +12 -6
  6. package/dist/cli/flare.js +5 -5
  7. package/dist/platform/admin.js +31 -23
  8. package/dist/platform/dashboard/_astro/{auth-view.EASEF9BN.js → auth-view.BhUFPmNv.js} +1 -1
  9. package/dist/platform/dashboard/_astro/{cli-approval.DwZVHdyo.js → cli-approval.emPTVRqn.js} +1 -1
  10. package/dist/platform/dashboard/_astro/client.4z54xJ_z.js +1 -0
  11. package/dist/platform/dashboard/_astro/{common.TAClb6G9.js → common.9pujiaSZ.js} +1 -1
  12. package/dist/platform/dashboard/_astro/{data-view.BaHEU4Xa.js → data-view.C-VU8Oxm.js} +1 -1
  13. package/dist/platform/dashboard/_astro/{functions-view.BGOYuDwu.js → functions-view.3yLrtS0h.js} +1 -1
  14. package/dist/platform/dashboard/_astro/{index.astro_astro_type_script_index_0_lang.Blg5hU9g.js → index.astro_astro_type_script_index_0_lang.Dsfj1oeZ.js} +2 -2
  15. package/dist/platform/dashboard/_astro/{input-group.DUp-h0xx.js → input-group.BZ1TZT7L.js} +1 -1
  16. package/dist/platform/dashboard/_astro/{sdk-view.2ErPn4cO.js → sdk-view.BAiQKNm8.js} +1 -1
  17. package/dist/platform/dashboard/_astro/{services-view.iZujitxl.js → services-view.BSrqpyr5.js} +1 -1
  18. package/dist/platform/dashboard/_astro/{settings-view.CqK-mrlB.js → settings-view.C_eYjlIE.js} +1 -1
  19. package/dist/platform/dashboard/bootstrap/facet.js +64 -46
  20. package/dist/platform/dashboard/bootstrap/openapi.json +1 -1
  21. package/dist/platform/dashboard/bootstrap/release.json +2 -2
  22. package/dist/platform/dashboard/index.html +1 -1
  23. package/dist/platform/manifest.json +36 -36
  24. package/dist/platform/runtime.js +99 -66
  25. package/dist/sdk/backup.js +4 -4
  26. package/dist/sdk/client.js +22 -41
  27. package/dist/sdk/cloudflare.js +10 -11
  28. package/dist/sdk/database.js +45 -27
  29. package/dist/sdk/http.d.ts +2 -0
  30. package/dist/sdk/http.js +27 -0
  31. package/dist/sdk/schema.d.ts +2 -2
  32. package/dist/sdk/schema.js +7 -6
  33. package/package.json +2 -2
  34. package/dist/platform/dashboard/_astro/client.BUUZgEFj.js +0 -1
package/README.md CHANGED
@@ -127,4 +127,6 @@ Password reset/block/revoke disconnects affected live users. Live cache is clear
127
127
 
128
128
  SDK and CLI are MIT-licensed; see LICENSE. npm name: `@auersperg/flare`.
129
129
  The `flare` binary remains unchanged. Preview releases are published under the `next` tag. Core/dashboard are separate non-published
130
- components and are not included in this package.
130
+ workspaces, not separately published npm packages. Their compiled, checksummed platform payload is included under `dist/platform` for CLI installation/update; browser SDK imports do not load it.
131
+
132
+ For new applications and coding agents, see the [application guide](https://github.com/auersperg/flare/blob/main/docs/build-an-app.md) and [app AGENTS.md template](https://github.com/auersperg/flare/blob/main/docs/templates/AGENTS.md). Match their verified SDK version to your pinned dependency.
@@ -102,7 +102,7 @@ ${url}`);
102
102
  await writeFile(join(directory, key + ".json"), JSON.stringify({ token, expiresAt: result.expiresAt }) + `
103
103
  `, { mode: 384 });
104
104
  process.env.FLARE_DEPLOY_TOKEN = token;
105
- const { link } = await import("./chunk-5yg1zj5n.js");
105
+ const { link } = await import("./chunk-znktbexz.js");
106
106
  return link({ platform, project: result.project });
107
107
  }
108
108
  throw Error("Login expired. Run flare dev again.");
@@ -22,14 +22,17 @@ var sha256 = (value) => createHash("sha256").update(value).digest("hex");
22
22
  var manifestDigest = ({ digest: _digest, ...manifest }) => sha256(JSON.stringify(manifest));
23
23
  async function payloadFiles(directory, prefix = "") {
24
24
  const files = [];
25
- for (const name of (await readdir(join(directory, prefix))).sort()) {
26
- const path = prefix + name;
27
- const stat = await lstat(join(directory, path));
28
- if (stat.isSymbolicLink())
25
+ const entries = await readdir(join(directory, prefix), {
26
+ withFileTypes: true
27
+ });
28
+ entries.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
29
+ for (const entry of entries) {
30
+ const path = prefix + entry.name;
31
+ if (entry.isSymbolicLink())
29
32
  throw Error("Platform payload contains a symlink");
30
- if (stat.isDirectory())
33
+ if (entry.isDirectory())
31
34
  files.push(...await payloadFiles(directory, path + "/"));
32
- else if (stat.isFile())
35
+ else if (entry.isFile())
33
36
  files.push(path);
34
37
  else
35
38
  throw Error("Unsupported platform payload entry");
@@ -594,6 +597,17 @@ function settingsDigest(settings) {
594
597
  }
595
598
 
596
599
  // packages/flare/src/cli/platform-health.mjs
600
+ function runtimeReadinessFailures(data, state, release = state.installed) {
601
+ const expected = {
602
+ readiness: "control-plane-ready",
603
+ installationId: state.id,
604
+ platformPayload: release?.digest,
605
+ version: release?.version,
606
+ runtimeAbi: release?.runtimeAbi ?? 4,
607
+ protocol: release?.protocol ?? 1
608
+ };
609
+ return Object.keys(expected).filter((key) => expected[key] === undefined || data?.[key] !== expected[key]);
610
+ }
597
611
  async function platformHealth(cf, state, fetcher) {
598
612
  const checkedAt = Date.now();
599
613
  const probe = async (fn) => {
@@ -611,9 +625,10 @@ async function platformHealth(cf, state, fetcher) {
611
625
  return { status: "unhealthy", httpStatus: r.status };
612
626
  }
613
627
  const d = await r.json();
614
- const matches = d.readiness === "control-plane-ready" && d.installationId === state.id && d.platformPayload === state.installed?.digest && d.version === state.installed?.version;
628
+ const mismatches = runtimeReadinessFailures(d, state);
615
629
  return {
616
- status: matches ? "healthy" : "mismatch",
630
+ status: mismatches.length ? "mismatch" : "healthy",
631
+ ...mismatches.length ? { mismatches } : {},
617
632
  version: d.version,
618
633
  installationId: d.installationId
619
634
  };
@@ -1024,19 +1039,29 @@ async function verify(cf, state, release, fetcher, wait) {
1024
1039
  await assertAccess(cf, state);
1025
1040
  await supervisorNamespace(cf, state);
1026
1041
  let healthy = false;
1042
+ let lastProbe = "unavailable";
1027
1043
  for (let attempt = 0;attempt < 5; attempt++) {
1028
1044
  try {
1029
1045
  const response = await fetcher(`https://${state.addresses.runtimeHost}/health?ready=1`, { redirect: "error", signal: AbortSignal.timeout(15000) });
1030
- const data = await response.json();
1031
- healthy = response.ok && data.readiness === "control-plane-ready" && data.version === release.manifest.version && data.installationId === state.id && data.platformPayload === release.manifest.digest && data.runtimeAbi === 4 && data.protocol === 1;
1032
- } catch {}
1046
+ lastProbe = `HTTP ${response.status}`;
1047
+ if (!response.ok)
1048
+ await response.body?.cancel();
1049
+ else {
1050
+ const failures = runtimeReadinessFailures(await response.json(), state, release.manifest);
1051
+ healthy = failures.length === 0;
1052
+ if (failures.length)
1053
+ lastProbe += "; mismatches: " + failures.join(",");
1054
+ }
1055
+ } catch {
1056
+ lastProbe = "network/timeout or invalid health response";
1057
+ }
1033
1058
  if (healthy)
1034
1059
  break;
1035
1060
  if (attempt < 4)
1036
1061
  await wait(2000);
1037
1062
  }
1038
1063
  if (!healthy)
1039
- throw Error("Runtime health does not match the installed platform payload; operation remains resumable");
1064
+ throw Error("Runtime health does not match the installed platform payload; operation remains resumable. Last probe: " + lastProbe);
1040
1065
  for (const path of ["/", "/api/session"]) {
1041
1066
  const response = await fetcher(`https://${state.addresses.adminHost}${path}`, { redirect: "manual", signal: AbortSignal.timeout(15000) });
1042
1067
  if ([301, 302, 303, 307, 308].includes(response.status)) {
@@ -26,7 +26,7 @@ async function request(platform, path, body) {
26
26
  return "null";
27
27
  }));
28
28
  if (config) {
29
- const { storedToken } = await import("./chunk-5s6zv1h9.js");
29
+ const { storedToken } = await import("./chunk-jrptpjmm.js");
30
30
  token = await storedToken(platform, config.project);
31
31
  }
32
32
  }
@@ -107,7 +107,7 @@ async function deploy(options = {}) {
107
107
  }
108
108
  }
109
109
  const { prepare, generateApi } = await import("./chunk-fv98dfg5.js");
110
- const { buildApplication } = await import("./chunk-vx7kmczm.js");
110
+ const { buildApplication } = await import("./chunk-zt96ghm7.js");
111
111
  const source = await prepare(), directory = resolve(".flare/releases/" + randomUUID());
112
112
  try {
113
113
  await buildApplication({ ...source, outdir: directory });
@@ -160,7 +160,7 @@ async function dev(options = {}) {
160
160
  } catch (e) {
161
161
  if (e.code !== "ENOENT")
162
162
  throw e;
163
- const { connect } = await import("./chunk-5s6zv1h9.js");
163
+ const { connect } = await import("./chunk-jrptpjmm.js");
164
164
  await connect();
165
165
  }
166
166
  }
@@ -172,11 +172,11 @@ async function dev(options = {}) {
172
172
  } catch (e) {
173
173
  if (process.env.FLARE_DEPLOY_TOKEN || e.status !== 401 && !e.message.startsWith("Sign in"))
174
174
  throw e;
175
- const { connect } = await import("./chunk-5s6zv1h9.js");
175
+ const { connect } = await import("./chunk-jrptpjmm.js");
176
176
  await connect();
177
177
  }
178
178
  }
179
- const { buildApplication } = await import("./chunk-vx7kmczm.js");
179
+ const { buildApplication } = await import("./chunk-zt96ghm7.js");
180
180
  let busy = false, pending = false, timer, hash;
181
181
  const publish = async (initial = false) => {
182
182
  if (busy) {
@@ -308,7 +308,7 @@ async function releaseOperation(options, operation) {
308
308
  const bundle = await request(config.platform, `/api/projects/${config.project}/${source}/release-bundle?hash=${options.hash}`);
309
309
  if (bundle.release.hash !== options.hash)
310
310
  throw Error("Release hash mismatch");
311
- const { generateReferences } = await import("./chunk-vx7kmczm.js");
311
+ const { generateReferences } = await import("./chunk-zt96ghm7.js");
312
312
  const { withGeneratedApi } = await import("./chunk-fv98dfg5.js");
313
313
  await mkdir(".flare", { recursive: true });
314
314
  const directory = await mkdtemp(resolve(".flare/release-contract-"));
@@ -356,13 +356,19 @@ async function generateReferences(contract) {
356
356
  let generated = `import {reference} from "@auersperg/flare";
357
357
  `;
358
358
  const refs = [];
359
+ const types = new Map;
360
+ const declaration = async (schema, name) => {
361
+ const key = stableJson(schema), previous = types.get(key);
362
+ if (previous)
363
+ return `export type ${name} = ${previous};
364
+ `;
365
+ const code = await compile(schema, name, { bannerComment: "" });
366
+ types.set(key, name);
367
+ return code;
368
+ };
359
369
  for (const [index, [path, fn]] of Object.entries(contract.functions).entries()) {
360
- generated += await compile(fn.input, `Input${index}`, {
361
- bannerComment: ""
362
- });
363
- generated += await compile(fn.output, `Output${index}`, {
364
- bannerComment: ""
365
- });
370
+ generated += await declaration(fn.input, `Input${index}`);
371
+ generated += await declaration(fn.output, `Output${index}`);
366
372
  refs.push(`${JSON.stringify(path)}: reference<Input${index}, Output${index}>(${JSON.stringify(path)}, ${JSON.stringify(fn.kind)})`);
367
373
  }
368
374
  generated += `
package/dist/cli/flare.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // packages/flare/package.json
4
4
  var package_default = {
5
5
  name: "@auersperg/flare",
6
- version: "0.3.8",
6
+ version: "0.3.10",
7
7
  license: "MIT",
8
8
  type: "module",
9
9
  description: "Flare client, server declarations and development CLI",
@@ -39,7 +39,7 @@ var package_default = {
39
39
  },
40
40
  dependencies: {
41
41
  "@standard-schema/spec": "1.1.0",
42
- zod: "4.6.5",
42
+ zod: "4.6.6",
43
43
  "json-schema-to-typescript": "16.0.0"
44
44
  },
45
45
  engines: {
@@ -147,7 +147,7 @@ Uses the platform payload bundled with this CLI release. Never updates applicati
147
147
  options[key] = args[++i];
148
148
  }
149
149
  }
150
- const { platform } = await import("./chunk-y6e2h40n.js");
150
+ const { platform } = await import("./chunk-m0698kap.js");
151
151
  console.log(JSON.stringify(await platform(options), null, 2));
152
152
  }
153
153
  } catch (error) {
@@ -187,12 +187,12 @@ Uses the platform payload bundled with this CLI release. Never updates applicati
187
187
  if (command === "build") {
188
188
  if (!options.entry || !options.outdir)
189
189
  throw Error("Required: --entry, --outdir");
190
- const { buildApplication } = await import("./chunk-vx7kmczm.js");
190
+ const { buildApplication } = await import("./chunk-zt96ghm7.js");
191
191
  const x = await buildApplication(options);
192
192
  console.log(`Built immutable release ${x.hash}
193
193
  ${x.outdir}`);
194
194
  } else {
195
- const cloud = await import("./chunk-5yg1zj5n.js");
195
+ const cloud = await import("./chunk-znktbexz.js");
196
196
  const x = await cloud[command](options);
197
197
  if (x)
198
198
  console.log(JSON.stringify(x, null, 2));
@@ -1,3 +1,32 @@
1
+ // packages/flare/src/sdk/http.ts
2
+ async function readBody(body, limit, tooLarge) {
3
+ const reader = body.getReader();
4
+ const chunks = [];
5
+ let size = 0;
6
+ try {
7
+ for (;; ) {
8
+ const { done, value } = await reader.read();
9
+ if (done)
10
+ break;
11
+ size += value.byteLength;
12
+ if (size > limit)
13
+ tooLarge();
14
+ chunks.push(value);
15
+ }
16
+ } finally {
17
+ await reader.cancel().catch(() => {});
18
+ }
19
+ if (chunks.length === 1)
20
+ return chunks[0];
21
+ const bytes = new Uint8Array(size);
22
+ let offset = 0;
23
+ for (const chunk of chunks) {
24
+ bytes.set(chunk, offset);
25
+ offset += chunk.byteLength;
26
+ }
27
+ return bytes;
28
+ }
29
+
1
30
  // packages/flare/src/sdk/protocol.ts
2
31
  var LIMITS = Object.freeze({
3
32
  inputBytes: 65536,
@@ -75,28 +104,7 @@ async function jsonBody(request, limit = LIMITS.inputBytes) {
75
104
  fail("INVALID_CONTENT_TYPE");
76
105
  if (!request.body)
77
106
  fail("INVALID_INPUT");
78
- const reader = request.body.getReader();
79
- const chunks = [];
80
- let size = 0;
81
- try {
82
- for (;; ) {
83
- const { done, value } = await reader.read();
84
- if (done)
85
- break;
86
- size += value.byteLength;
87
- if (size > limit)
88
- fail("TOO_LARGE");
89
- chunks.push(value);
90
- }
91
- } finally {
92
- await reader.cancel().catch(() => {});
93
- }
94
- const bytes = new Uint8Array(size);
95
- let offset = 0;
96
- for (const c of chunks) {
97
- bytes.set(c, offset);
98
- offset += c.byteLength;
99
- }
107
+ const bytes = await readBody(request.body, limit, () => fail("TOO_LARGE"));
100
108
  try {
101
109
  const parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
102
110
  boundedJson(parsed, limit);
@@ -301,7 +309,7 @@ async function collectCatalog(database, now = Date.now()) {
301
309
  // packages/core/package.json
302
310
  var package_default = {
303
311
  name: "@flare/platform",
304
- version: "0.3.8",
312
+ version: "0.3.10",
305
313
  type: "module",
306
314
  license: "MIT",
307
315
  private: true,
@@ -1 +1 @@
1
- import{a as e,r as t}from"./client.BeKNsjPN.js";import{At as n,S as r,_ as i,a,b as o,f as s,g as c,n as l,p as u,pt as d,s as f,u as p,v as m,x as h,y as g}from"./common.TAClb6G9.js";import{o as _}from"./index.astro_astro_type_script_index_0_lang.Blg5hU9g.js";var v=e(t(),1),y=n();function b(e){let t=c(e.project,e.env),[n,b]=(0,v.useState)(``),[x,S]=(0,v.useState)(),C=p(t+`/auth-users`+(n?`?cursor=`+encodeURIComponent(n):``)),w=p(t+`/auth-settings`),T=e.session.role===`owner`,E=(n,r,i,a=[])=>{S({title:r,description:`Passwords are never displayed. Changes revoke existing sessions.`,productionId:e.env===`prod`?e.project.id:void 0,fields:a,submit:async r=>{try{await u(t+`/`+n,{...i,...s(a,e=>new FormData(r).get(e)),confirm:e.project.id})}catch(e){throw e instanceof Error&&e.message===`INVALID_PASSWORD`?Error(`Password must contain 6–128 characters. Spaces are allowed; the password is not trimmed.`):e instanceof Error&&e.message===`REGISTRATION_UNAVAILABLE`?Error(`This login is already registered. Choose another login or manage the existing user.`):e}C.refresh(),w.refresh()}})},D={name:`password`,type:`password`,required:!0,description:`6–128 characters.`};return(0,y.jsxs)(`div`,{className:`space-y-4 p-4`,children:[(0,y.jsx)(`h1`,{className:`page-title`,children:`Auth`}),(0,y.jsx)(f,{loading:C.loading||w.loading,error:C.error||w.error}),w.data&&(0,y.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,y.jsx)(_,{status:w.data.enabled?`Enabled`:`Disabled`}),(0,y.jsx)(_,{status:w.data.registration?`Public registration`:`Registration closed`}),T&&(0,y.jsx)(d,{variant:`outline`,onClick:()=>E(`auth-configure`,`Configure authentication`,{},[{name:`enabled`,type:`boolean`,value:w.data.enabled},{name:`registration`,type:`boolean`,value:w.data.registration}]),children:`Configure`}),T&&(0,y.jsx)(d,{onClick:()=>E(`auth-create`,`Create user`,{},[{name:`login`,type:`string`,required:!0},D]),children:`Create user`})]}),C.data&&(0,y.jsxs)(y.Fragment,{children:[(0,y.jsxs)(i,{children:[(0,y.jsx)(h,{children:(0,y.jsxs)(r,{children:[(0,y.jsx)(o,{children:`Login`}),(0,y.jsx)(o,{children:`User ID`}),(0,y.jsx)(o,{children:`Status`}),(0,y.jsx)(o,{children:`Created`}),(0,y.jsx)(o,{children:`Actions`})]})}),(0,y.jsx)(m,{children:C.data.users.map(e=>(0,y.jsxs)(r,{children:[(0,y.jsx)(g,{children:e.login}),(0,y.jsx)(g,{children:(0,y.jsx)(`code`,{children:e.id})}),(0,y.jsx)(g,{children:(0,y.jsx)(_,{status:e.disabled?`Blocked`:`Active`})}),(0,y.jsx)(g,{children:new Date(e.createdAt).toLocaleString()}),(0,y.jsx)(g,{children:(0,y.jsx)(`div`,{className:`flex flex-wrap gap-2`,children:T&&(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)(d,{variant:`outline`,size:`sm`,onClick:()=>E(`auth-password`,`Change user password`,{id:e.id},[D]),children:`Change password`}),(0,y.jsx)(d,{variant:`outline`,size:`sm`,onClick:()=>E(`auth-revoke`,`Revoke all sessions`,{id:e.id}),children:`Revoke sessions`}),(0,y.jsx)(d,{variant:`outline`,size:`sm`,onClick:()=>E(`auth-disable`,e.disabled?`Unblock user`:`Block user`,{id:e.id,disabled:!e.disabled}),children:e.disabled?`Unblock`:`Block`})]})})})]},e.id))})]}),!C.data.users.length&&(0,y.jsx)(l,{title:`No users`}),(0,y.jsxs)(`div`,{className:`flex gap-2`,children:[(0,y.jsx)(d,{variant:`outline`,disabled:!n,onClick:()=>b(``),children:`First page`}),(0,y.jsx)(d,{variant:`outline`,disabled:!C.data.next,onClick:()=>b(C.data.next),children:`Next page`})]})]}),x&&(0,y.jsx)(a,{modal:x,onClose:()=>S(void 0)})]})}export{b as default};
1
+ import{a as e,r as t}from"./client.BeKNsjPN.js";import{At as n,S as r,_ as i,a,b as o,f as s,g as c,n as l,p as u,pt as d,s as f,u as p,v as m,x as h,y as g}from"./common.9pujiaSZ.js";import{o as _}from"./index.astro_astro_type_script_index_0_lang.Dsfj1oeZ.js";var v=e(t(),1),y=n();function b(e){let t=c(e.project,e.env),[n,b]=(0,v.useState)(``),[x,S]=(0,v.useState)(),C=p(t+`/auth-users`+(n?`?cursor=`+encodeURIComponent(n):``)),w=p(t+`/auth-settings`),T=e.session.role===`owner`,E=(n,r,i,a=[])=>{S({title:r,description:`Passwords are never displayed. Changes revoke existing sessions.`,productionId:e.env===`prod`?e.project.id:void 0,fields:a,submit:async r=>{try{await u(t+`/`+n,{...i,...s(a,e=>new FormData(r).get(e)),confirm:e.project.id})}catch(e){throw e instanceof Error&&e.message===`INVALID_PASSWORD`?Error(`Password must contain 6–128 characters. Spaces are allowed; the password is not trimmed.`):e instanceof Error&&e.message===`REGISTRATION_UNAVAILABLE`?Error(`This login is already registered. Choose another login or manage the existing user.`):e}C.refresh(),w.refresh()}})},D={name:`password`,type:`password`,required:!0,description:`6–128 characters.`};return(0,y.jsxs)(`div`,{className:`space-y-4 p-4`,children:[(0,y.jsx)(`h1`,{className:`page-title`,children:`Auth`}),(0,y.jsx)(f,{loading:C.loading||w.loading,error:C.error||w.error}),w.data&&(0,y.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,y.jsx)(_,{status:w.data.enabled?`Enabled`:`Disabled`}),(0,y.jsx)(_,{status:w.data.registration?`Public registration`:`Registration closed`}),T&&(0,y.jsx)(d,{variant:`outline`,onClick:()=>E(`auth-configure`,`Configure authentication`,{},[{name:`enabled`,type:`boolean`,value:w.data.enabled},{name:`registration`,type:`boolean`,value:w.data.registration}]),children:`Configure`}),T&&(0,y.jsx)(d,{onClick:()=>E(`auth-create`,`Create user`,{},[{name:`login`,type:`string`,required:!0},D]),children:`Create user`})]}),C.data&&(0,y.jsxs)(y.Fragment,{children:[(0,y.jsxs)(i,{children:[(0,y.jsx)(h,{children:(0,y.jsxs)(r,{children:[(0,y.jsx)(o,{children:`Login`}),(0,y.jsx)(o,{children:`User ID`}),(0,y.jsx)(o,{children:`Status`}),(0,y.jsx)(o,{children:`Created`}),(0,y.jsx)(o,{children:`Actions`})]})}),(0,y.jsx)(m,{children:C.data.users.map(e=>(0,y.jsxs)(r,{children:[(0,y.jsx)(g,{children:e.login}),(0,y.jsx)(g,{children:(0,y.jsx)(`code`,{children:e.id})}),(0,y.jsx)(g,{children:(0,y.jsx)(_,{status:e.disabled?`Blocked`:`Active`})}),(0,y.jsx)(g,{children:new Date(e.createdAt).toLocaleString()}),(0,y.jsx)(g,{children:(0,y.jsx)(`div`,{className:`flex flex-wrap gap-2`,children:T&&(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)(d,{variant:`outline`,size:`sm`,onClick:()=>E(`auth-password`,`Change user password`,{id:e.id},[D]),children:`Change password`}),(0,y.jsx)(d,{variant:`outline`,size:`sm`,onClick:()=>E(`auth-revoke`,`Revoke all sessions`,{id:e.id}),children:`Revoke sessions`}),(0,y.jsx)(d,{variant:`outline`,size:`sm`,onClick:()=>E(`auth-disable`,e.disabled?`Unblock user`:`Block user`,{id:e.id,disabled:!e.disabled}),children:e.disabled?`Unblock`:`Block`})]})})})]},e.id))})]}),!C.data.users.length&&(0,y.jsx)(l,{title:`No users`}),(0,y.jsxs)(`div`,{className:`flex gap-2`,children:[(0,y.jsx)(d,{variant:`outline`,disabled:!n,onClick:()=>b(``),children:`First page`}),(0,y.jsx)(d,{variant:`outline`,disabled:!C.data.next,onClick:()=>b(C.data.next),children:`Next page`})]})]}),x&&(0,y.jsx)(a,{modal:x,onClose:()=>S(void 0)})]})}export{b as default};
@@ -1 +1 @@
1
- import{a as e,r as t}from"./client.BeKNsjPN.js";import{A as n,At as r,O as i,k as a,m as o,p as s,pt as c,q as l,r as u,t as d}from"./common.TAClb6G9.js";var f=e(t(),1),p=r();function m(){let e=new URLSearchParams(location.search).get(`cli`)??``,[t,r]=(0,f.useState)(``),[m,h]=(0,f.useState)([]),[g,_]=(0,f.useState)(`new`),[v,y]=(0,f.useState)(``),[b,x]=(0,f.useState)(``),[S,C]=(0,f.useState)(!1),[w,T]=(0,f.useState)(!1);(0,f.useEffect)(()=>{let t=new AbortController;return Promise.all([s(`cli/approval?id=`+encodeURIComponent(e),void 0,t.signal),s(`projects`,void 0,t.signal)]).then(([e,t])=>{r(e.code),h(t),_(`new`)}).catch(e=>{t.signal.aborted||x(o(e))}),()=>t.abort()},[e]);async function E(){C(!0),x(``);try{let t=g;if(t===`new`){let e=await s(`projects`,{name:v,customer:`Applications`,origins:[]});t=e.id,h(t=>[...t,{id:e.id,name:v,customer:`Applications`}]),_(t)}await s(`cli/approval`,{id:e,project:t}),T(!0)}catch(e){x(o(e))}finally{C(!1)}}return(0,p.jsxs)(`main`,{className:`mx-auto max-w-lg p-8`,children:[(0,p.jsx)(`h1`,{className:`mb-4 text-xl font-semibold`,children:`Connect Flare CLI`}),w?(0,p.jsx)(`p`,{children:`Connected. Return to your terminal. You can close this tab.`}):(0,p.jsxs)(a,{children:[(0,p.jsx)(`p`,{children:`Approve only a login you just started. Compare this code with your terminal:`}),(0,p.jsx)(`strong`,{className:`text-2xl font-mono`,children:t||`Loading…`}),(0,p.jsx)(`p`,{children:`Grants this computer deployment access to the selected project, including production. The credential expires in 30 days and can be revoked in Settings.`}),(0,p.jsxs)(i,{children:[(0,p.jsx)(n,{children:`Project`}),(0,p.jsx)(d,{label:`CLI project`,value:g,onChange:_,items:[...m.map(e=>({value:e.id,label:e.name})),{value:`new`,label:`Create new project`}]})]}),g===`new`&&(0,p.jsxs)(i,{children:[(0,p.jsx)(n,{children:`New project name`}),(0,p.jsx)(l,{value:v,onChange:e=>y(e.target.value),maxLength:100})]}),b&&(0,p.jsx)(u,{children:b}),(0,p.jsx)(c,{disabled:S||!t||!g||g===`new`&&!v.trim(),onClick:E,children:`Approve connection`})]})]})}export{m as default};
1
+ import{a as e,r as t}from"./client.BeKNsjPN.js";import{A as n,At as r,O as i,k as a,m as o,p as s,pt as c,q as l,r as u,t as d}from"./common.9pujiaSZ.js";var f=e(t(),1),p=r();function m(){let e=new URLSearchParams(location.search).get(`cli`)??``,[t,r]=(0,f.useState)(``),[m,h]=(0,f.useState)([]),[g,_]=(0,f.useState)(`new`),[v,y]=(0,f.useState)(``),[b,x]=(0,f.useState)(``),[S,C]=(0,f.useState)(!1),[w,T]=(0,f.useState)(!1);(0,f.useEffect)(()=>{let t=new AbortController;return Promise.all([s(`cli/approval?id=`+encodeURIComponent(e),void 0,t.signal),s(`projects`,void 0,t.signal)]).then(([e,t])=>{r(e.code),h(t),_(`new`)}).catch(e=>{t.signal.aborted||x(o(e))}),()=>t.abort()},[e]);async function E(){C(!0),x(``);try{let t=g;if(t===`new`){let e=await s(`projects`,{name:v,customer:`Applications`,origins:[]});t=e.id,h(t=>[...t,{id:e.id,name:v,customer:`Applications`}]),_(t)}await s(`cli/approval`,{id:e,project:t}),T(!0)}catch(e){x(o(e))}finally{C(!1)}}return(0,p.jsxs)(`main`,{className:`mx-auto max-w-lg p-8`,children:[(0,p.jsx)(`h1`,{className:`mb-4 text-xl font-semibold`,children:`Connect Flare CLI`}),w?(0,p.jsx)(`p`,{children:`Connected. Return to your terminal. You can close this tab.`}):(0,p.jsxs)(a,{children:[(0,p.jsx)(`p`,{children:`Approve only a login you just started. Compare this code with your terminal:`}),(0,p.jsx)(`strong`,{className:`text-2xl font-mono`,children:t||`Loading…`}),(0,p.jsx)(`p`,{children:`Grants this computer deployment access to the selected project, including production. The credential expires in 30 days and can be revoked in Settings.`}),(0,p.jsxs)(i,{children:[(0,p.jsx)(n,{children:`Project`}),(0,p.jsx)(d,{label:`CLI project`,value:g,onChange:_,items:[...m.map(e=>({value:e.id,label:e.name})),{value:`new`,label:`Create new project`}]})]}),g===`new`&&(0,p.jsxs)(i,{children:[(0,p.jsx)(n,{children:`New project name`}),(0,p.jsx)(l,{value:v,onChange:e=>y(e.target.value),maxLength:100})]}),b&&(0,p.jsx)(u,{children:b}),(0,p.jsx)(c,{disabled:S||!t||!g||g===`new`&&!v.trim(),onClick:E,children:`Approve connection`})]})]})}export{m as default};
@@ -0,0 +1 @@
1
+ async function e(e,t,n){let r=e.getReader(),i=[],a=0;try{for(;;){let{done:e,value:o}=await r.read();if(e)break;a+=o.byteLength,a>t&&n(),i.push(o)}}finally{await r.cancel().catch(()=>{})}if(i.length===1)return i[0];let o=new Uint8Array(a),s=0;for(let e of i)o.set(e,s),s+=e.byteLength;return o}var t=Object.freeze({inputBytes:65536,outputBytes:262144,frameBytes:8192,subscriptions:64,fileBytes:10485760}),n=class extends Error{code;ambiguous;constructor(e,t,n=!1){super(t),this.code=e,this.ambiguous=n,this.name=`FlareError`}};function r(e){if(e.length>256||!/^[A-Za-z][A-Za-z0-9_]*(\/[A-Za-z][A-Za-z0-9_]*)*$/.test(e)||e.split(`/`).some(e=>[`__proto__`,`prototype`,`constructor`].includes(e)))throw new n(`INVALID_PATH`,`Invalid function path`)}function i(e,t){return r(e),Object.freeze({path:e,kind:t})}function a(e){let t=new Set;function r(e,i){if(i>40)throw new n(`INVALID_JSON`,`JSON nesting limit exceeded`);if(e===null||typeof e==`string`||typeof e==`boolean`||typeof e==`number`&&Number.isFinite(e))return e;if(typeof e!=`object`||!e||t.has(e))throw new n(`INVALID_JSON`,`Expected lossless JSON`);if(!Array.isArray(e)&&Object.getPrototypeOf(e)!==Object.prototype&&Object.getPrototypeOf(e)!==null)throw new n(`INVALID_JSON`,`Expected plain JSON object`);if(Object.getOwnPropertySymbols(e).length)throw new n(`INVALID_JSON`,`Symbol keys are unsupported`);t.add(e);let a;if(Array.isArray(e))a=Array.from(e,e=>r(e,i+1));else{let t=Object.create(null);for(let a of Object.keys(e).sort()){let o=Object.getOwnPropertyDescriptor(e,a);if(!(`value`in o))throw new n(`INVALID_JSON`,`Getters are unsupported`);t[a]=r(o.value,i+1)}a=t}return t.delete(e),a}return JSON.stringify(r(e,0))}function o(e,t){let r=a(e);if(new TextEncoder().encode(r).byteLength>t)throw new n(`TOO_LARGE`,`JSON payload exceeds limit`);return r}function s(e){if(e.size>t.fileBytes)throw new n(`TOO_LARGE`,`Maximum file size: 10 MiB`);if(!e.name||e.name.length>120||/[\x00-\x1f\x7f/\\]/.test(e.name))throw new n(`INVALID_INPUT`,`File name must be 1–120 characters, without path separators`);return{name:e.name,size:e.size,type:[`text/plain`,`image/png`,`image/jpeg`,`image/webp`,`application/pdf`].includes(e.type)?e.type:`application/octet-stream`}}async function c(r,i,a={}){if(i.size>t.fileBytes)throw new n(`TOO_LARGE`,`Maximum file size: 10 MiB`);let o=new URL(r.url);if(o.username||o.password||o.protocol!==`https:`&&!(o.protocol===`http:`&&[`localhost`,`127.0.0.1`,`[::1]`].includes(o.hostname)))throw new n(`INVALID_URL`,`Expected secure upload URL`);let s;try{s=await(a.fetch??globalThis.fetch)(o.href,{method:`POST`,body:i,redirect:`error`,credentials:`omit`,signal:AbortSignal.any([...a.signal?[a.signal]:[],AbortSignal.timeout(a.timeoutMs??12e4)])})}catch{throw new n(`UPLOAD_UNKNOWN`,`Connection interrupted or upload timed out; refresh the file list before trying again`,!0)}let c=s.body?await e(s.body,t.outputBytes,()=>{throw Error(`Upload response exceeds limit`)}).then(e=>JSON.parse(new TextDecoder(`utf-8`,{fatal:!0}).decode(e))).catch(()=>null):null;if(!s.ok){let e=c&&typeof c==`object`&&`error`in c&&typeof c.error==`string`?c.error:`HTTP_${s.status}`;throw new n(e,`Upload failed: ${e}`,s.status>=500)}if(!c||typeof c!=`object`||!(`id`in c)||typeof c.id!=`string`)throw new n(`INVALID_RESPONSE`,`Upload outcome is unknown; refresh the file list before uploading again`,!0);return{id:c.id}}var l=new Set([`UNAUTHORIZED`,`AUTH_EXPIRED`,`FORBIDDEN`,`AUTH_DISABLED`,`HTTP_401`,`HTTP_403`]);function u(i){let s=new URL(i.url);if(!/^https?:$/.test(s.protocol)||s.username||s.password||s.search||s.hash)throw new n(`INVALID_URL`,`Expected backend HTTP URL`);if(s.protocol===`http:`&&![`localhost`,`127.0.0.1`,`[::1]`].includes(s.hostname))throw new n(`INVALID_URL`,`Remote backends require HTTPS`);s.pathname=s.pathname.replace(/\/$/,``);let c=i.fetch??globalThis.fetch;if(!c)throw new n(`TRANSPORT_UNAVAILABLE`,`Provide a fetch implementation`);let u=new Map,d=!1,f,p=!1,m=0,h=0,g=0,_,v=new Set,y=e=>`${s.href.replace(/\/$/,``)}${e}`,b=(e,t,n)=>{for(let r of e.observers)try{r[t]?.(t===`next`?JSON.parse(n):n)}catch{}};function x(){if(d)throw new n(`DISPOSED`,`Client is disposed`)}async function S(r,a,s){x();let l=o(a,t.inputBytes),u;try{u=await i.getToken?.()??null}catch{throw new n(`AUTH_UNAVAILABLE`,`Cannot obtain access token`)}x();let d=new AbortController,f=setTimeout(()=>d.abort(),i.requestTimeoutMs??3e4);try{let i=await c(y(r),{method:`POST`,headers:{"Content-Type":`application/json`,...u?{Authorization:`Bearer ${u}`}:{}},body:l,signal:d.signal,redirect:`error`});if(!i.body)throw new n(`INVALID_RESPONSE`,`Missing response body`,s);let a=await e(i.body,t.outputBytes,()=>{throw new n(`INVALID_RESPONSE`,`Response exceeds limit`,s)}),f;try{f=JSON.parse(new TextDecoder(`utf-8`,{fatal:!0}).decode(a))}catch(e){throw i.ok?e:new n(`HTTP_${i.status}`,`Backend request failed`,s&&i.status>=500)}if(!i.ok)throw new n(f&&typeof f==`object`&&`error`in f&&typeof f.error==`string`&&/^[A-Z][A-Z0-9_]{0,63}$/.test(f.error)?f.error:`HTTP_${i.status}`,`Backend request failed`,s&&i.status>=500);if(!f||typeof f!=`object`||!(`result`in f))throw new n(`INVALID_RESPONSE`,`Invalid protocol envelope`,s);return o(f.result,t.outputBytes),f.result}catch(e){throw e instanceof n?e:e instanceof SyntaxError?new n(`INVALID_RESPONSE`,`Invalid JSON response`,s):new n(`TRANSPORT_ERROR`,s?`Write outcome is unknown; do not replay automatically`:`Backend request interrupted`,s)}finally{clearTimeout(f)}}function C(e){f?.send(o(e,t.frameBytes))}function w(e){C({type:`subscribe`,id:e.id,path:e.ref.path,args:e.args})}function T(){if(d||!u.size||_)return;let e=Math.min(3e4,250*2**Math.min(m++,7));_=setTimeout(()=>{_=void 0,E()},e)}async function E(){if(d||p||f||!u.size)return;p=!0;let e=++h;try{let r=await S(`/v1/live/ticket`,{},!1);if(d||e!==h||!u.size)return;if(!r||typeof r.ticket!=`string`||r.ticket.length>1024)throw new n(`INVALID_RESPONSE`,`Invalid live ticket`);let a=new URL(y(`/v1/live`));a.protocol=a.protocol===`https:`?`wss:`:`ws:`,a.searchParams.set(`ticket`,r.ticket);let s=i.createWebSocket?i.createWebSocket(a.href):new globalThis.WebSocket(a.href);f=s;let c=()=>{if(!(f!==s||d)){m=0;for(let e of u.values())e.revision=-1,e.snapshot=void 0,e.terminal||w(e)}},p=e=>{if(!(f!==s||d))try{if(typeof e.data!=`string`||new TextEncoder().encode(e.data).byteLength>t.outputBytes+1024)throw Error(`Invalid frame`);let r=JSON.parse(e.data);if(r.type===`ready`)return;let i=[...u.values()].find(e=>e.id===r.id);if(!i||i.terminal)return;if(r.type===`error`){i.terminal=!0,l.has(r.code)&&(i.snapshot=void 0),b(i,`error`,new n(typeof r.code==`string`?r.code:`LIVE_ERROR`,`Live subscription failed`));return}if(r.type!==`snapshot`||!Number.isSafeInteger(r.revision)||r.revision<0||!(`result`in r))throw Error(`Invalid snapshot`);let a=o(r.result,t.outputBytes);if(r.revision>i.revision){let e=i.snapshot!==a;i.revision=r.revision,i.snapshot=a,e&&b(i,`next`,i.snapshot)}C({type:`ack`,id:i.id,revision:i.revision})}catch{s.close(1002,`Invalid protocol frame`)}},g=e=>{if(x(),f===s){f=void 0;for(let t of u.values())e.code===1008&&(t.snapshot=void 0,t.revision=-1),t.terminal||b(t,`error`,new n(e.code===1008?`AUTH_EXPIRED`:`LIVE_DISCONNECTED`,`Live reconnecting; next snapshot will be current`));T()}},_=()=>s.close(),x=()=>{s.removeEventListener(`open`,c),s.removeEventListener(`message`,p),s.removeEventListener(`close`,g),s.removeEventListener(`error`,_),v.delete(x)};v.add(x),s.addEventListener(`open`,c),s.addEventListener(`message`,p),s.addEventListener(`close`,g),s.addEventListener(`error`,_)}catch(t){if(d||e!==h)return;for(let e of u.values())t instanceof n&&l.has(t.code)&&(e.snapshot=void 0,e.revision=-1),b(e,`error`,t instanceof n?t:new n(`LIVE_UNAVAILABLE`,`Live connection failed`));T()}finally{p=!1,!d&&e!==h&&!f&&u.size&&T()}}function D(e){return async(t,i)=>{if(r(t.path),t.kind!==e)throw new n(`INVALID_KIND`,`Expected a ${e} function`);return S(`/v1/call`,{path:t.path,kind:t.kind,args:i},t.kind===`mutation`||t.kind===`action`)}}return{query:D(`query`),mutation:D(`mutation`),action:D(`action`),onUpdate(e,i,s,c){let l={next:s,error:c};if(x(),r(e.path),e.kind!==`query`)throw new n(`INVALID_KIND`,`Only queries are subscribable`);o({type:`subscribe`,id:`subscription-9999999999`,path:e.path,args:i},t.frameBytes);let d=a({path:e.path,args:i}),p=u.get(d);if(!p){if(u.size>=t.subscriptions)throw new n(`TOO_MANY_SUBSCRIPTIONS`,`Subscription limit exceeded`);p={id:`subscription-${++g}`,ref:e,args:JSON.parse(a(i)),observers:new Set,revision:-1,terminal:!1},u.set(d,p),f?.readyState===1&&w(p)}let m={next:l.next,error:l.error};p.observers.add(m),p.snapshot!==void 0&&b({...p,observers:new Set([l])},`next`,p.snapshot),E();let y=!1;return()=>{if(!y&&(y=!0,p.observers.delete(m),p.observers.size||(u.delete(d),f?.readyState===1&&C({type:`unsubscribe`,id:p.id})),!u.size)){++h,_&&clearTimeout(_),_=void 0;let e=f;f=void 0;for(let e of[...v])e();e?.close(1e3,`No subscriptions`)}}},refreshAuth(){x(),++h,_&&clearTimeout(_),_=void 0;for(let e of[...v])e();let e=f;f=void 0,e?.close(1e3,`Authorization changed`);for(let e of u.values())e.snapshot=void 0,e.revision=-1,e.terminal=!1,b(e,`error`,new n(`AUTH_CHANGED`,`Authorization changed; cached value invalidated`));E()},dispose(){if(!d){d=!0,++h,_&&clearTimeout(_),_=void 0;for(let e of[...v])e();u.clear(),f?.close(1e3,`Disposed`),f=void 0}}}}export{i,s as n,c as r,u as t};