@absolutejs/absolute 0.19.0-beta.1049 → 0.19.0-beta.1050

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.
@@ -31726,6 +31726,7 @@ var init_fromType = __esm(() => {
31726
31726
  var exports_resolveAbsoluteConfig = {};
31727
31727
  __export(exports_resolveAbsoluteConfig, {
31728
31728
  resolveAbsoluteConfigState: () => resolveAbsoluteConfigState,
31729
+ readAbsoluteConfigValues: () => readAbsoluteConfigValues,
31729
31730
  parseConfigObject: () => parseConfigObject,
31730
31731
  findConfigPath: () => findConfigPath2,
31731
31732
  findConfigObject: () => findConfigObject
@@ -31825,6 +31826,10 @@ var CONFIG_CANDIDATES3, RUNTIME_FIELDS, findConfigPath2 = (cwd, override) => {
31825
31826
  current2[name] = result.value;
31826
31827
  }
31827
31828
  return { current: current2, opaqueKeys };
31829
+ }, readAbsoluteConfigValues = (cwd, override) => {
31830
+ const configPath = findConfigPath2(cwd, override);
31831
+ const { current: current2, opaqueKeys } = configPath ? readCurrent2(configPath) : { current: {}, opaqueKeys: [] };
31832
+ return { configPath, current: current2, opaqueKeys };
31828
31833
  }, resolveAbsoluteConfigState = (cwd, override) => {
31829
31834
  const configPath = findConfigPath2(cwd, override);
31830
31835
  const fields = introspectType(cwd, "BaseBuildConfig", RUNTIME_FIELDS);
@@ -32244,9 +32249,187 @@ var init_resolveAuthState = __esm(() => {
32244
32249
  SOURCE_FILE = /\.(ts|tsx|mts|cts|js|mjs)$/;
32245
32250
  });
32246
32251
 
32252
+ // src/cli/add/dependencies.ts
32253
+ var runBunAdd = (cwd, specs, dev) => {
32254
+ if (specs.length === 0)
32255
+ return true;
32256
+ const flags = dev ? ["--dev"] : [];
32257
+ const result = Bun.spawnSync(["bun", "add", ...flags, ...specs], {
32258
+ cwd,
32259
+ stderr: "inherit",
32260
+ stdout: "inherit"
32261
+ });
32262
+ return result.success;
32263
+ }, installPackages = (cwd, specs, dev = false) => runBunAdd(cwd, specs, dev);
32264
+ var init_dependencies = () => {};
32265
+
32266
+ // src/cli/integrations/catalog.ts
32267
+ var INTEGRATIONS, findIntegration = (id) => INTEGRATIONS.find((integration) => integration.id === id) ?? null;
32268
+ var init_catalog = __esm(() => {
32269
+ INTEGRATIONS = [
32270
+ {
32271
+ blurb: "Auto-derived OpenAPI docs + Scalar/Swagger UI from your route schemas.",
32272
+ id: "openapi",
32273
+ label: "OpenAPI",
32274
+ packages: [],
32275
+ wiring: { field: "openapi", kind: "config" }
32276
+ },
32277
+ {
32278
+ blurb: "Production distributed tracing via OpenTelemetry (complements `absolute inspect`).",
32279
+ id: "telemetry",
32280
+ label: "OpenTelemetry",
32281
+ packages: ["@elysiajs/opentelemetry"],
32282
+ wiring: { field: "telemetry", kind: "config" }
32283
+ },
32284
+ {
32285
+ blurb: "Cross-origin resource sharing (CORS) headers.",
32286
+ id: "cors",
32287
+ label: "@elysiajs/cors",
32288
+ packages: ["@elysiajs/cors"],
32289
+ wiring: {
32290
+ importLine: "import { cors } from '@elysiajs/cors';",
32291
+ kind: "use",
32292
+ useLine: ".use(cors())"
32293
+ }
32294
+ },
32295
+ {
32296
+ blurb: "Sign and verify your own JWTs \u2014 custom API/service tokens.",
32297
+ id: "jwt",
32298
+ label: "@elysiajs/jwt",
32299
+ note: "Not for user login. For authentication (OAuth2, SSO, MFA, passkeys, sessions) use the Auth panel + @absolutejs/auth.",
32300
+ packages: ["@elysiajs/jwt"],
32301
+ wiring: {
32302
+ importLine: "import { jwt } from '@elysiajs/jwt';",
32303
+ kind: "use",
32304
+ useLine: ".use(jwt({ name: 'jwt', secret: getEnv('JWT_SECRET') }))"
32305
+ }
32306
+ },
32307
+ {
32308
+ blurb: "Scheduled jobs on a cron pattern.",
32309
+ id: "cron",
32310
+ label: "@elysiajs/cron",
32311
+ packages: ["@elysiajs/cron"],
32312
+ wiring: {
32313
+ importLine: "import { cron } from '@elysiajs/cron';",
32314
+ kind: "use",
32315
+ useLine: ".use(cron({ name: 'heartbeat', pattern: '0 */6 * * *', run() {} }))"
32316
+ }
32317
+ }
32318
+ ];
32319
+ });
32320
+
32321
+ // src/cli/integrations/addPlugin.ts
32322
+ var exports_addPlugin = {};
32323
+ __export(exports_addPlugin, {
32324
+ resolveIntegrationsState: () => resolveIntegrationsState,
32325
+ addIntegration: () => addIntegration
32326
+ });
32327
+ import { existsSync as existsSync11, readFileSync as readFileSync15 } from "fs";
32328
+ import { join as join3 } from "path";
32329
+ var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), readPackageJson = (cwd) => {
32330
+ const path = join3(cwd, "package.json");
32331
+ if (!existsSync11(path))
32332
+ return null;
32333
+ try {
32334
+ const parsed = JSON.parse(readFileSync15(path, "utf-8"));
32335
+ return isRecord4(parsed) ? parsed : null;
32336
+ } catch {
32337
+ return null;
32338
+ }
32339
+ }, addGroupKeys = (group, names) => {
32340
+ if (!isRecord4(group))
32341
+ return;
32342
+ for (const name of Object.keys(group))
32343
+ names.add(name);
32344
+ }, declaredDeps = (cwd) => {
32345
+ const names = new Set;
32346
+ const pkg = readPackageJson(cwd);
32347
+ if (!pkg)
32348
+ return names;
32349
+ for (const field of ["dependencies", "devDependencies"]) {
32350
+ addGroupKeys(pkg[field], names);
32351
+ }
32352
+ return names;
32353
+ }, snippetFor = (wiring) => wiring.kind === "use" ? `${wiring.importLine}
32354
+ ${wiring.useLine}` : null, toItem = (meta, deps, current2) => {
32355
+ const installed = meta.packages.every((pkg) => deps.has(pkg));
32356
+ const enabled = meta.wiring.kind === "config" ? current2[meta.wiring.field] === true : installed;
32357
+ return {
32358
+ blurb: meta.blurb,
32359
+ enabled,
32360
+ id: meta.id,
32361
+ installed,
32362
+ kind: meta.wiring.kind,
32363
+ label: meta.label,
32364
+ note: meta.note ?? null,
32365
+ packages: meta.packages,
32366
+ wiringSnippet: snippetFor(meta.wiring)
32367
+ };
32368
+ }, resolveIntegrationsState = (cwd, override) => {
32369
+ const deps = declaredDeps(cwd);
32370
+ const { configPath, current: current2 } = readAbsoluteConfigValues(cwd, override);
32371
+ return {
32372
+ configPath,
32373
+ items: INTEGRATIONS.map((meta) => toItem(meta, deps, current2))
32374
+ };
32375
+ }, baseMessage = (meta, willInstall, installOk) => {
32376
+ if (!installOk) {
32377
+ return `Couldn't install ${meta.packages.join(", ")} \u2014 run \`bun add\` manually.`;
32378
+ }
32379
+ if (willInstall)
32380
+ return `Installed ${meta.label}.`;
32381
+ if (meta.packages.length > 0) {
32382
+ return `Skipped install (--no-install) for ${meta.label}.`;
32383
+ }
32384
+ return `${meta.label} is built in \u2014 no install needed.`;
32385
+ }, failure2 = (message) => ({
32386
+ installed: false,
32387
+ item: null,
32388
+ message,
32389
+ ok: false,
32390
+ wired: false,
32391
+ wiringSnippet: null
32392
+ }), addIntegration = (cwd, id, options = {}) => {
32393
+ const meta = findIntegration(id);
32394
+ if (!meta)
32395
+ return failure2(`Unknown integration "${id}".`);
32396
+ const install = options.install ?? true;
32397
+ const willInstall = install && meta.packages.length > 0;
32398
+ const installOk = willInstall ? installPackages(cwd, meta.packages) : true;
32399
+ const { configPath } = readAbsoluteConfigValues(cwd, options.override);
32400
+ let wired = false;
32401
+ let message = baseMessage(meta, willInstall, installOk);
32402
+ if (meta.wiring.kind === "config") {
32403
+ if (!configPath)
32404
+ return failure2("No absolute.config.ts found.");
32405
+ const edit = applyAbsoluteConfigEdit(configPath, {
32406
+ name: meta.wiring.field,
32407
+ value: true
32408
+ });
32409
+ wired = edit.ok;
32410
+ message = edit.ok ? `Enabled ${meta.label} in absolute.config.ts.` : `Installed, but couldn't edit config: ${edit.message}`;
32411
+ }
32412
+ const state = resolveIntegrationsState(cwd, options.override);
32413
+ const item = state.items.find((entry) => entry.id === id) ?? null;
32414
+ return {
32415
+ installed: item?.installed ?? installOk,
32416
+ item,
32417
+ message,
32418
+ ok: true,
32419
+ wired,
32420
+ wiringSnippet: item?.wiringSnippet ?? snippetFor(meta.wiring)
32421
+ };
32422
+ };
32423
+ var init_addPlugin = __esm(() => {
32424
+ init_dependencies();
32425
+ init_editAbsoluteConfig();
32426
+ init_resolveAbsoluteConfig();
32427
+ init_catalog();
32428
+ });
32429
+
32247
32430
  // src/cli/config/server.ts
32248
32431
  import { Elysia as Elysia2 } from "elysia";
32249
- import { existsSync as existsSync11, readFileSync as readFileSync15 } from "fs";
32432
+ import { existsSync as existsSync12, readFileSync as readFileSync16 } from "fs";
32250
32433
  import { resolve as resolve10 } from "path";
32251
32434
 
32252
32435
  // src/cli/config/page/PanelHost.tsx
@@ -34392,146 +34575,112 @@ var AbsoluteConfigPanel = ({
34392
34575
  // src/cli/config/integrations/IntegrationsPanel.tsx
34393
34576
  var import_react6 = __toESM(require_react(), 1);
34394
34577
  var jsx_dev_runtime6 = __toESM(require_jsx_dev_runtime(), 1);
34395
- var CONFIG_INTEGRATIONS = [
34396
- { field: "openapi", title: "OpenAPI" },
34397
- { field: "telemetry", title: "OpenTelemetry" }
34398
- ];
34399
- var MANUAL_PLUGINS = [
34400
- {
34401
- blurb: "Cross-origin resource sharing (CORS) headers.",
34402
- id: "@elysiajs/cors",
34403
- install: "bun add @elysiajs/cors",
34404
- wire: ".use(cors())"
34405
- },
34406
- {
34407
- blurb: "Sign and verify your own JWTs \u2014 custom API/service tokens.",
34408
- id: "@elysiajs/jwt",
34409
- install: "bun add @elysiajs/jwt",
34410
- note: "Not for user login. For authentication (OAuth2, SSO, MFA, passkeys, sessions) use the Auth panel + @absolutejs/auth.",
34411
- wire: ".use(jwt({ name: 'jwt', secret: getEnv('JWT_SECRET') }))"
34412
- },
34413
- {
34414
- blurb: "Scheduled jobs on a cron pattern.",
34415
- id: "@elysiajs/cron",
34416
- install: "bun add @elysiajs/cron",
34417
- wire: ".use(cron({ name: 'heartbeat', pattern: '0 */6 * * *', run() {} }))"
34418
- }
34419
- ];
34420
- var ManualPluginCard = ({ blurb, id, install, note, wire }) => /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("div", {
34578
+ var IntegrationCard = ({ busy, item, onAdd, onDisable }) => /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("div", {
34421
34579
  className: "rule",
34422
- children: /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("div", {
34423
- className: "rule-main",
34424
- children: [
34425
- /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("div", {
34426
- className: "rule-name-row",
34427
- children: /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("span", {
34428
- className: "rule-name",
34429
- children: id
34430
- }, undefined, false, undefined, this)
34431
- }, undefined, false, undefined, this),
34432
- /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("p", {
34433
- className: "rule-desc",
34434
- children: blurb
34435
- }, undefined, false, undefined, this),
34436
- note && /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("p", {
34437
- className: "intg-note",
34438
- children: note
34439
- }, undefined, false, undefined, this),
34440
- /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("pre", {
34441
- className: "intg-code",
34442
- children: install
34443
- }, undefined, false, undefined, this),
34444
- /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("pre", {
34445
- className: "intg-code",
34446
- children: wire
34447
- }, undefined, false, undefined, this)
34448
- ]
34449
- }, undefined, true, undefined, this)
34450
- }, undefined, false, undefined, this);
34451
- var FieldRow2 = ({ busy, field, isSet, onSave, title, value }) => {
34452
- const [draft, setDraft] = import_react6.useState(value);
34453
- return /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("div", {
34454
- className: "rule fe-block",
34455
- children: [
34456
- /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("div", {
34457
- className: "rule-main",
34458
- children: [
34459
- /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("div", {
34460
- className: "rule-name-row",
34461
- children: [
34462
- /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("span", {
34463
- className: "rule-name",
34464
- children: title
34465
- }, undefined, false, undefined, this),
34466
- /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("span", {
34467
- className: "badge dep",
34468
- children: field.name
34469
- }, undefined, false, undefined, this),
34470
- isSet && /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("span", {
34471
- className: "badge src",
34472
- children: "on"
34473
- }, undefined, false, undefined, this)
34474
- ]
34475
- }, undefined, true, undefined, this),
34476
- field.description !== "" && /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("p", {
34477
- className: "rule-desc",
34478
- children: field.description
34479
- }, undefined, false, undefined, this),
34480
- /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("div", {
34481
- className: "fe-root",
34482
- children: /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(FieldEditor, {
34483
- onChange: setDraft,
34484
- schema: field.schema,
34485
- value: draft
34580
+ children: [
34581
+ /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("div", {
34582
+ className: "rule-main",
34583
+ children: [
34584
+ /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("div", {
34585
+ className: "rule-name-row",
34586
+ children: [
34587
+ /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("span", {
34588
+ className: "rule-name",
34589
+ children: item.label
34590
+ }, undefined, false, undefined, this),
34591
+ /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("span", {
34592
+ className: "badge",
34593
+ children: item.kind === "config" ? "config" : "plugin"
34594
+ }, undefined, false, undefined, this),
34595
+ item.installed && item.packages.length > 0 && /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("span", {
34596
+ className: "badge dep",
34597
+ children: "installed"
34598
+ }, undefined, false, undefined, this),
34599
+ item.enabled && /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("span", {
34600
+ className: "badge src",
34601
+ children: item.kind === "config" ? "enabled" : "ready"
34486
34602
  }, undefined, false, undefined, this)
34487
- }, undefined, false, undefined, this)
34488
- ]
34489
- }, undefined, true, undefined, this),
34490
- /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("div", {
34491
- className: "rule-controls fe-actions",
34492
- children: [
34493
- /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("button", {
34494
- className: "ts-btn",
34495
- disabled: busy,
34496
- onClick: () => onSave(draft),
34497
- type: "button",
34498
- children: "save"
34499
- }, undefined, false, undefined, this),
34500
- isSet && /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("button", {
34501
- className: "ts-clear",
34502
- onClick: () => onSave(undefined, true),
34503
- type: "button",
34504
- children: "disable"
34505
- }, undefined, false, undefined, this)
34506
- ]
34507
- }, undefined, true, undefined, this)
34508
- ]
34509
- }, undefined, true, undefined, this);
34510
- };
34603
+ ]
34604
+ }, undefined, true, undefined, this),
34605
+ /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("p", {
34606
+ className: "rule-desc",
34607
+ children: item.blurb
34608
+ }, undefined, false, undefined, this),
34609
+ item.note && /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("p", {
34610
+ className: "intg-note",
34611
+ children: item.note
34612
+ }, undefined, false, undefined, this),
34613
+ item.kind === "use" && item.wiringSnippet && /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("pre", {
34614
+ className: "intg-code",
34615
+ children: item.wiringSnippet
34616
+ }, undefined, false, undefined, this)
34617
+ ]
34618
+ }, undefined, true, undefined, this),
34619
+ /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("div", {
34620
+ className: "rule-controls fe-actions",
34621
+ children: [
34622
+ /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("button", {
34623
+ className: "ts-btn",
34624
+ disabled: busy,
34625
+ onClick: onAdd,
34626
+ type: "button",
34627
+ children: item.kind === "config" ? "enable" : "install"
34628
+ }, undefined, false, undefined, this),
34629
+ item.kind === "config" && item.enabled && /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("button", {
34630
+ className: "ts-clear",
34631
+ disabled: busy,
34632
+ onClick: onDisable,
34633
+ type: "button",
34634
+ children: "disable"
34635
+ }, undefined, false, undefined, this)
34636
+ ]
34637
+ }, undefined, true, undefined, this)
34638
+ ]
34639
+ }, undefined, true, undefined, this);
34511
34640
  var IntegrationsPanel = ({
34512
34641
  state: initial
34513
34642
  }) => {
34514
- const [state, setState] = import_react6.useState(initial);
34643
+ const [items, setItems] = import_react6.useState(initial.items);
34515
34644
  const [busy, setBusy] = import_react6.useState(null);
34516
34645
  const [notice, setNotice] = import_react6.useState(null);
34517
- const save = (name) => async (value, remove) => {
34518
- setBusy(name);
34646
+ const replaceItem = (next) => setItems((prev) => prev.map((entry) => entry.id === next.id ? next : entry));
34647
+ const add = (id) => async () => {
34648
+ setBusy(id);
34519
34649
  setNotice(null);
34520
34650
  try {
34521
- const response = await fetch("/api/absolute", {
34522
- body: JSON.stringify({ name, remove, value }),
34651
+ const response = await fetch("/api/integrations/add", {
34652
+ body: JSON.stringify({ id }),
34523
34653
  headers: { "Content-Type": "application/json" },
34524
34654
  method: "POST"
34525
34655
  });
34526
34656
  const result = await response.json();
34527
- if (result.ok && result.state) {
34528
- setState(result.state);
34657
+ if (result.ok && result.item) {
34658
+ replaceItem(result.item);
34529
34659
  setNotice({ kind: "ok", text: result.message });
34530
34660
  } else {
34531
- setNotice({
34532
- kind: "err",
34533
- text: result.message ?? "Update failed"
34534
- });
34661
+ setNotice({ kind: "err", text: result.message });
34662
+ }
34663
+ } catch (error) {
34664
+ setNotice({ kind: "err", text: String(error) });
34665
+ } finally {
34666
+ setBusy(null);
34667
+ }
34668
+ };
34669
+ const disable = (item) => async () => {
34670
+ setBusy(item.id);
34671
+ setNotice(null);
34672
+ try {
34673
+ const response = await fetch("/api/absolute", {
34674
+ body: JSON.stringify({ name: item.id, remove: true }),
34675
+ headers: { "Content-Type": "application/json" },
34676
+ method: "POST"
34677
+ });
34678
+ const result = await response.json();
34679
+ if (result.ok) {
34680
+ replaceItem({ ...item, enabled: false });
34681
+ setNotice({ kind: "ok", text: `Disabled ${item.label}.` });
34682
+ } else {
34683
+ setNotice({ kind: "err", text: result.message });
34535
34684
  }
34536
34685
  } catch (error) {
34537
34686
  setNotice({ kind: "err", text: String(error) });
@@ -34539,10 +34688,6 @@ var IntegrationsPanel = ({
34539
34688
  setBusy(null);
34540
34689
  }
34541
34690
  };
34542
- const rows = CONFIG_INTEGRATIONS.map((entry) => ({
34543
- field: state.fields.find((candidate) => candidate.name === entry.field),
34544
- title: entry.title
34545
- })).filter((row) => Boolean(row.field));
34546
34691
  return /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("div", {
34547
34692
  className: "shell",
34548
34693
  children: [
@@ -34566,63 +34711,45 @@ var IntegrationsPanel = ({
34566
34711
  /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("span", {
34567
34712
  className: "dot"
34568
34713
  }, undefined, false, undefined, this),
34569
- "official Elysia plugins, wired the AbsoluteJS way"
34714
+ "official Elysia plugins \u2014 install & wire from here or",
34715
+ " ",
34716
+ /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("code", {
34717
+ children: "absolute add"
34718
+ }, undefined, false, undefined, this)
34570
34719
  ]
34571
34720
  }, undefined, true, undefined, this)
34572
34721
  ]
34573
34722
  }, undefined, true, undefined, this)
34574
34723
  }, undefined, false, undefined, this),
34575
34724
  /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("main", {
34576
- children: [
34577
- /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("section", {
34578
- className: "section",
34579
- children: [
34580
- /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("div", {
34581
- className: "section-head",
34582
- children: [
34583
- /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("h2", {
34584
- className: "section-title",
34585
- children: "Config-driven"
34586
- }, undefined, false, undefined, this),
34587
- /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("span", {
34588
- className: "section-files",
34589
- children: "toggle in absolute.config.ts"
34590
- }, undefined, false, undefined, this)
34591
- ]
34592
- }, undefined, true, undefined, this),
34593
- rows.map((row) => /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(FieldRow2, {
34594
- busy: busy === row.field.name,
34595
- field: row.field,
34596
- isSet: Object.prototype.hasOwnProperty.call(state.current, row.field.name),
34597
- onSave: save(row.field.name),
34598
- title: row.title,
34599
- value: state.current[row.field.name]
34600
- }, row.field.name, false, undefined, this))
34601
- ]
34602
- }, undefined, true, undefined, this),
34603
- /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("section", {
34604
- className: "section",
34605
- children: [
34606
- /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("div", {
34607
- className: "section-head",
34608
- children: [
34609
- /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("h2", {
34610
- className: "section-title",
34611
- children: "More official plugins"
34612
- }, undefined, false, undefined, this),
34613
- /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("span", {
34614
- className: "section-files",
34615
- children: "install + wire"
34616
- }, undefined, false, undefined, this)
34617
- ]
34618
- }, undefined, true, undefined, this),
34619
- MANUAL_PLUGINS.map((plugin) => /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(ManualPluginCard, {
34620
- ...plugin
34621
- }, plugin.id, false, undefined, this))
34622
- ]
34623
- }, undefined, true, undefined, this)
34624
- ]
34625
- }, undefined, true, undefined, this),
34725
+ children: /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("section", {
34726
+ className: "section",
34727
+ children: [
34728
+ /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("div", {
34729
+ className: "section-head",
34730
+ children: [
34731
+ /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("h2", {
34732
+ className: "section-title",
34733
+ children: "Official plugins"
34734
+ }, undefined, false, undefined, this),
34735
+ /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("span", {
34736
+ className: "section-files",
34737
+ children: [
34738
+ items.filter((item) => item.enabled).length,
34739
+ " enabled"
34740
+ ]
34741
+ }, undefined, true, undefined, this)
34742
+ ]
34743
+ }, undefined, true, undefined, this),
34744
+ items.map((item) => /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(IntegrationCard, {
34745
+ busy: busy === item.id,
34746
+ item,
34747
+ onAdd: add(item.id),
34748
+ onDisable: disable(item)
34749
+ }, item.id, false, undefined, this))
34750
+ ]
34751
+ }, undefined, true, undefined, this)
34752
+ }, undefined, false, undefined, this),
34626
34753
  notice && /* @__PURE__ */ jsx_dev_runtime6.jsxDEV("div", {
34627
34754
  className: `toast ${notice.kind}`,
34628
34755
  children: [
@@ -34948,7 +35075,7 @@ var AuthPanel = ({ state }) => {
34948
35075
  var import_react7 = __toESM(require_react(), 1);
34949
35076
  var jsx_dev_runtime8 = __toESM(require_jsx_dev_runtime(), 1);
34950
35077
  var matchesQuery5 = (query, text) => query === "" || text.toLowerCase().includes(query.toLowerCase());
34951
- var FieldRow3 = ({ field, isSet, onSave, value }) => {
35078
+ var FieldRow2 = ({ field, isSet, onSave, value }) => {
34952
35079
  const [draft, setDraft] = import_react7.useState(value);
34953
35080
  return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV("div", {
34954
35081
  className: "rule fe-block",
@@ -35242,7 +35369,7 @@ var PackageJsonPanel = ({ state: initial }) => {
35242
35369
  }, undefined, true, undefined, this)
35243
35370
  ]
35244
35371
  }, undefined, true, undefined, this),
35245
- fields.map((field) => /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(FieldRow3, {
35372
+ fields.map((field) => /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(FieldRow2, {
35246
35373
  field,
35247
35374
  isSet: Object.prototype.hasOwnProperty.call(state.current, field.name),
35248
35375
  onSave: saveField(field.name),
@@ -35271,7 +35398,7 @@ var ENDPOINTS = {
35271
35398
  absolute: "/api/absolute",
35272
35399
  auth: "/api/auth",
35273
35400
  eslint: "/api/rules",
35274
- integrations: "/api/absolute",
35401
+ integrations: "/api/integrations",
35275
35402
  package: "/api/package",
35276
35403
  prettier: "/api/prettier",
35277
35404
  tsconfig: "/api/tsconfig"
@@ -35320,6 +35447,7 @@ var isTsState = (value) => isRecord(value) && Array.isArray(value.options);
35320
35447
  var isPrettierState = (value) => isRecord(value) && Array.isArray(value.options);
35321
35448
  var isAbsoluteState = (value) => isRecord(value) && Array.isArray(value.fields);
35322
35449
  var isAuthState = (value) => isRecord(value) && Array.isArray(value.features);
35450
+ var isIntegrationsState = (value) => isRecord(value) && Array.isArray(value.items);
35323
35451
  var isPackageState = (value) => isRecord(value) && Array.isArray(value.scripts) && Array.isArray(value.fields);
35324
35452
  var renderPanel = (panel, data) => {
35325
35453
  if (panel === "eslint") {
@@ -35355,11 +35483,11 @@ var renderPanel = (panel, data) => {
35355
35483
  }, undefined, false, undefined, this);
35356
35484
  }
35357
35485
  if (panel === "integrations") {
35358
- return isAbsoluteState(data) && data.configPath ? /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(IntegrationsPanel, {
35486
+ return isIntegrationsState(data) ? /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(IntegrationsPanel, {
35359
35487
  state: data
35360
35488
  }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Message, {
35361
- body: "No absolute.config.ts was found. Run with --config <path> to point at one.",
35362
- title: "No absolute.config"
35489
+ body: "Couldn't read the integrations status for this project.",
35490
+ title: "Integrations unavailable"
35363
35491
  }, undefined, false, undefined, this);
35364
35492
  }
35365
35493
  if (panel === "auth") {
@@ -35974,6 +36102,9 @@ var CONFIG_CSS = `
35974
36102
  .auth-link { font-size: 12px; color: var(--accent); text-decoration: none; }
35975
36103
  .auth-link:hover { text-decoration: underline; }
35976
36104
 
36105
+ /* ---- grouped sidebar ---- */
36106
+ .cfg-group + .cfg-group { margin-top: 16px; }
36107
+
35977
36108
  @media (max-width: 720px) {
35978
36109
  .cfg { flex-direction: column; }
35979
36110
  .cfg-nav {
@@ -35991,45 +36122,52 @@ var CONFIG_CSS = `
35991
36122
  var CONFIG_PANELS = [
35992
36123
  {
35993
36124
  blurb: "Framework config (defineConfig)",
36125
+ group: "Project",
35994
36126
  id: "absolute",
35995
36127
  label: "absolute.config",
35996
36128
  status: "ready"
35997
36129
  },
35998
- {
35999
- blurb: "Official Elysia plugins",
36000
- id: "integrations",
36001
- label: "Integrations",
36002
- status: "ready"
36003
- },
36004
- {
36005
- blurb: "@absolutejs/auth setup",
36006
- id: "auth",
36007
- label: "Auth",
36008
- status: "ready"
36009
- },
36010
36130
  {
36011
36131
  blurb: "Scripts & metadata",
36132
+ group: "Project",
36012
36133
  id: "package",
36013
36134
  label: "package.json",
36014
36135
  status: "ready"
36015
36136
  },
36016
36137
  {
36017
36138
  blurb: "Lint rules & severities",
36139
+ group: "Project",
36018
36140
  id: "eslint",
36019
36141
  label: "ESLint",
36020
36142
  status: "ready"
36021
36143
  },
36022
36144
  {
36023
36145
  blurb: "TypeScript compiler options",
36146
+ group: "Project",
36024
36147
  id: "tsconfig",
36025
36148
  label: "tsconfig",
36026
36149
  status: "ready"
36027
36150
  },
36028
36151
  {
36029
36152
  blurb: "Formatting options",
36153
+ group: "Project",
36030
36154
  id: "prettier",
36031
36155
  label: "Prettier",
36032
36156
  status: "ready"
36157
+ },
36158
+ {
36159
+ blurb: "Official Elysia plugins",
36160
+ group: "Integrations",
36161
+ id: "integrations",
36162
+ label: "Integrations",
36163
+ status: "ready"
36164
+ },
36165
+ {
36166
+ blurb: "@absolutejs/auth setup",
36167
+ group: "Integrations",
36168
+ id: "auth",
36169
+ label: "Auth",
36170
+ status: "ready"
36033
36171
  }
36034
36172
  ];
36035
36173
  var DEFAULT_PANEL = "absolute";
@@ -36061,6 +36199,35 @@ var NavItem = ({ active, panel }) => /* @__PURE__ */ jsx_dev_runtime10.jsxDEV("a
36061
36199
  }, undefined, false, undefined, this)
36062
36200
  ]
36063
36201
  }, undefined, true, undefined, this);
36202
+ var addToGroup = (entry, order, byGroup) => {
36203
+ const list = byGroup.get(entry.group);
36204
+ if (list) {
36205
+ list.push(entry);
36206
+ return;
36207
+ }
36208
+ order.push(entry.group);
36209
+ byGroup.set(entry.group, [entry]);
36210
+ };
36211
+ var groupedPanels = () => {
36212
+ const order = [];
36213
+ const byGroup = new Map;
36214
+ for (const entry of CONFIG_PANELS)
36215
+ addToGroup(entry, order, byGroup);
36216
+ return order.map((label) => ({ items: byGroup.get(label) ?? [], label }));
36217
+ };
36218
+ var NavGroup = ({ active, group }) => /* @__PURE__ */ jsx_dev_runtime10.jsxDEV("div", {
36219
+ className: "cfg-group",
36220
+ children: [
36221
+ /* @__PURE__ */ jsx_dev_runtime10.jsxDEV("div", {
36222
+ className: "cfg-rail-label",
36223
+ children: group.label
36224
+ }, undefined, false, undefined, this),
36225
+ group.items.map((entry) => /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(NavItem, {
36226
+ active: entry.id === active,
36227
+ panel: entry
36228
+ }, entry.id, false, undefined, this))
36229
+ ]
36230
+ }, undefined, true, undefined, this);
36064
36231
  var ConfigShell = ({ panel }) => {
36065
36232
  const active = CONFIG_PANELS.find((entry) => entry.id === panel);
36066
36233
  const activeLabel = active?.label ?? "Config";
@@ -36126,17 +36293,11 @@ var ConfigShell = ({ panel }) => {
36126
36293
  }, undefined, true, undefined, this),
36127
36294
  /* @__PURE__ */ jsx_dev_runtime10.jsxDEV("nav", {
36128
36295
  className: "cfg-panels",
36129
- children: [
36130
- /* @__PURE__ */ jsx_dev_runtime10.jsxDEV("div", {
36131
- className: "cfg-rail-label",
36132
- children: "Panels"
36133
- }, undefined, false, undefined, this),
36134
- CONFIG_PANELS.map((entry) => /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(NavItem, {
36135
- active: entry.id === panel,
36136
- panel: entry
36137
- }, entry.id, false, undefined, this))
36138
- ]
36139
- }, undefined, true, undefined, this)
36296
+ children: groupedPanels().map((group) => /* @__PURE__ */ jsx_dev_runtime10.jsxDEV(NavGroup, {
36297
+ active: panel,
36298
+ group
36299
+ }, group.label, false, undefined, this))
36300
+ }, undefined, false, undefined, this)
36140
36301
  ]
36141
36302
  }, undefined, true, undefined, this),
36142
36303
  /* @__PURE__ */ jsx_dev_runtime10.jsxDEV("main", {
@@ -37761,6 +37922,7 @@ var packageOps = () => Promise.all([
37761
37922
  Promise.resolve().then(() => (init_resolvePackageJson(), exports_resolvePackageJson))
37762
37923
  ]);
37763
37924
  var authOps = () => Promise.resolve().then(() => (init_resolveAuthState(), exports_resolveAuthState));
37925
+ var integrationsOps = () => Promise.resolve().then(() => (init_addPlugin(), exports_addPlugin));
37764
37926
  var CLIENT_ROUTE = "/config-client.js";
37765
37927
  var readVersion = () => {
37766
37928
  for (const candidate of [
@@ -37768,7 +37930,7 @@ var readVersion = () => {
37768
37930
  resolve10(import.meta.dir, "..", "..", "..", "..", "package.json")
37769
37931
  ]) {
37770
37932
  try {
37771
- const pkg = JSON.parse(readFileSync15(candidate, "utf-8"));
37933
+ const pkg = JSON.parse(readFileSync16(candidate, "utf-8"));
37772
37934
  if (typeof pkg?.version === "string")
37773
37935
  return pkg.version;
37774
37936
  } catch {}
@@ -37812,7 +37974,7 @@ var buildClientBundle = async () => {
37812
37974
  var getClientBundle = async () => {
37813
37975
  if (cachedClientBundle !== null)
37814
37976
  return cachedClientBundle;
37815
- cachedClientBundle = existsSync11(distClientBundle) ? readFileSync15(distClientBundle, "utf-8") : await buildClientBundle();
37977
+ cachedClientBundle = existsSync12(distClientBundle) ? readFileSync16(distClientBundle, "utf-8") : await buildClientBundle();
37816
37978
  return cachedClientBundle;
37817
37979
  };
37818
37980
  var renderShell = (panel) => handleReactPageRequest({
@@ -37962,6 +38124,21 @@ var handleAbsoluteEdit = async (cwd, body, override) => {
37962
38124
  };
37963
38125
  return result;
37964
38126
  };
38127
+ var handleIntegrationAdd = async (cwd, body, override) => {
38128
+ const { addIntegration: addIntegration2 } = await integrationsOps();
38129
+ if (!isRecord(body) || typeof body.id !== "string") {
38130
+ const invalid = {
38131
+ installed: false,
38132
+ item: null,
38133
+ message: "Missing integration id.",
38134
+ ok: false,
38135
+ wired: false,
38136
+ wiringSnippet: null
38137
+ };
38138
+ return invalid;
38139
+ }
38140
+ return addIntegration2(cwd, body.id, { install: true, override });
38141
+ };
37965
38142
  var packageError = (message) => {
37966
38143
  const invalid = { message, ok: false, state: null };
37967
38144
  return new Response(JSON.stringify(invalid), {
@@ -38040,7 +38217,10 @@ var launchConfig = async (args, cwd = process.cwd()) => {
38040
38217
  }).post("/api/prettier", ({ body }) => handlePrettierEdit(cwd, body)).get("/api/absolute", async () => {
38041
38218
  const [, { resolveAbsoluteConfigState: resolveAbsoluteConfigState2 }] = await absoluteOps();
38042
38219
  return resolveAbsoluteConfigState2(cwd, configOverride);
38043
- }).post("/api/absolute", ({ body }) => handleAbsoluteEdit(cwd, body, configOverride)).get("/api/auth", async () => {
38220
+ }).post("/api/absolute", ({ body }) => handleAbsoluteEdit(cwd, body, configOverride)).get("/api/integrations", async () => {
38221
+ const { resolveIntegrationsState: resolveIntegrationsState2 } = await integrationsOps();
38222
+ return resolveIntegrationsState2(cwd, configOverride);
38223
+ }).post("/api/integrations/add", ({ body }) => handleIntegrationAdd(cwd, body, configOverride)).get("/api/auth", async () => {
38044
38224
  const { resolveAuthState: resolveAuthState2 } = await authOps();
38045
38225
  return resolveAuthState2(cwd);
38046
38226
  }).get("/api/package", async () => {