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

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.
@@ -31536,11 +31536,12 @@ var VIRTUAL_NAME = "__absolute_type_introspect__.ts", MAX_DEPTH2 = 6, isFramewor
31536
31536
  skipLibCheck: true,
31537
31537
  types: []
31538
31538
  };
31539
- }, SCHEMA_VERSION = 1, frameworkVersion = (cwd) => {
31540
- for (const candidate of [
31539
+ }, SCHEMA_VERSION = 1, packageVersion = (cwd, specifier) => {
31540
+ const candidates = specifier === "@absolutejs/absolute" ? [
31541
31541
  resolve6(cwd, "node_modules", "@absolutejs", "absolute", "package.json"),
31542
31542
  resolve6(cwd, "package.json")
31543
- ]) {
31543
+ ] : [resolve6(cwd, "node_modules", ...specifier.split("/"), "package.json")];
31544
+ for (const candidate of candidates) {
31544
31545
  try {
31545
31546
  const { version: version3 } = JSON.parse(readFileSync9(candidate, "utf-8"));
31546
31547
  if (typeof version3 === "string")
@@ -31548,8 +31549,8 @@ var VIRTUAL_NAME = "__absolute_type_introspect__.ts", MAX_DEPTH2 = 6, isFramewor
31548
31549
  } catch {}
31549
31550
  }
31550
31551
  return "unknown";
31551
- }, cacheSignature = (cwd, typeName2, local) => {
31552
- let signature = `${SCHEMA_VERSION}:${frameworkVersion(cwd)}`;
31552
+ }, cacheSignature = (cwd, typeName2, local, specifier) => {
31553
+ let signature = `${SCHEMA_VERSION}:${specifier}:${packageVersion(cwd, specifier)}`;
31553
31554
  if (local) {
31554
31555
  const file = typeName2 === "PackageJson" ? "packageJson.ts" : "build.ts";
31555
31556
  try {
@@ -31557,20 +31558,23 @@ var VIRTUAL_NAME = "__absolute_type_introspect__.ts", MAX_DEPTH2 = 6, isFramewor
31557
31558
  } catch {}
31558
31559
  }
31559
31560
  return signature;
31560
- }, cacheFile = (cwd, typeName2) => resolve6(cwd, ".absolutejs", "config-schema", `${typeName2}.json`), readDiskCache = (cwd, typeName2, signature) => {
31561
+ }, cacheSlug = (specifier) => specifier.replace("@", "").split("/").join("-"), cacheFile = (cwd, typeName2, specifier) => {
31562
+ const name = specifier === "@absolutejs/absolute" ? typeName2 : `${typeName2}.${cacheSlug(specifier)}`;
31563
+ return resolve6(cwd, ".absolutejs", "config-schema", `${name}.json`);
31564
+ }, readDiskCache = (cwd, typeName2, signature, specifier) => {
31561
31565
  try {
31562
- const cached = JSON.parse(readFileSync9(cacheFile(cwd, typeName2), "utf-8"));
31566
+ const cached = JSON.parse(readFileSync9(cacheFile(cwd, typeName2, specifier), "utf-8"));
31563
31567
  if (cached?.signature === signature && Array.isArray(cached.fields)) {
31564
31568
  return cached.fields;
31565
31569
  }
31566
31570
  } catch {}
31567
31571
  return null;
31568
- }, writeDiskCache = (cwd, typeName2, signature, fields) => {
31572
+ }, writeDiskCache = (cwd, typeName2, signature, fields, specifier) => {
31569
31573
  try {
31570
31574
  mkdirSync2(resolve6(cwd, ".absolutejs", "config-schema"), {
31571
31575
  recursive: true
31572
31576
  });
31573
- writeFileSync4(cacheFile(cwd, typeName2), JSON.stringify({ fields, signature }));
31577
+ writeFileSync4(cacheFile(cwd, typeName2, specifier), JSON.stringify({ fields, signature }));
31574
31578
  } catch {}
31575
31579
  }, docOf = (symbol, checker) => ts2.displayPartsToString(symbol.getDocumentationComment(checker)).trim(), typeOfSymbol = (symbol, checker) => {
31576
31580
  const declaration = symbol.valueDeclaration ?? symbol.declarations?.[0];
@@ -31692,31 +31696,32 @@ export { value };
31692
31696
  }
31693
31697
  });
31694
31698
  return nodes.sort((left, right) => left.name.localeCompare(right.name));
31695
- }, cache, introspectType = (cwd, typeName2, exclude = new Set) => {
31696
- const cached = cache.get(typeName2);
31699
+ }, cache, introspectType = (cwd, typeName2, exclude = new Set, specifier = "@absolutejs/absolute") => {
31700
+ const cacheKey = `${specifier}:${typeName2}`;
31701
+ const cached = cache.get(cacheKey);
31697
31702
  if (cached)
31698
31703
  return cached;
31699
- const local = isFrameworkRepo(cwd) && existsSync7(resolve6(cwd, "types/index.ts"));
31700
- const signature = cacheSignature(cwd, typeName2, local);
31701
- const fromDisk = readDiskCache(cwd, typeName2, signature);
31704
+ const local = specifier === "@absolutejs/absolute" && isFrameworkRepo(cwd) && existsSync7(resolve6(cwd, "types/index.ts"));
31705
+ const signature = cacheSignature(cwd, typeName2, local, specifier);
31706
+ const fromDisk = readDiskCache(cwd, typeName2, signature, specifier);
31702
31707
  if (fromDisk) {
31703
- cache.set(typeName2, fromDisk);
31708
+ cache.set(cacheKey, fromDisk);
31704
31709
  return fromDisk;
31705
31710
  }
31706
31711
  const options = compilerOptionsFor(cwd);
31707
- const specifiers = local ? ["@absolutejs/absolute", "./types"] : ["@absolutejs/absolute"];
31708
- for (const specifier of specifiers) {
31712
+ const specifiers = local ? [specifier, "./types"] : [specifier];
31713
+ for (const candidate of specifiers) {
31709
31714
  try {
31710
- const nodes = introspectFrom(cwd, specifier, typeName2, options, exclude);
31715
+ const nodes = introspectFrom(cwd, candidate, typeName2, options, exclude);
31711
31716
  if (nodes.length > 0) {
31712
- cache.set(typeName2, nodes);
31713
- writeDiskCache(cwd, typeName2, signature, nodes);
31717
+ cache.set(cacheKey, nodes);
31718
+ writeDiskCache(cwd, typeName2, signature, nodes, specifier);
31714
31719
  return nodes;
31715
31720
  }
31716
31721
  } catch {}
31717
31722
  }
31718
- cache.set(typeName2, []);
31719
- return cache.get(typeName2) ?? [];
31723
+ cache.set(cacheKey, []);
31724
+ return cache.get(cacheKey) ?? [];
31720
31725
  };
31721
31726
  var init_fromType = __esm(() => {
31722
31727
  cache = new Map;
@@ -32070,19 +32075,351 @@ var init_resolvePackageJson = __esm(() => {
32070
32075
  init_fromType();
32071
32076
  });
32072
32077
 
32078
+ // src/cli/config/auth/authScaffolds.ts
32079
+ var TODO = (message) => `async () => { throw new Error("TODO: ${message}"); }`, AUTH_SCAFFOLDS, isScaffoldableFeature = (id) => (id in AUTH_SCAFFOLDS);
32080
+ var init_authScaffolds = __esm(() => {
32081
+ AUTH_SCAFFOLDS = {
32082
+ audit: {
32083
+ configKey: "audit",
32084
+ exportName: "auditConfig",
32085
+ fields: [
32086
+ { name: "auditStore", value: "createInMemoryAuditSink()" },
32087
+ { name: "getUserId", value: "(user) => user.sub" }
32088
+ ],
32089
+ generic: true,
32090
+ imports: ["createInMemoryAuditSink"],
32091
+ note: "Swap the in-memory sink for a durable store (append-only table); add `onAuditEvent` to forward to a SIEM.",
32092
+ packages: [],
32093
+ typeName: "AuditConfig"
32094
+ },
32095
+ authorization: {
32096
+ configKey: "authorization",
32097
+ exportName: "authorizationConfig",
32098
+ fields: [
32099
+ {
32100
+ name: "hasPermission",
32101
+ value: '({ user, permission, organizationId }) => { throw new Error("TODO: decide if user has permission"); }'
32102
+ }
32103
+ ],
32104
+ generic: true,
32105
+ imports: [],
32106
+ note: "Pair with `createMembershipPermissionResolver` (from @absolutejs/auth) when you also use organizations + roles.",
32107
+ packages: [],
32108
+ typeName: "AuthorizationConfig"
32109
+ },
32110
+ compliance: {
32111
+ configKey: "compliance",
32112
+ exportName: "complianceConfig",
32113
+ fields: [
32114
+ { name: "deleteUserData", value: TODO("erase or anonymize the user in your stores") },
32115
+ { name: "exportUserData", value: "async ({ user }) => ({})" },
32116
+ { name: "getUserId", value: "(user) => user.sub" }
32117
+ ],
32118
+ generic: true,
32119
+ imports: [],
32120
+ note: "exportUserData must return everything you hold on the user; getUserId lets the route revoke sibling sessions.",
32121
+ packages: [],
32122
+ typeName: "ComplianceConfig"
32123
+ },
32124
+ credentials: {
32125
+ configKey: "credentials",
32126
+ exportName: "credentialsConfig",
32127
+ fields: [
32128
+ { name: "credentialStore", value: "createInMemoryCredentialStore()" },
32129
+ { name: "getUserByEmail", value: TODO("look up a user by email") },
32130
+ { name: "onCreateCredentialUser", value: TODO("create and return the user for identity.email") },
32131
+ { name: "onSendEmail", value: TODO("send the verification / reset email containing message.token") },
32132
+ { name: "requireEmailVerification", value: "false" }
32133
+ ],
32134
+ generic: true,
32135
+ imports: ["createInMemoryCredentialStore"],
32136
+ note: "Swap the in-memory credential store for a real one (it holds password hashes + tokens).",
32137
+ packages: [],
32138
+ typeName: "CredentialsConfig"
32139
+ },
32140
+ lockout: {
32141
+ configKey: "lockout",
32142
+ exportName: "lockoutConfig",
32143
+ fields: [
32144
+ { name: "lockoutStore", value: "createInMemoryLockoutStore()" },
32145
+ { name: "maxAttempts", value: "5" },
32146
+ { name: "windowMs", value: "15 * 60 * 1000" }
32147
+ ],
32148
+ generic: false,
32149
+ imports: ["createInMemoryLockoutStore"],
32150
+ note: "Throttles the credential login route; pair with the `credentials` block.",
32151
+ packages: [],
32152
+ typeName: "LockoutConfig"
32153
+ },
32154
+ mfa: {
32155
+ configKey: "mfa",
32156
+ exportName: "mfaConfig",
32157
+ fields: [
32158
+ { name: "mfaStore", value: "createInMemoryMfaStore()" },
32159
+ { name: "getUserId", value: "(user) => user.sub" },
32160
+ { name: "getChallengeUser", value: TODO("resolve the parked challenge identity to a user") },
32161
+ { name: "issuer", value: "'YourApp'" }
32162
+ ],
32163
+ generic: true,
32164
+ imports: ["createInMemoryMfaStore"],
32165
+ note: "Set `encryptionKey` (base64url) to encrypt the TOTP secret at rest in any real deployment.",
32166
+ packages: [],
32167
+ typeName: "MfaConfig"
32168
+ },
32169
+ organizations: {
32170
+ configKey: "organizations",
32171
+ exportName: "organizationsConfig",
32172
+ fields: [
32173
+ { name: "getUserId", value: "(user) => user.sub" },
32174
+ { name: "organizationStore", value: "createInMemoryOrganizationStore()" }
32175
+ ],
32176
+ generic: true,
32177
+ imports: ["createInMemoryOrganizationStore"],
32178
+ note: "Add `onSendInvitation` to deliver invite emails; otherwise the plaintext token is returned from the invite route.",
32179
+ packages: [],
32180
+ typeName: "OrganizationsConfig"
32181
+ },
32182
+ passwordless: {
32183
+ configKey: "passwordless",
32184
+ exportName: "passwordlessConfig",
32185
+ fields: [
32186
+ { name: "passwordlessTokenStore", value: "createInMemoryPasswordlessTokenStore()" },
32187
+ { name: "getUserByEmail", value: TODO("look up a user by email") },
32188
+ { name: "onSendMagicLink", value: TODO("email the magic link containing message.token") }
32189
+ ],
32190
+ generic: true,
32191
+ imports: ["createInMemoryPasswordlessTokenStore"],
32192
+ note: "The magic-link flow mounts when `onSendMagicLink` is set; add `onSendOtp` to also mount the email/SMS OTP flow.",
32193
+ packages: [],
32194
+ typeName: "PasswordlessConfig"
32195
+ },
32196
+ portal: {
32197
+ configKey: "portal",
32198
+ exportName: "portalConfig",
32199
+ fields: [
32200
+ { name: "setupSessionStore", value: "createInMemorySetupSessionStore()" }
32201
+ ],
32202
+ generic: false,
32203
+ imports: ["createInMemorySetupSessionStore"],
32204
+ note: "Pass the same `ssoConnectionStore` / `scimTokenStore` your sso / scim blocks use so portal edits take effect live.",
32205
+ packages: [],
32206
+ typeName: "PortalConfig"
32207
+ },
32208
+ roles: {
32209
+ configKey: "roles",
32210
+ exportName: "rolesConfig",
32211
+ fields: [
32212
+ { name: "getUserId", value: "(user) => user.sub" },
32213
+ { name: "organizationStore", value: "createInMemoryOrganizationStore()" },
32214
+ { name: "roleStore", value: "createInMemoryRoleStore()" }
32215
+ ],
32216
+ generic: true,
32217
+ imports: ["createInMemoryOrganizationStore", "createInMemoryRoleStore"],
32218
+ note: "Builds on the `organizations` block \u2014 reuse the same organizationStore instance there.",
32219
+ packages: [],
32220
+ typeName: "RolesConfig"
32221
+ },
32222
+ scim: {
32223
+ configKey: "scim",
32224
+ exportName: "scimConfig",
32225
+ fields: [
32226
+ { name: "scimTokenStore", value: "createInMemoryScimTokenStore()" },
32227
+ { name: "getScimUser", value: TODO("return the SCIM user for { id, organizationId }") },
32228
+ { name: "listScimUsers", value: "async () => []" },
32229
+ { name: "onScimUserCreate", value: TODO("create and return the SCIM user") },
32230
+ { name: "onScimUserDeactivate", value: TODO("deprovision the user (hard-delete or deactivate)") },
32231
+ { name: "onScimUserReplace", value: TODO("replace and return the SCIM user (undefined if unknown)") }
32232
+ ],
32233
+ generic: false,
32234
+ imports: ["createInMemoryScimTokenStore"],
32235
+ note: "Group hooks are optional (the /Groups routes 501 without them). Mint per-org tokens with `createScimToken`.",
32236
+ packages: [],
32237
+ typeName: "ScimConfig"
32238
+ },
32239
+ sessions: {
32240
+ configKey: "sessions",
32241
+ exportName: "sessionsConfig",
32242
+ fields: [{ name: "getUserId", value: "(user) => user.sub" }],
32243
+ generic: true,
32244
+ imports: [],
32245
+ note: "Requires an `authSessionStore` (top-level) that can enumerate a user\u2019s sessions.",
32246
+ packages: [],
32247
+ typeName: "SessionsConfig"
32248
+ },
32249
+ sso: {
32250
+ configKey: "sso",
32251
+ exportName: "ssoConfig",
32252
+ fields: [
32253
+ { name: "ssoConnectionStore", value: "createInMemorySsoConnectionStore()" },
32254
+ { name: "getSsoUser", value: TODO("map the verified SSO identity to your user (create on first sign-in)") }
32255
+ ],
32256
+ generic: true,
32257
+ imports: ["createInMemorySsoConnectionStore"],
32258
+ note: "OIDC works out of the box; add a `samlAdapter` (wrapping e.g. @node-saml/node-saml) to also mount SAML.",
32259
+ packages: [],
32260
+ typeName: "SSOConfig"
32261
+ },
32262
+ webauthn: {
32263
+ configKey: "webauthn",
32264
+ exportName: "webauthnConfig",
32265
+ fields: [
32266
+ { name: "credentialStore", value: "createInMemoryWebAuthnCredentialStore()" },
32267
+ { name: "getUserId", value: "(user) => user.sub" },
32268
+ { name: "getWebAuthnUser", value: TODO("resolve a stored credential userId back to a user") },
32269
+ { name: "origin", value: "'http://localhost:3000'" },
32270
+ { name: "rpId", value: "'localhost'" },
32271
+ { name: "rpName", value: "'YourApp'" },
32272
+ {
32273
+ name: "webauthnAdapter",
32274
+ value: "{} as unknown as WebAuthnAdapter"
32275
+ }
32276
+ ],
32277
+ generic: true,
32278
+ imports: ["createInMemoryWebAuthnCredentialStore", "type WebAuthnAdapter"],
32279
+ note: "Provide a real `webauthnAdapter` wrapping a vetted library (e.g. @simplewebauthn/server); set origin/rpId/rpName for your domain.",
32280
+ packages: [],
32281
+ typeName: "WebAuthnConfig"
32282
+ },
32283
+ webhooks: {
32284
+ configKey: "webhooks",
32285
+ exportName: "webhooksConfig",
32286
+ fields: [{ name: "endpoints", value: "[]" }],
32287
+ generic: false,
32288
+ imports: [],
32289
+ note: "Add endpoints as { url, secret } \u2014 each event is HMAC-signed (Standard Webhooks) with that secret.",
32290
+ packages: [],
32291
+ typeName: "WebhooksConfig"
32292
+ }
32293
+ };
32294
+ });
32295
+
32296
+ // src/cli/config/auth/resolveAuthSettings.ts
32297
+ var exports_resolveAuthSettings = {};
32298
+ __export(exports_resolveAuthSettings, {
32299
+ resolveAuthSettingsState: () => resolveAuthSettingsState,
32300
+ parseAuthSettingsObject: () => parseAuthSettingsObject,
32301
+ findAuthSettingsPath: () => findAuthSettingsPath,
32302
+ findAuthSettingsObject: () => findAuthSettingsObject
32303
+ });
32304
+ import ts5 from "typescript";
32305
+ import { existsSync as existsSync10, readFileSync as readFileSync14 } from "fs";
32306
+ import { resolve as resolve9 } from "path";
32307
+ var AUTH_PACKAGE = "@absolutejs/auth", CONFIG_CANDIDATES4, findAuthSettingsPath = (cwd, override) => {
32308
+ if (override) {
32309
+ const resolved = resolve9(cwd, override);
32310
+ return existsSync10(resolved) ? resolved : null;
32311
+ }
32312
+ for (const name of CONFIG_CANDIDATES4) {
32313
+ const candidate = resolve9(cwd, name);
32314
+ if (existsSync10(candidate))
32315
+ return candidate;
32316
+ }
32317
+ return null;
32318
+ }, parseSource2 = (configPath, text) => ts5.createSourceFile(configPath, text, ts5.ScriptTarget.Latest, true), findAuthSettingsObject = (sourceFile) => {
32319
+ let found = null;
32320
+ const visit2 = (node) => {
32321
+ if (found)
32322
+ return;
32323
+ const [firstArgument] = ts5.isCallExpression(node) ? node.arguments : [];
32324
+ if (ts5.isCallExpression(node) && ts5.isIdentifier(node.expression) && node.expression.text === "defineAuthSettings" && firstArgument && ts5.isObjectLiteralExpression(firstArgument)) {
32325
+ found = firstArgument;
32326
+ return;
32327
+ }
32328
+ if (ts5.isExportAssignment(node) && ts5.isObjectLiteralExpression(node.expression)) {
32329
+ found = node.expression;
32330
+ return;
32331
+ }
32332
+ ts5.forEachChild(node, visit2);
32333
+ };
32334
+ visit2(sourceFile);
32335
+ return found;
32336
+ }, parseAuthSettingsObject = (configPath) => {
32337
+ const text = readFileSync14(configPath, "utf-8");
32338
+ return {
32339
+ object: findAuthSettingsObject(parseSource2(configPath, text)),
32340
+ text
32341
+ };
32342
+ }, evalLiteral2 = (node) => {
32343
+ if (ts5.isStringLiteralLike(node))
32344
+ return { opaque: false, value: node.text };
32345
+ if (node.kind === ts5.SyntaxKind.TrueKeyword) {
32346
+ return { opaque: false, value: true };
32347
+ }
32348
+ if (node.kind === ts5.SyntaxKind.FalseKeyword) {
32349
+ return { opaque: false, value: false };
32350
+ }
32351
+ if (node.kind === ts5.SyntaxKind.NullKeyword) {
32352
+ return { opaque: false, value: null };
32353
+ }
32354
+ if (ts5.isNumericLiteral(node)) {
32355
+ return { opaque: false, value: Number(node.text) };
32356
+ }
32357
+ if (ts5.isPrefixUnaryExpression(node) && node.operator === ts5.SyntaxKind.MinusToken && ts5.isNumericLiteral(node.operand)) {
32358
+ return { opaque: false, value: -Number(node.operand.text) };
32359
+ }
32360
+ if (ts5.isArrayLiteralExpression(node)) {
32361
+ const items = [];
32362
+ for (const element of node.elements) {
32363
+ const result = evalLiteral2(element);
32364
+ if (result.opaque)
32365
+ return { opaque: true, value: undefined };
32366
+ items.push(result.value);
32367
+ }
32368
+ return { opaque: false, value: items };
32369
+ }
32370
+ return { opaque: true, value: undefined };
32371
+ }, readCurrent3 = (configPath) => {
32372
+ const current2 = {};
32373
+ const opaqueKeys = [];
32374
+ const { object } = parseAuthSettingsObject(configPath);
32375
+ if (!object)
32376
+ return { current: current2, opaqueKeys };
32377
+ for (const property of object.properties) {
32378
+ if (!ts5.isPropertyAssignment(property) || !(ts5.isIdentifier(property.name) || ts5.isStringLiteral(property.name))) {
32379
+ continue;
32380
+ }
32381
+ const name = property.name.text;
32382
+ const result = evalLiteral2(property.initializer);
32383
+ if (result.opaque)
32384
+ opaqueKeys.push(name);
32385
+ else
32386
+ current2[name] = result.value;
32387
+ }
32388
+ return { current: current2, opaqueKeys };
32389
+ }, resolveAuthSettingsState = (cwd, override) => {
32390
+ const configPath = findAuthSettingsPath(cwd, override);
32391
+ const fields = introspectType(cwd, "AuthSettings", new Set, AUTH_PACKAGE);
32392
+ const { current: current2, opaqueKeys } = configPath ? readCurrent3(configPath) : { current: {}, opaqueKeys: [] };
32393
+ return {
32394
+ available: fields.length > 0,
32395
+ configPath,
32396
+ current: current2,
32397
+ fields,
32398
+ opaqueKeys
32399
+ };
32400
+ };
32401
+ var init_resolveAuthSettings = __esm(() => {
32402
+ init_fromType();
32403
+ CONFIG_CANDIDATES4 = [
32404
+ "auth.config.ts",
32405
+ "auth.config.js",
32406
+ "auth.config.mjs"
32407
+ ];
32408
+ });
32409
+
32073
32410
  // src/cli/config/auth/resolveAuthState.ts
32074
32411
  var exports_resolveAuthState = {};
32075
32412
  __export(exports_resolveAuthState, {
32076
32413
  resolveAuthState: () => resolveAuthState
32077
32414
  });
32078
- import ts5 from "typescript";
32079
- import { existsSync as existsSync10, readdirSync, readFileSync as readFileSync14 } from "fs";
32080
- import { join as join2, relative, resolve as resolve9 } from "path";
32081
- var AUTH_PACKAGE = "@absolutejs/auth", REPO_URL = "https://github.com/absolutejs/absolute-auth", NPM_URL = "https://www.npmjs.com/package/@absolutejs/auth", SKIP_DIRS, MAX_FILES = 4000, SETUP_EXPORTS, isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), readJson = (path) => {
32082
- if (!existsSync10(path))
32415
+ import ts6 from "typescript";
32416
+ import { existsSync as existsSync11, readdirSync, readFileSync as readFileSync15 } from "fs";
32417
+ import { join as join2, relative, resolve as resolve10 } from "path";
32418
+ var AUTH_PACKAGE2 = "@absolutejs/auth", REPO_URL = "https://github.com/absolutejs/absolute-auth", NPM_URL = "https://www.npmjs.com/package/@absolutejs/auth", SKIP_DIRS, MAX_FILES = 4000, SETUP_EXPORTS, isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), readJson = (path) => {
32419
+ if (!existsSync11(path))
32083
32420
  return null;
32084
32421
  try {
32085
- const parsed = JSON.parse(readFileSync14(path, "utf-8"));
32422
+ const parsed = JSON.parse(readFileSync15(path, "utf-8"));
32086
32423
  return isRecord3(parsed) ? parsed : null;
32087
32424
  } catch {
32088
32425
  return null;
@@ -32098,12 +32435,12 @@ var AUTH_PACKAGE = "@absolutejs/auth", REPO_URL = "https://github.com/absolutejs
32098
32435
  const group = pkg[field];
32099
32436
  if (!isRecord3(group))
32100
32437
  continue;
32101
- const version3 = group[AUTH_PACKAGE];
32438
+ const version3 = group[AUTH_PACKAGE2];
32102
32439
  if (typeof version3 === "string")
32103
32440
  return version3;
32104
32441
  }
32105
32442
  return null;
32106
- }, installedVersionFor = (cwd) => stringField(readJson(join2(cwd, "node_modules", AUTH_PACKAGE, "package.json")), "version"), SOURCE_FILE, safeReaddir = (dir) => {
32443
+ }, installedVersionFor = (cwd) => stringField(readJson(join2(cwd, "node_modules", AUTH_PACKAGE2, "package.json")), "version"), SOURCE_FILE, safeReaddir = (dir) => {
32107
32444
  try {
32108
32445
  return readdirSync(dir, { withFileTypes: true });
32109
32446
  } catch {
@@ -32132,9 +32469,9 @@ var AUTH_PACKAGE = "@absolutejs/auth", REPO_URL = "https://github.com/absolutejs
32132
32469
  collectFrom(dir, found, stack);
32133
32470
  }
32134
32471
  return found;
32135
- }, isAuthPackageImport = (statement) => ts5.isImportDeclaration(statement) && ts5.isStringLiteral(statement.moduleSpecifier) && statement.moduleSpecifier.text === AUTH_PACKAGE, addAuthNames = (statement, names) => {
32472
+ }, isAuthPackageImport = (statement) => ts6.isImportDeclaration(statement) && ts6.isStringLiteral(statement.moduleSpecifier) && statement.moduleSpecifier.text === AUTH_PACKAGE2, addAuthNames = (statement, names) => {
32136
32473
  const bindings = statement.importClause?.namedBindings;
32137
- if (!bindings || !ts5.isNamedImports(bindings))
32474
+ if (!bindings || !ts6.isNamedImports(bindings))
32138
32475
  return;
32139
32476
  for (const element of bindings.elements) {
32140
32477
  const imported = element.propertyName?.text ?? element.name.text;
@@ -32149,18 +32486,18 @@ var AUTH_PACKAGE = "@absolutejs/auth", REPO_URL = "https://github.com/absolutejs
32149
32486
  addAuthNames(statement, names);
32150
32487
  }
32151
32488
  return names;
32152
- }, isAuthCall = (node, bindings) => ts5.isIdentifier(node.expression) && bindings.has(node.expression.text), providerCountOf = (property) => {
32153
- if (!ts5.isPropertyAssignment(property) || !ts5.isObjectLiteralExpression(property.initializer)) {
32489
+ }, isAuthCall = (node, bindings) => ts6.isIdentifier(node.expression) && bindings.has(node.expression.text), providerCountOf = (property) => {
32490
+ if (!ts6.isPropertyAssignment(property) || !ts6.isObjectLiteralExpression(property.initializer)) {
32154
32491
  return null;
32155
32492
  }
32156
32493
  return property.initializer.properties.length;
32157
32494
  }, readConfigKeys = (object) => {
32158
32495
  const keys = new Set;
32159
32496
  let providerCount = null;
32160
- const usesSpread = object.properties.some((property) => ts5.isSpreadAssignment(property));
32497
+ const usesSpread = object.properties.some((property) => ts6.isSpreadAssignment(property));
32161
32498
  for (const property of object.properties) {
32162
32499
  const { name } = property;
32163
- if (name === undefined || !ts5.isIdentifier(name))
32500
+ if (name === undefined || !ts6.isIdentifier(name))
32164
32501
  continue;
32165
32502
  keys.add(name.text);
32166
32503
  if (name.text !== "providersConfiguration")
@@ -32170,20 +32507,20 @@ var AUTH_PACKAGE = "@absolutejs/auth", REPO_URL = "https://github.com/absolutejs
32170
32507
  return { keys, providerCount, usesSpread };
32171
32508
  }, matchFromCall = (node) => {
32172
32509
  const [arg] = node.arguments;
32173
- if (arg && ts5.isObjectLiteralExpression(arg))
32510
+ if (arg && ts6.isObjectLiteralExpression(arg))
32174
32511
  return readConfigKeys(arg);
32175
32512
  return { keys: new Set, providerCount: null, usesSpread: true };
32176
32513
  }, readFileOrNull = (path) => {
32177
32514
  try {
32178
- return readFileSync14(path, "utf-8");
32515
+ return readFileSync15(path, "utf-8");
32179
32516
  } catch {
32180
32517
  return null;
32181
32518
  }
32182
32519
  }, findSetupInFile = (path) => {
32183
32520
  const text = readFileOrNull(path);
32184
- if (text === null || !text.includes(AUTH_PACKAGE))
32521
+ if (text === null || !text.includes(AUTH_PACKAGE2))
32185
32522
  return null;
32186
- const sourceFile = ts5.createSourceFile(path, text, ts5.ScriptTarget.Latest, true);
32523
+ const sourceFile = ts6.createSourceFile(path, text, ts6.ScriptTarget.Latest, true);
32187
32524
  const bindings = authBindings(sourceFile);
32188
32525
  if (bindings.size === 0)
32189
32526
  return null;
@@ -32191,11 +32528,11 @@ var AUTH_PACKAGE = "@absolutejs/auth", REPO_URL = "https://github.com/absolutejs
32191
32528
  const visit2 = (node) => {
32192
32529
  if (match)
32193
32530
  return;
32194
- if (ts5.isCallExpression(node) && isAuthCall(node, bindings)) {
32531
+ if (ts6.isCallExpression(node) && isAuthCall(node, bindings)) {
32195
32532
  match = matchFromCall(node);
32196
32533
  return;
32197
32534
  }
32198
- ts5.forEachChild(node, visit2);
32535
+ ts6.forEachChild(node, visit2);
32199
32536
  };
32200
32537
  visit2(sourceFile);
32201
32538
  return match;
@@ -32205,10 +32542,11 @@ var AUTH_PACKAGE = "@absolutejs/auth", REPO_URL = "https://github.com/absolutejs
32205
32542
  configured: keys.has(feature.configKey),
32206
32543
  id: feature.id,
32207
32544
  kind: feature.kind,
32208
- label: feature.label
32545
+ label: feature.label,
32546
+ scaffoldable: isScaffoldableFeature(feature.id)
32209
32547
  })), resolveAuthState = (cwd) => {
32210
32548
  const installedVersion = installedVersionFor(cwd);
32211
- const root = existsSync10(join2(cwd, "src")) ? join2(cwd, "src") : cwd;
32549
+ const root = existsSync11(join2(cwd, "src")) ? join2(cwd, "src") : cwd;
32212
32550
  let match = null;
32213
32551
  let setupPath = null;
32214
32552
  for (const file of candidateFiles(root)) {
@@ -32216,7 +32554,7 @@ var AUTH_PACKAGE = "@absolutejs/auth", REPO_URL = "https://github.com/absolutejs
32216
32554
  if (found === null)
32217
32555
  continue;
32218
32556
  match = found;
32219
- setupPath = relative(cwd, resolve9(file));
32557
+ setupPath = relative(cwd, resolve10(file));
32220
32558
  break;
32221
32559
  }
32222
32560
  const keys = match?.keys ?? new Set;
@@ -32229,6 +32567,7 @@ var AUTH_PACKAGE = "@absolutejs/auth", REPO_URL = "https://github.com/absolutejs
32229
32567
  npmUrl: NPM_URL,
32230
32568
  providerCount: match?.providerCount ?? null,
32231
32569
  repoUrl: REPO_URL,
32570
+ settings: resolveAuthSettingsState(cwd),
32232
32571
  setupPath,
32233
32572
  usesSpread: match?.usesSpread ?? false
32234
32573
  };
@@ -32236,6 +32575,8 @@ var AUTH_PACKAGE = "@absolutejs/auth", REPO_URL = "https://github.com/absolutejs
32236
32575
  };
32237
32576
  var init_resolveAuthState = __esm(() => {
32238
32577
  init_authCatalog();
32578
+ init_authScaffolds();
32579
+ init_resolveAuthSettings();
32239
32580
  SKIP_DIRS = new Set([
32240
32581
  "node_modules",
32241
32582
  ".git",
@@ -32249,6 +32590,87 @@ var init_resolveAuthState = __esm(() => {
32249
32590
  SOURCE_FILE = /\.(ts|tsx|mts|cts|js|mjs)$/;
32250
32591
  });
32251
32592
 
32593
+ // src/cli/config/auth/editAuthConfig.ts
32594
+ var exports_editAuthConfig = {};
32595
+ __export(exports_editAuthConfig, {
32596
+ applyAuthConfigEdit: () => applyAuthConfigEdit
32597
+ });
32598
+ import ts7 from "typescript";
32599
+ import { readFileSync as readFileSync16, writeFileSync as writeFileSync7 } from "fs";
32600
+ var lineStartOffset2 = (text, position) => {
32601
+ let index = position;
32602
+ while (index > 0 && text[index - 1] !== `
32603
+ `)
32604
+ index -= 1;
32605
+ return index;
32606
+ }, indentBefore2 = (text, position) => text.slice(lineStartOffset2(text, position), position), findProperty3 = (object, name) => object.properties.find((property) => ts7.isPropertyAssignment(property) && (ts7.isIdentifier(property.name) || ts7.isStringLiteral(property.name)) && property.name.text === name), applyAuthConfigEdit = (configPath, request) => {
32607
+ try {
32608
+ const text = readFileSync16(configPath, "utf-8");
32609
+ const sourceFile = ts7.createSourceFile(configPath, text, ts7.ScriptTarget.Latest, true);
32610
+ const object = findAuthSettingsObject(sourceFile);
32611
+ if (!object) {
32612
+ return {
32613
+ message: "Could not find defineAuthSettings({ ... }) in auth.config.ts.",
32614
+ ok: false
32615
+ };
32616
+ }
32617
+ const existing = findProperty3(object, request.name);
32618
+ if (request.remove) {
32619
+ if (!existing)
32620
+ return { message: `${request.name} is not set`, ok: true };
32621
+ const start = lineStartOffset2(text, existing.getStart(sourceFile));
32622
+ let end = existing.getEnd();
32623
+ if (text[end] === ",")
32624
+ end += 1;
32625
+ if (text[end] === `
32626
+ `)
32627
+ end += 1;
32628
+ writeFileSync7(configPath, text.slice(0, start) + text.slice(end), "utf-8");
32629
+ return { message: `Removed ${request.name}`, ok: true };
32630
+ }
32631
+ const valueText = serializeValue2(request.value);
32632
+ if (existing) {
32633
+ const start = existing.initializer.getStart(sourceFile);
32634
+ const end = existing.initializer.getEnd();
32635
+ writeFileSync7(configPath, text.slice(0, start) + valueText + text.slice(end), "utf-8");
32636
+ return { message: `Updated ${request.name}`, ok: true };
32637
+ }
32638
+ const { properties } = object;
32639
+ const entry = `${request.name}: ${valueText}`;
32640
+ if (properties.length > 0) {
32641
+ const last = properties[properties.length - 1];
32642
+ if (!last) {
32643
+ return {
32644
+ message: "Could not locate insertion point.",
32645
+ ok: false
32646
+ };
32647
+ }
32648
+ const indent = indentBefore2(text, last.getStart(sourceFile));
32649
+ let insertAt = last.getEnd();
32650
+ const hasComma = text[insertAt] === ",";
32651
+ if (hasComma)
32652
+ insertAt += 1;
32653
+ const insertion = `${hasComma ? "" : ","}
32654
+ ${indent}${entry}`;
32655
+ writeFileSync7(configPath, text.slice(0, insertAt) + insertion + text.slice(insertAt), "utf-8");
32656
+ } else {
32657
+ const insertAt = object.getStart(sourceFile) + 1;
32658
+ const indent = `${indentBefore2(text, object.getStart(sourceFile))} `;
32659
+ const insertion = `
32660
+ ${indent}${entry}
32661
+ ${indentBefore2(text, object.getStart(sourceFile))}`;
32662
+ writeFileSync7(configPath, text.slice(0, insertAt) + insertion + text.slice(insertAt), "utf-8");
32663
+ }
32664
+ return { message: `Updated ${request.name}`, ok: true };
32665
+ } catch (error) {
32666
+ return { message: String(error), ok: false };
32667
+ }
32668
+ };
32669
+ var init_editAuthConfig = __esm(() => {
32670
+ init_resolveAuthSettings();
32671
+ init_serialize();
32672
+ });
32673
+
32252
32674
  // src/cli/add/dependencies.ts
32253
32675
  var runBunAdd = (cwd, specs, dev) => {
32254
32676
  if (specs.length === 0)
@@ -32263,6 +32685,80 @@ var runBunAdd = (cwd, specs, dev) => {
32263
32685
  }, installPackages = (cwd, specs, dev = false) => runBunAdd(cwd, specs, dev);
32264
32686
  var init_dependencies = () => {};
32265
32687
 
32688
+ // src/cli/config/auth/scaffoldAuthFeature.ts
32689
+ var exports_scaffoldAuthFeature = {};
32690
+ __export(exports_scaffoldAuthFeature, {
32691
+ scaffoldAuthFeature: () => scaffoldAuthFeature
32692
+ });
32693
+ import { existsSync as existsSync12, writeFileSync as writeFileSync8 } from "fs";
32694
+ import { dirname, join as join3, relative as relative2, resolve as resolve11 } from "path";
32695
+ var renderScaffold = (scaffold) => {
32696
+ const importNames = [...scaffold.imports, `type ${scaffold.typeName}`];
32697
+ const importLine = `import { ${importNames.join(", ")} } from '@absolutejs/auth';`;
32698
+ const userType = scaffold.generic ? `
32699
+ // TODO: replace with your app's user type (the one you pass to auth<User>()).
32700
+ type User = {
32701
+ sub: string;
32702
+ };
32703
+ ` : "";
32704
+ const note = scaffold.note ? `
32705
+ // ${scaffold.note}
32706
+ ` : `
32707
+ `;
32708
+ const generic = scaffold.generic ? "<User>" : "";
32709
+ const body = scaffold.fields.map((field) => ` ${field.name}: ${field.value}`).join(`,
32710
+ `);
32711
+ return `${importLine}
32712
+ ${userType}${note}export const ${scaffold.exportName}: ${scaffold.typeName}${generic} = {
32713
+ ${body}
32714
+ };
32715
+ `;
32716
+ }, targetDir = (cwd) => {
32717
+ const { setupPath } = resolveAuthState(cwd);
32718
+ if (setupPath)
32719
+ return dirname(resolve11(cwd, setupPath));
32720
+ const src = join3(cwd, "src");
32721
+ return existsSync12(src) ? src : cwd;
32722
+ }, spreadFor = (scaffold) => `import { ${scaffold.exportName} } from './${scaffold.exportName}';
32723
+ // add to your auth() call:
32724
+ ${scaffold.configKey}: ${scaffold.exportName}`, failure2 = (message) => ({
32725
+ created: null,
32726
+ installed: false,
32727
+ message,
32728
+ ok: false,
32729
+ spreadSnippet: null
32730
+ }), scaffoldAuthFeature = (cwd, id, options = {}) => {
32731
+ const scaffold = AUTH_SCAFFOLDS[id];
32732
+ if (!scaffold)
32733
+ return failure2(`Unknown auth feature "${id}".`);
32734
+ const filePath = join3(targetDir(cwd), `${scaffold.exportName}.ts`);
32735
+ const relPath = relative2(cwd, filePath);
32736
+ if (existsSync12(filePath)) {
32737
+ return {
32738
+ created: null,
32739
+ installed: false,
32740
+ message: `${relPath} already exists \u2014 left untouched.`,
32741
+ ok: true,
32742
+ spreadSnippet: spreadFor(scaffold)
32743
+ };
32744
+ }
32745
+ const install = options.install ?? true;
32746
+ const installOk = install && scaffold.packages.length > 0 ? installPackages(cwd, scaffold.packages) : true;
32747
+ writeFileSync8(filePath, renderScaffold(scaffold));
32748
+ return {
32749
+ created: relPath,
32750
+ installed: installOk,
32751
+ message: `Scaffolded ${relPath}.`,
32752
+ ok: true,
32753
+ spreadSnippet: spreadFor(scaffold)
32754
+ };
32755
+ };
32756
+ var init_scaffoldAuthFeature = __esm(() => {
32757
+ init_dependencies();
32758
+ init_authScaffolds();
32759
+ init_resolveAuthState();
32760
+ });
32761
+
32266
32762
  // src/cli/integrations/catalog.ts
32267
32763
  var INTEGRATIONS, findIntegration = (id) => INTEGRATIONS.find((integration) => integration.id === id) ?? null;
32268
32764
  var init_catalog = __esm(() => {
@@ -32324,14 +32820,14 @@ __export(exports_addPlugin, {
32324
32820
  resolveIntegrationsState: () => resolveIntegrationsState,
32325
32821
  addIntegration: () => addIntegration
32326
32822
  });
32327
- import { existsSync as existsSync11, readFileSync as readFileSync15 } from "fs";
32328
- import { join as join3 } from "path";
32823
+ import { existsSync as existsSync13, readFileSync as readFileSync17 } from "fs";
32824
+ import { join as join4 } from "path";
32329
32825
  var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), readPackageJson = (cwd) => {
32330
- const path = join3(cwd, "package.json");
32331
- if (!existsSync11(path))
32826
+ const path = join4(cwd, "package.json");
32827
+ if (!existsSync13(path))
32332
32828
  return null;
32333
32829
  try {
32334
- const parsed = JSON.parse(readFileSync15(path, "utf-8"));
32830
+ const parsed = JSON.parse(readFileSync17(path, "utf-8"));
32335
32831
  return isRecord4(parsed) ? parsed : null;
32336
32832
  } catch {
32337
32833
  return null;
@@ -32382,7 +32878,7 @@ ${wiring.useLine}` : null, toItem = (meta, deps, current2) => {
32382
32878
  return `Skipped install (--no-install) for ${meta.label}.`;
32383
32879
  }
32384
32880
  return `${meta.label} is built in \u2014 no install needed.`;
32385
- }, failure2 = (message) => ({
32881
+ }, failure3 = (message) => ({
32386
32882
  installed: false,
32387
32883
  item: null,
32388
32884
  message,
@@ -32392,7 +32888,7 @@ ${wiring.useLine}` : null, toItem = (meta, deps, current2) => {
32392
32888
  }), addIntegration = (cwd, id, options = {}) => {
32393
32889
  const meta = findIntegration(id);
32394
32890
  if (!meta)
32395
- return failure2(`Unknown integration "${id}".`);
32891
+ return failure3(`Unknown integration "${id}".`);
32396
32892
  const install = options.install ?? true;
32397
32893
  const willInstall = install && meta.packages.length > 0;
32398
32894
  const installOk = willInstall ? installPackages(cwd, meta.packages) : true;
@@ -32401,7 +32897,7 @@ ${wiring.useLine}` : null, toItem = (meta, deps, current2) => {
32401
32897
  let message = baseMessage(meta, willInstall, installOk);
32402
32898
  if (meta.wiring.kind === "config") {
32403
32899
  if (!configPath)
32404
- return failure2("No absolute.config.ts found.");
32900
+ return failure3("No absolute.config.ts found.");
32405
32901
  const edit = applyAbsoluteConfigEdit(configPath, {
32406
32902
  name: meta.wiring.field,
32407
32903
  value: true
@@ -32429,11 +32925,11 @@ var init_addPlugin = __esm(() => {
32429
32925
 
32430
32926
  // src/cli/config/server.ts
32431
32927
  import { Elysia as Elysia2 } from "elysia";
32432
- import { existsSync as existsSync12, readFileSync as readFileSync16 } from "fs";
32433
- import { resolve as resolve10 } from "path";
32928
+ import { existsSync as existsSync14, readFileSync as readFileSync18 } from "fs";
32929
+ import { resolve as resolve12 } from "path";
32434
32930
 
32435
32931
  // src/cli/config/page/PanelHost.tsx
32436
- var import_react8 = __toESM(require_react(), 1);
32932
+ var import_react9 = __toESM(require_react(), 1);
32437
32933
 
32438
32934
  // src/cli/config/eslint/EslintPanel.tsx
32439
32935
  var import_react2 = __toESM(require_react(), 1);
@@ -34765,40 +35261,113 @@ var IntegrationsPanel = ({
34765
35261
 
34766
35262
  // src/cli/config/auth/AuthPanel.tsx
34767
35263
  init_authCatalog();
35264
+ var import_react7 = __toESM(require_react(), 1);
34768
35265
  var jsx_dev_runtime7 = __toESM(require_jsx_dev_runtime(), 1);
34769
- var FeatureCard = ({ blurb, configKey, configured, kind, label }) => /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("div", {
34770
- className: "rule",
34771
- children: /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("div", {
34772
- className: "rule-main",
35266
+ var SettingRow = ({ busy, field, isSet, onSave, value }) => {
35267
+ const [draft, setDraft] = import_react7.useState(value);
35268
+ return /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("div", {
35269
+ className: "rule fe-block",
34773
35270
  children: [
34774
35271
  /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("div", {
34775
- className: "rule-name-row",
35272
+ className: "rule-main",
34776
35273
  children: [
34777
- /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("span", {
34778
- className: "rule-name",
34779
- children: label
34780
- }, undefined, false, undefined, this),
34781
- /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("span", {
34782
- className: "badge dep",
34783
- children: configKey
34784
- }, undefined, false, undefined, this),
34785
- /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("span", {
34786
- className: "badge",
34787
- children: kind
35274
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("div", {
35275
+ className: "rule-name-row",
35276
+ children: [
35277
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("span", {
35278
+ className: "rule-name",
35279
+ children: field.name
35280
+ }, undefined, false, undefined, this),
35281
+ isSet && /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("span", {
35282
+ className: "badge src",
35283
+ children: "set"
35284
+ }, undefined, false, undefined, this)
35285
+ ]
35286
+ }, undefined, true, undefined, this),
35287
+ field.description !== "" && /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("p", {
35288
+ className: "rule-desc",
35289
+ children: field.description
34788
35290
  }, undefined, false, undefined, this),
34789
- configured && /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("span", {
34790
- className: "badge src",
34791
- children: "configured"
35291
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("div", {
35292
+ className: "fe-root",
35293
+ children: /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(FieldEditor, {
35294
+ onChange: setDraft,
35295
+ schema: field.schema,
35296
+ value: draft
35297
+ }, undefined, false, undefined, this)
34792
35298
  }, undefined, false, undefined, this)
34793
35299
  ]
34794
35300
  }, undefined, true, undefined, this),
34795
- /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("p", {
34796
- className: "rule-desc",
34797
- children: blurb
34798
- }, undefined, false, undefined, this)
35301
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("div", {
35302
+ className: "rule-controls fe-actions",
35303
+ children: [
35304
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("button", {
35305
+ className: "ts-btn",
35306
+ disabled: busy,
35307
+ onClick: () => onSave(draft),
35308
+ type: "button",
35309
+ children: "save"
35310
+ }, undefined, false, undefined, this),
35311
+ isSet && /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("button", {
35312
+ className: "ts-clear",
35313
+ onClick: () => onSave(undefined, true),
35314
+ type: "button",
35315
+ children: "unset"
35316
+ }, undefined, false, undefined, this)
35317
+ ]
35318
+ }, undefined, true, undefined, this)
34799
35319
  ]
34800
- }, undefined, true, undefined, this)
34801
- }, undefined, false, undefined, this);
35320
+ }, undefined, true, undefined, this);
35321
+ };
35322
+ var FeatureCard = ({ busy, feature, onScaffold, result }) => /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("div", {
35323
+ className: "rule",
35324
+ children: [
35325
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("div", {
35326
+ className: "rule-main",
35327
+ children: [
35328
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("div", {
35329
+ className: "rule-name-row",
35330
+ children: [
35331
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("span", {
35332
+ className: "rule-name",
35333
+ children: feature.label
35334
+ }, undefined, false, undefined, this),
35335
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("span", {
35336
+ className: "badge dep",
35337
+ children: feature.configKey
35338
+ }, undefined, false, undefined, this),
35339
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("span", {
35340
+ className: "badge",
35341
+ children: feature.kind
35342
+ }, undefined, false, undefined, this),
35343
+ feature.configured && /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("span", {
35344
+ className: "badge src",
35345
+ children: "configured"
35346
+ }, undefined, false, undefined, this)
35347
+ ]
35348
+ }, undefined, true, undefined, this),
35349
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("p", {
35350
+ className: "rule-desc",
35351
+ children: feature.blurb
35352
+ }, undefined, false, undefined, this),
35353
+ result?.spreadSnippet && /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("pre", {
35354
+ className: "intg-code",
35355
+ children: result.spreadSnippet
35356
+ }, undefined, false, undefined, this)
35357
+ ]
35358
+ }, undefined, true, undefined, this),
35359
+ !feature.configured && feature.scaffoldable && /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("div", {
35360
+ className: "rule-controls fe-actions",
35361
+ children: /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("button", {
35362
+ className: "ts-btn",
35363
+ disabled: busy,
35364
+ onClick: onScaffold,
35365
+ type: "button",
35366
+ children: result?.created ? "scaffolded" : "scaffold wiring"
35367
+ }, undefined, false, undefined, this)
35368
+ }, undefined, false, undefined, this)
35369
+ ]
35370
+ }, undefined, true, undefined, this);
34802
35371
  var NotInstalled = ({ npmUrl, repoUrl }) => /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("div", {
34803
35372
  className: "shell",
34804
35373
  children: [
@@ -34864,13 +35433,59 @@ var NotInstalled = ({ npmUrl, repoUrl }) => /* @__PURE__ */ jsx_dev_runtime7.jsx
34864
35433
  }, undefined, false, undefined, this)
34865
35434
  ]
34866
35435
  }, undefined, true, undefined, this);
34867
- var AuthPanel = ({ state }) => {
35436
+ var AuthPanel = ({ state: initial }) => {
35437
+ const [state, setState] = import_react7.useState(initial);
35438
+ const [busy, setBusy] = import_react7.useState(null);
35439
+ const [notice, setNotice] = import_react7.useState(null);
35440
+ const [results, setResults] = import_react7.useState({});
35441
+ const saveSetting = (name) => async (value, remove) => {
35442
+ setBusy(`setting:${name}`);
35443
+ setNotice(null);
35444
+ try {
35445
+ const response = await fetch("/api/auth", {
35446
+ body: JSON.stringify({ name, remove, value }),
35447
+ headers: { "Content-Type": "application/json" },
35448
+ method: "POST"
35449
+ });
35450
+ const result = await response.json();
35451
+ if (result.ok && result.state) {
35452
+ setState(result.state);
35453
+ setNotice({ kind: "ok", text: result.message });
35454
+ } else {
35455
+ setNotice({ kind: "err", text: result.message });
35456
+ }
35457
+ } catch (error) {
35458
+ setNotice({ kind: "err", text: String(error) });
35459
+ } finally {
35460
+ setBusy(null);
35461
+ }
35462
+ };
35463
+ const scaffold = (id) => async () => {
35464
+ setBusy(id);
35465
+ setNotice(null);
35466
+ try {
35467
+ const response = await fetch("/api/auth/scaffold", {
35468
+ body: JSON.stringify({ id }),
35469
+ headers: { "Content-Type": "application/json" },
35470
+ method: "POST"
35471
+ });
35472
+ const result = await response.json();
35473
+ setResults((prev) => ({ ...prev, [id]: result }));
35474
+ setNotice({ kind: result.ok ? "ok" : "err", text: result.message });
35475
+ } catch (error) {
35476
+ setNotice({ kind: "err", text: String(error) });
35477
+ } finally {
35478
+ setBusy(null);
35479
+ }
35480
+ };
34868
35481
  if (!state.installed)
34869
35482
  return /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(NotInstalled, {
34870
35483
  ...state
34871
35484
  }, undefined, false, undefined, this);
34872
35485
  const configuredCount = state.features.filter((feature) => feature.configured).length;
34873
35486
  const version = state.installedVersion ?? state.declaredVersion ?? "\u2014";
35487
+ const { settings } = state;
35488
+ const canEditSettings = settings.available && settings.configPath !== null;
34874
35489
  return /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("div", {
34875
35490
  className: "shell",
34876
35491
  children: [
@@ -35005,10 +35620,71 @@ var AuthPanel = ({ state }) => {
35005
35620
  ]
35006
35621
  }, undefined, true, undefined, this),
35007
35622
  state.features.map((feature) => /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(FeatureCard, {
35008
- ...feature
35623
+ busy: busy === feature.id,
35624
+ feature,
35625
+ onScaffold: scaffold(feature.id),
35626
+ result: results[feature.id] ?? null
35009
35627
  }, feature.id, false, undefined, this))
35010
35628
  ]
35011
35629
  }, undefined, true, undefined, this),
35630
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("section", {
35631
+ className: "section",
35632
+ children: [
35633
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("div", {
35634
+ className: "section-head",
35635
+ children: [
35636
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("h2", {
35637
+ className: "section-title",
35638
+ children: "Settings"
35639
+ }, undefined, false, undefined, this),
35640
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("span", {
35641
+ className: "section-files",
35642
+ children: settings.configPath ?? "auth.config.ts"
35643
+ }, undefined, false, undefined, this)
35644
+ ]
35645
+ }, undefined, true, undefined, this),
35646
+ !settings.available && /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("p", {
35647
+ className: "rule-desc",
35648
+ children: [
35649
+ "Upgrade ",
35650
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("code", {
35651
+ children: "@absolutejs/auth"
35652
+ }, undefined, false, undefined, this),
35653
+ " to a version that exports ",
35654
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("code", {
35655
+ children: "AuthSettings"
35656
+ }, undefined, false, undefined, this),
35657
+ " to edit settings here."
35658
+ ]
35659
+ }, undefined, true, undefined, this),
35660
+ settings.available && settings.configPath === null && /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("p", {
35661
+ className: "rule-desc",
35662
+ children: [
35663
+ "Create an ",
35664
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("code", {
35665
+ children: "auth.config.ts"
35666
+ }, undefined, false, undefined, this),
35667
+ " exporting",
35668
+ " ",
35669
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("code", {
35670
+ children: "defineAuthSettings({})"
35671
+ }, undefined, false, undefined, this),
35672
+ " and spread it into your ",
35673
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("code", {
35674
+ children: "auth()"
35675
+ }, undefined, false, undefined, this),
35676
+ " call, then edit the route paths, durations, and limits here."
35677
+ ]
35678
+ }, undefined, true, undefined, this),
35679
+ canEditSettings && settings.fields.map((field) => /* @__PURE__ */ jsx_dev_runtime7.jsxDEV(SettingRow, {
35680
+ busy: busy === `setting:${field.name}`,
35681
+ field,
35682
+ isSet: Object.prototype.hasOwnProperty.call(settings.current, field.name),
35683
+ onSave: saveSetting(field.name),
35684
+ value: settings.current[field.name]
35685
+ }, field.name, false, undefined, this))
35686
+ ]
35687
+ }, undefined, true, undefined, this),
35012
35688
  /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("section", {
35013
35689
  className: "section",
35014
35690
  children: [
@@ -35066,17 +35742,26 @@ var AuthPanel = ({ state }) => {
35066
35742
  ]
35067
35743
  }, undefined, true, undefined, this)
35068
35744
  ]
35745
+ }, undefined, true, undefined, this),
35746
+ notice && /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("div", {
35747
+ className: `toast ${notice.kind}`,
35748
+ children: [
35749
+ /* @__PURE__ */ jsx_dev_runtime7.jsxDEV("b", {
35750
+ children: notice.kind === "ok" ? "\u2713" : "\u2715"
35751
+ }, undefined, false, undefined, this),
35752
+ notice.text
35753
+ ]
35069
35754
  }, undefined, true, undefined, this)
35070
35755
  ]
35071
35756
  }, undefined, true, undefined, this);
35072
35757
  };
35073
35758
 
35074
35759
  // src/cli/config/packageJson/PackageJsonPanel.tsx
35075
- var import_react7 = __toESM(require_react(), 1);
35760
+ var import_react8 = __toESM(require_react(), 1);
35076
35761
  var jsx_dev_runtime8 = __toESM(require_jsx_dev_runtime(), 1);
35077
35762
  var matchesQuery5 = (query, text) => query === "" || text.toLowerCase().includes(query.toLowerCase());
35078
35763
  var FieldRow2 = ({ field, isSet, onSave, value }) => {
35079
- const [draft, setDraft] = import_react7.useState(value);
35764
+ const [draft, setDraft] = import_react8.useState(value);
35080
35765
  return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV("div", {
35081
35766
  className: "rule fe-block",
35082
35767
  children: [
@@ -35127,7 +35812,7 @@ var FieldRow2 = ({ field, isSet, onSave, value }) => {
35127
35812
  }, undefined, true, undefined, this);
35128
35813
  };
35129
35814
  var ScriptRow = ({ onRemove, onSave, script }) => {
35130
- const [draft, setDraft] = import_react7.useState(script.command);
35815
+ const [draft, setDraft] = import_react8.useState(script.command);
35131
35816
  return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV("div", {
35132
35817
  className: "rule",
35133
35818
  children: /* @__PURE__ */ jsx_dev_runtime8.jsxDEV("div", {
@@ -35172,8 +35857,8 @@ var ScriptRow = ({ onRemove, onSave, script }) => {
35172
35857
  }, undefined, false, undefined, this);
35173
35858
  };
35174
35859
  var AddScript = ({ onAdd }) => {
35175
- const [name, setName] = import_react7.useState("");
35176
- const [command, setCommand] = import_react7.useState("");
35860
+ const [name, setName] = import_react8.useState("");
35861
+ const [command, setCommand] = import_react8.useState("");
35177
35862
  const add = () => {
35178
35863
  if (name.trim() === "")
35179
35864
  return;
@@ -35218,9 +35903,9 @@ var AddScript = ({ onAdd }) => {
35218
35903
  }, undefined, false, undefined, this);
35219
35904
  };
35220
35905
  var PackageJsonPanel = ({ state: initial }) => {
35221
- const [state, setState] = import_react7.useState(initial);
35222
- const [query, setQuery] = import_react7.useState("");
35223
- const [notice, setNotice] = import_react7.useState(null);
35906
+ const [state, setState] = import_react8.useState(initial);
35907
+ const [query, setQuery] = import_react8.useState("");
35908
+ const [notice, setNotice] = import_react8.useState(null);
35224
35909
  const post = async (path, body) => {
35225
35910
  setNotice(null);
35226
35911
  try {
@@ -35509,9 +36194,9 @@ var renderPanel = (panel, data) => {
35509
36194
  return null;
35510
36195
  };
35511
36196
  var PanelHost = ({ panel }) => {
35512
- const [data, setData] = import_react8.useState(null);
35513
- const [phase, setPhase] = import_react8.useState("loading");
35514
- import_react8.useEffect(() => {
36197
+ const [data, setData] = import_react9.useState(null);
36198
+ const [phase, setPhase] = import_react9.useState("loading");
36199
+ import_react9.useEffect(() => {
35515
36200
  let active = true;
35516
36201
  setPhase("loading");
35517
36202
  fetch(ENDPOINTS[panel]).then((response) => response.json()).then((result) => {
@@ -37922,15 +38607,20 @@ var packageOps = () => Promise.all([
37922
38607
  Promise.resolve().then(() => (init_resolvePackageJson(), exports_resolvePackageJson))
37923
38608
  ]);
37924
38609
  var authOps = () => Promise.resolve().then(() => (init_resolveAuthState(), exports_resolveAuthState));
38610
+ var authEditOps = () => Promise.all([
38611
+ Promise.resolve().then(() => (init_editAuthConfig(), exports_editAuthConfig)),
38612
+ Promise.resolve().then(() => (init_resolveAuthSettings(), exports_resolveAuthSettings))
38613
+ ]);
38614
+ var scaffoldOps = () => Promise.resolve().then(() => (init_scaffoldAuthFeature(), exports_scaffoldAuthFeature));
37925
38615
  var integrationsOps = () => Promise.resolve().then(() => (init_addPlugin(), exports_addPlugin));
37926
38616
  var CLIENT_ROUTE = "/config-client.js";
37927
38617
  var readVersion = () => {
37928
38618
  for (const candidate of [
37929
- resolve10(import.meta.dir, "..", "..", "..", "package.json"),
37930
- resolve10(import.meta.dir, "..", "..", "..", "..", "package.json")
38619
+ resolve12(import.meta.dir, "..", "..", "..", "package.json"),
38620
+ resolve12(import.meta.dir, "..", "..", "..", "..", "package.json")
37931
38621
  ]) {
37932
38622
  try {
37933
- const pkg = JSON.parse(readFileSync16(candidate, "utf-8"));
38623
+ const pkg = JSON.parse(readFileSync18(candidate, "utf-8"));
37934
38624
  if (typeof pkg?.version === "string")
37935
38625
  return pkg.version;
37936
38626
  } catch {}
@@ -37952,8 +38642,8 @@ var resolvePort = (args) => {
37952
38642
  };
37953
38643
  var resolveHost = (args) => flagValue(args, "--host") || process.env.ABSOLUTE_CONFIG_HOST || CONFIG_DEFAULT_HOST;
37954
38644
  var fileScopeOf = (query) => typeof query.file === "string" ? query.file : undefined;
37955
- var distClientBundle = resolve10(import.meta.dir, "client.js");
37956
- var sourceClientEntry = resolve10(import.meta.dir, "client.tsx");
38645
+ var distClientBundle = resolve12(import.meta.dir, "client.js");
38646
+ var sourceClientEntry = resolve12(import.meta.dir, "client.tsx");
37957
38647
  var cachedClientBundle = null;
37958
38648
  var buildClientBundle = async () => {
37959
38649
  const built = await Bun.build({
@@ -37974,7 +38664,7 @@ var buildClientBundle = async () => {
37974
38664
  var getClientBundle = async () => {
37975
38665
  if (cachedClientBundle !== null)
37976
38666
  return cachedClientBundle;
37977
- cachedClientBundle = existsSync12(distClientBundle) ? readFileSync16(distClientBundle, "utf-8") : await buildClientBundle();
38667
+ cachedClientBundle = existsSync14(distClientBundle) ? readFileSync18(distClientBundle, "utf-8") : await buildClientBundle();
37978
38668
  return cachedClientBundle;
37979
38669
  };
37980
38670
  var renderShell = (panel) => handleReactPageRequest({
@@ -38139,6 +38829,54 @@ var handleIntegrationAdd = async (cwd, body, override) => {
38139
38829
  }
38140
38830
  return addIntegration2(cwd, body.id, { install: true, override });
38141
38831
  };
38832
+ var parseAuthEdit = (body) => {
38833
+ if (!isRecord(body) || typeof body.name !== "string")
38834
+ return null;
38835
+ const request = {
38836
+ name: body.name,
38837
+ remove: body.remove === true,
38838
+ value: body.value
38839
+ };
38840
+ return request;
38841
+ };
38842
+ var handleAuthEdit = async (cwd, body) => {
38843
+ const [{ applyAuthConfigEdit: applyAuthConfigEdit2 }, { findAuthSettingsPath: findAuthSettingsPath2 }] = await authEditOps();
38844
+ const { resolveAuthState: resolveAuthState2 } = await authOps();
38845
+ const request = parseAuthEdit(body);
38846
+ const configPath = findAuthSettingsPath2(cwd);
38847
+ if (!request || !configPath) {
38848
+ const invalid = {
38849
+ message: configPath ? "Invalid edit request." : "No auth.config.ts found \u2014 create one with defineAuthSettings({}).",
38850
+ ok: false,
38851
+ state: null
38852
+ };
38853
+ return new Response(JSON.stringify(invalid), {
38854
+ headers: { "Content-Type": "application/json" },
38855
+ status: HTTP_STATUS_BAD_REQUEST
38856
+ });
38857
+ }
38858
+ const outcome = applyAuthConfigEdit2(configPath, request);
38859
+ const result = {
38860
+ message: outcome.message,
38861
+ ok: outcome.ok,
38862
+ state: outcome.ok ? resolveAuthState2(cwd) : null
38863
+ };
38864
+ return result;
38865
+ };
38866
+ var handleAuthScaffold = async (cwd, body) => {
38867
+ const { scaffoldAuthFeature: scaffoldAuthFeature2 } = await scaffoldOps();
38868
+ if (!isRecord(body) || typeof body.id !== "string") {
38869
+ const invalid = {
38870
+ created: null,
38871
+ installed: false,
38872
+ message: "Missing auth feature id.",
38873
+ ok: false,
38874
+ spreadSnippet: null
38875
+ };
38876
+ return invalid;
38877
+ }
38878
+ return scaffoldAuthFeature2(cwd, body.id, { install: true });
38879
+ };
38142
38880
  var packageError = (message) => {
38143
38881
  const invalid = { message, ok: false, state: null };
38144
38882
  return new Response(JSON.stringify(invalid), {
@@ -38223,7 +38961,7 @@ var launchConfig = async (args, cwd = process.cwd()) => {
38223
38961
  }).post("/api/integrations/add", ({ body }) => handleIntegrationAdd(cwd, body, configOverride)).get("/api/auth", async () => {
38224
38962
  const { resolveAuthState: resolveAuthState2 } = await authOps();
38225
38963
  return resolveAuthState2(cwd);
38226
- }).get("/api/package", async () => {
38964
+ }).post("/api/auth", ({ body }) => handleAuthEdit(cwd, body)).post("/api/auth/scaffold", ({ body }) => handleAuthScaffold(cwd, body)).get("/api/package", async () => {
38227
38965
  const [, { resolvePackageJsonState: resolvePackageJsonState2 }] = await packageOps();
38228
38966
  return resolvePackageJsonState2(cwd);
38229
38967
  }).post("/api/package/script", ({ body }) => handleScriptEdit(cwd, body)).post("/api/package/field", ({ body }) => handleFieldEdit(cwd, body)).listen(listenOptions(port, cert));