@exposify/next 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -4
- package/dist/config.cjs +365 -94
- package/dist/config.d.cts +49 -3
- package/dist/config.d.ts +49 -3
- package/dist/config.js +359 -88
- package/dist/exposify-next.js +552 -118
- package/dist/index.cjs +190 -43
- package/dist/index.d.cts +40 -120
- package/dist/index.d.ts +40 -120
- package/dist/index.js +196 -43
- package/dist/manifest-DZQCvzQy.d.cts +176 -0
- package/dist/manifest-DZQCvzQy.d.ts +176 -0
- package/package.json +3 -3
- package/dist/fields-olvyFnxj.d.cts +0 -23
- package/dist/fields-olvyFnxj.d.ts +0 -23
package/dist/config.cjs
CHANGED
|
@@ -24,14 +24,14 @@ __export(config_exports, {
|
|
|
24
24
|
withExposify: () => withExposify
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(config_exports);
|
|
27
|
-
var
|
|
28
|
-
var
|
|
27
|
+
var import_node_fs8 = require("fs");
|
|
28
|
+
var import_node_path8 = require("path");
|
|
29
29
|
var import_core3 = require("@exposify/core");
|
|
30
30
|
|
|
31
31
|
// src/codegen.ts
|
|
32
32
|
var import_node_crypto = require("crypto");
|
|
33
|
-
var
|
|
34
|
-
var
|
|
33
|
+
var import_node_fs7 = require("fs");
|
|
34
|
+
var import_node_path7 = require("path");
|
|
35
35
|
var import_core2 = require("@exposify/core");
|
|
36
36
|
var import_tinyglobby = require("tinyglobby");
|
|
37
37
|
|
|
@@ -360,8 +360,8 @@ function readsOf(fn, formNames, st, depth) {
|
|
|
360
360
|
st.readsAll = true;
|
|
361
361
|
const decl = [...parents].reverse().find((p) => !["TSAsExpression", "TSSatisfiesExpression", "ParenthesizedExpression"].includes(p.type));
|
|
362
362
|
if (decl?.type === "VariableDeclarator" && decl.id.type === "ObjectPattern") {
|
|
363
|
-
for (const
|
|
364
|
-
const key =
|
|
363
|
+
for (const prop2 of decl.id.properties) {
|
|
364
|
+
const key = prop2.type === "ObjectProperty" ? keyName(prop2.key) : void 0;
|
|
365
365
|
if (key) addField(st, key, {}, false);
|
|
366
366
|
}
|
|
367
367
|
}
|
|
@@ -403,19 +403,19 @@ function analyzeFields(fn, roles, ctx) {
|
|
|
403
403
|
if (literal?.type === "TSTypeLiteral") {
|
|
404
404
|
for (const m of literal.members) if (m.type === "TSPropertySignature" && keyName(m.key)) members.set(keyName(m.key), m);
|
|
405
405
|
}
|
|
406
|
-
for (const
|
|
407
|
-
const key =
|
|
406
|
+
for (const prop2 of id.properties) {
|
|
407
|
+
const key = prop2.type === "ObjectProperty" ? keyName(prop2.key) : void 0;
|
|
408
408
|
if (!key) {
|
|
409
|
-
st.unresolved.push(`line ${lineOf(
|
|
409
|
+
st.unresolved.push(`line ${lineOf(prop2)}: a rest element in parameter ${role.name}`);
|
|
410
410
|
continue;
|
|
411
411
|
}
|
|
412
412
|
const member = members.get(key);
|
|
413
413
|
const hint = annotated(member?.typeAnnotation?.typeAnnotation);
|
|
414
414
|
if (!hint) {
|
|
415
|
-
st.unresolved.push(`line ${lineOf(
|
|
415
|
+
st.unresolved.push(`line ${lineOf(prop2)}: ${key} has no type it can read (string, number, boolean or a union of strings)`);
|
|
416
416
|
continue;
|
|
417
417
|
}
|
|
418
|
-
st.fields.set(key, { name: key, type: hint.type, ...hint.enum ? { enum: hint.enum } : {}, ...member?.optional ||
|
|
418
|
+
st.fields.set(key, { name: key, type: hint.type, ...hint.enum ? { enum: hint.enum } : {}, ...member?.optional || prop2.value.type === "AssignmentPattern" ? {} : { required: true } });
|
|
419
419
|
}
|
|
420
420
|
});
|
|
421
421
|
return { fields: [...st.fields.values()], readsAll: st.readsAll, unresolved: st.unresolved };
|
|
@@ -493,20 +493,20 @@ function renderedAttributes(el, fn, use, mod) {
|
|
|
493
493
|
for (const p of pattern.properties) {
|
|
494
494
|
if (p.type === "RestElement" && p.argument.type === "Identifier") restName = p.argument.name;
|
|
495
495
|
if (p.type !== "ObjectProperty") continue;
|
|
496
|
-
const
|
|
496
|
+
const prop2 = keyName(p.key);
|
|
497
497
|
const value = p.value.type === "AssignmentPattern" ? p.value.left : p.value;
|
|
498
|
-
if (!
|
|
498
|
+
if (!prop2 || value.type !== "Identifier") continue;
|
|
499
499
|
const fallback = p.value.type === "AssignmentPattern" ? attrValue({ type: "JSXAttribute", value: { type: "JSXExpressionContainer", expression: p.value.right } }, [], mod.scope) : void 0;
|
|
500
|
-
locals.set(value.name, { prop, ...fallback ? { fallback } : {} });
|
|
500
|
+
locals.set(value.name, { prop: prop2, ...fallback ? { fallback } : {} });
|
|
501
501
|
}
|
|
502
502
|
}
|
|
503
|
-
const
|
|
503
|
+
const named = new Set([...locals.values()].map((l) => l.prop));
|
|
504
504
|
const out = /* @__PURE__ */ new Map();
|
|
505
505
|
for (const a of el.openingElement.attributes) {
|
|
506
506
|
if (a.type === "JSXSpreadAttribute") {
|
|
507
507
|
const arg = unwrapTs(a.argument);
|
|
508
508
|
if (arg?.type === "Identifier" && (arg.name === restName || arg.name === propsName)) {
|
|
509
|
-
for (const [k, v] of use) if (arg.name === propsName || !
|
|
509
|
+
for (const [k, v] of use) if (arg.name === propsName || !named.has(k)) out.set(k, v);
|
|
510
510
|
}
|
|
511
511
|
continue;
|
|
512
512
|
}
|
|
@@ -514,14 +514,14 @@ function renderedAttributes(el, fn, use, mod) {
|
|
|
514
514
|
if (!key) continue;
|
|
515
515
|
const expr = a.value?.type === "JSXExpressionContainer" ? unwrapTs(a.value.expression) : void 0;
|
|
516
516
|
if (expr?.type === "Identifier" && locals.has(expr.name)) {
|
|
517
|
-
const { prop, fallback } = locals.get(expr.name);
|
|
518
|
-
const v = use.has(
|
|
517
|
+
const { prop: prop2, fallback } = locals.get(expr.name);
|
|
518
|
+
const v = use.has(prop2) ? use.get(prop2) : fallback;
|
|
519
519
|
if (v !== void 0) out.set(key, v);
|
|
520
520
|
continue;
|
|
521
521
|
}
|
|
522
522
|
if (expr?.type === "MemberExpression" && unwrapTs(expr.object)?.type === "Identifier" && unwrapTs(expr.object).name === propsName) {
|
|
523
|
-
const
|
|
524
|
-
if (
|
|
523
|
+
const prop2 = keyName(expr.property);
|
|
524
|
+
if (prop2 && use.has(prop2)) out.set(key, use.get(prop2));
|
|
525
525
|
continue;
|
|
526
526
|
}
|
|
527
527
|
out.set(key, attrValue(a, [], mod.scope));
|
|
@@ -918,7 +918,7 @@ function actionHosts(pages, graph, actionExports) {
|
|
|
918
918
|
var import_node_fs2 = require("fs");
|
|
919
919
|
var import_node_path2 = require("path");
|
|
920
920
|
var import_core = require("@exposify/core");
|
|
921
|
-
var SESSION_CODE = /cookies\(\)\s*\.set|\.cookies\.set\(|cookieStore\.set|set-cookie|setCookie|\bjwt\.sign\(|new\s+SignJWT|\bsign\(|createSession|session\.(save|create)|\.login\(/i;
|
|
921
|
+
var SESSION_CODE = /cookies\(\)\)?\s*\.set|\.cookies\.set\(|cookieStore\.set|set-cookie|setCookie|\bjwt\.sign\(|new\s+SignJWT|\bsign\(|createSession|session\.(save|create)|\.login\(/i;
|
|
922
922
|
function routeBodyFields(source) {
|
|
923
923
|
const names = /* @__PURE__ */ new Set();
|
|
924
924
|
let body = null;
|
|
@@ -939,16 +939,27 @@ function routeBodyFields(source) {
|
|
|
939
939
|
}
|
|
940
940
|
return names.size && body ? { fields: [...names], body } : null;
|
|
941
941
|
}
|
|
942
|
-
|
|
943
|
-
|
|
942
|
+
var LOGIN_RULE = { not: import_core.NOT_A_LOGIN, name: import_core.LOGIN_NAME, mustBeNamed: false, fields: (n) => (0, import_core.loginFields)(n), session: true };
|
|
943
|
+
var VERIFY_RULE = { not: import_core.NOT_A_VERIFY, name: import_core.VERIFY_NAME, mustBeNamed: true, fields: (n, named) => (0, import_core.verifyFields)(n, named), session: true };
|
|
944
|
+
var START_RULE = { not: import_core.NOT_A_LOGIN, name: new RegExp(`${import_core.LOGIN_NAME.source}|${import_core.START_NAME.source}`, "i"), mustBeNamed: true, fields: (n) => (0, import_core.startFields)(n), session: false };
|
|
945
|
+
var knownKey = (l) => l.kind === "action" ? `${l.file}#${l.export}` : l.file;
|
|
946
|
+
function actionCandidate(a, rule, known) {
|
|
947
|
+
if (rule.not.test(a.key) || rule.not.test(a.action.name)) return null;
|
|
944
948
|
if (!a.action.params.some((p) => p.role === "formData" || p.role === "object")) return null;
|
|
945
|
-
const
|
|
949
|
+
const isNamed = rule.name.test(a.action.name);
|
|
950
|
+
if (rule.mustBeNamed && !isNamed) return null;
|
|
951
|
+
const fields = rule.fields(
|
|
952
|
+
a.action.fields.map((f) => f.name),
|
|
953
|
+
isNamed
|
|
954
|
+
);
|
|
946
955
|
if (!fields) return null;
|
|
947
|
-
if (!a.action.setsCookies && !import_core.LOGIN_NAME.test(a.action.name)) return null;
|
|
956
|
+
if (rule.session && !a.action.setsCookies && !import_core.LOGIN_NAME.test(a.action.name) && !(rule === LOGIN_RULE && known.has(`${a.file}#${a.action.name}`))) return null;
|
|
948
957
|
return { kind: "action", key: a.key, export: a.action.name, file: a.file, fields, ...a.host ? { host: a.host } : {} };
|
|
949
958
|
}
|
|
950
|
-
function routeCandidate(root, r) {
|
|
951
|
-
if (r.router !== "app" || !r.module.methods.includes("POST") || r.params.length > 0 ||
|
|
959
|
+
function routeCandidate(root, r, rule, known) {
|
|
960
|
+
if (r.router !== "app" || !r.module.methods.includes("POST") || r.params.length > 0 || rule.not.test(r.path)) return null;
|
|
961
|
+
const isNamed = rule.name.test(r.path);
|
|
962
|
+
if (rule.mustBeNamed && !isNamed) return null;
|
|
952
963
|
let source;
|
|
953
964
|
try {
|
|
954
965
|
source = (0, import_node_fs2.readFileSync)((0, import_node_path2.join)(root, r.file), "utf8");
|
|
@@ -956,26 +967,41 @@ function routeCandidate(root, r) {
|
|
|
956
967
|
return null;
|
|
957
968
|
}
|
|
958
969
|
const read2 = routeBodyFields(source);
|
|
959
|
-
const fields = read2 ?
|
|
970
|
+
const fields = read2 ? rule.fields(read2.fields, isNamed) : null;
|
|
960
971
|
if (!read2 || !fields) return null;
|
|
961
|
-
if (!import_core.LOGIN_NAME.test(r.path) && !SESSION_CODE.test(source)) return null;
|
|
972
|
+
if (rule.session && !import_core.LOGIN_NAME.test(r.path) && !SESSION_CODE.test(source) && !(rule === LOGIN_RULE && known.has(r.file))) return null;
|
|
962
973
|
return { kind: "route", path: r.path, file: r.file, fields, body: read2.body };
|
|
963
974
|
}
|
|
964
|
-
var
|
|
965
|
-
function
|
|
966
|
-
const
|
|
975
|
+
var nameOf = (l) => l.kind === "action" ? l.export : l.path;
|
|
976
|
+
function candidates(d, rule, known) {
|
|
977
|
+
const out = [];
|
|
967
978
|
for (const a of d.actions) {
|
|
968
|
-
const c = actionCandidate(a);
|
|
969
|
-
if (c)
|
|
979
|
+
const c = actionCandidate(a, rule, known);
|
|
980
|
+
if (c) out.push(c);
|
|
970
981
|
}
|
|
971
982
|
for (const r of d.routes) {
|
|
972
|
-
const c = routeCandidate(d.root, r);
|
|
973
|
-
if (c)
|
|
983
|
+
const c = routeCandidate(d.root, r, rule, known);
|
|
984
|
+
if (c) out.push(c);
|
|
985
|
+
}
|
|
986
|
+
const score = (l) => (rule === LOGIN_RULE && known.has(knownKey(l)) ? 0 : 8) + (rule.name.test(nameOf(l)) ? 0 : 4) + (l.kind === "action" && l.host ? 0 : 2) + l.fields.length / 100;
|
|
987
|
+
return out.sort((a, b) => score(a) - score(b));
|
|
988
|
+
}
|
|
989
|
+
function findLogin(d, known = /* @__PURE__ */ new Set()) {
|
|
990
|
+
const verifies = candidates(d, VERIFY_RULE, known);
|
|
991
|
+
const logins = candidates(d, LOGIN_RULE, known);
|
|
992
|
+
if (logins.length > 0) {
|
|
993
|
+
const [first2, ...others2] = logins;
|
|
994
|
+
return { login: { ...first2, ...verifies[0] ? { verify: verifies[0] } : {} }, others: others2 };
|
|
995
|
+
}
|
|
996
|
+
const pairs = [];
|
|
997
|
+
for (const start of candidates(d, START_RULE, known)) {
|
|
998
|
+
const who = new Set(start.fields.filter((f) => f.kind === "identity").map((f) => f.name));
|
|
999
|
+
const verify = verifies.find((v) => v.fields.some((f) => who.has(f.name))) ?? verifies.find((v) => !v.fields.some((f) => f.kind === "identity"));
|
|
1000
|
+
if (verify) pairs.push({ ...start, passwordless: true, verify });
|
|
974
1001
|
}
|
|
975
|
-
if (
|
|
976
|
-
const
|
|
977
|
-
|
|
978
|
-
return { login: candidates[0], others: candidates.slice(1) };
|
|
1002
|
+
if (pairs.length === 0) return null;
|
|
1003
|
+
const [first, ...others] = pairs;
|
|
1004
|
+
return { login: first, others };
|
|
979
1005
|
}
|
|
980
1006
|
|
|
981
1007
|
// src/guards.ts
|
|
@@ -1157,9 +1183,9 @@ function pageSearchParams(program) {
|
|
|
1157
1183
|
if (!FUNCTION_TYPES.has(fn.type)) return;
|
|
1158
1184
|
const param = fn.params?.[0];
|
|
1159
1185
|
if (param?.type === "ObjectPattern") {
|
|
1160
|
-
for (const
|
|
1161
|
-
if (
|
|
1162
|
-
const value =
|
|
1186
|
+
for (const prop2 of param.properties) {
|
|
1187
|
+
if (prop2.type !== "ObjectProperty" || keyName(prop2.key) !== "searchParams") continue;
|
|
1188
|
+
const value = prop2.value.type === "AssignmentPattern" ? prop2.value.left : prop2.value;
|
|
1163
1189
|
if (value.type === "Identifier") sources.add(value.name);
|
|
1164
1190
|
for (const k of patternKeys(value)) names.add(k);
|
|
1165
1191
|
}
|
|
@@ -1226,10 +1252,10 @@ function staticMetadata(program) {
|
|
|
1226
1252
|
const init = unwrapTs(d.init);
|
|
1227
1253
|
if (d.id?.type !== "Identifier" || d.id.name !== "metadata" || init?.type !== "ObjectExpression") continue;
|
|
1228
1254
|
const out = {};
|
|
1229
|
-
for (const
|
|
1230
|
-
if (
|
|
1231
|
-
const key = keyName(
|
|
1232
|
-
const value = unwrapTs(
|
|
1255
|
+
for (const prop2 of init.properties) {
|
|
1256
|
+
if (prop2.type !== "ObjectProperty") continue;
|
|
1257
|
+
const key = keyName(prop2.key);
|
|
1258
|
+
const value = unwrapTs(prop2.value);
|
|
1233
1259
|
if (key === "description") {
|
|
1234
1260
|
const s = stringValue(value);
|
|
1235
1261
|
if (s) out.description = s;
|
|
@@ -1332,15 +1358,15 @@ function readTsconfig(root) {
|
|
|
1332
1358
|
}
|
|
1333
1359
|
}
|
|
1334
1360
|
function asFile(path) {
|
|
1335
|
-
const
|
|
1336
|
-
if (/\.[mc]?jsx?$/.test(path))
|
|
1337
|
-
return
|
|
1361
|
+
const candidates2 = [path, ...EXTENSIONS.map((e) => path + e), ...EXTENSIONS.map((e) => (0, import_node_path5.join)(path, `index${e}`))];
|
|
1362
|
+
if (/\.[mc]?jsx?$/.test(path)) candidates2.push(...EXTENSIONS.map((e) => path.replace(/\.[mc]?jsx?$/, e)));
|
|
1363
|
+
return candidates2.find((c) => (0, import_node_fs5.existsSync)(c) && (0, import_node_fs5.statSync)(c).isFile());
|
|
1338
1364
|
}
|
|
1339
1365
|
function moduleLoader(root) {
|
|
1340
1366
|
const { baseUrl, paths } = readTsconfig(root);
|
|
1341
1367
|
const base = (0, import_node_path5.join)(root, baseUrl ?? ".");
|
|
1342
1368
|
const cache = /* @__PURE__ */ new Map();
|
|
1343
|
-
const
|
|
1369
|
+
const candidates2 = (specifier2, fromFile) => {
|
|
1344
1370
|
if (specifier2.startsWith(".")) return [(0, import_node_path5.resolve)(root, (0, import_node_path5.dirname)(fromFile), specifier2)];
|
|
1345
1371
|
for (const [pattern, targets] of Object.entries(paths ?? {})) {
|
|
1346
1372
|
const star = pattern.indexOf("*");
|
|
@@ -1357,7 +1383,7 @@ function moduleLoader(root) {
|
|
|
1357
1383
|
const key = `${fromFile}\0${specifier2}`;
|
|
1358
1384
|
if (cache.has(key)) return cache.get(key);
|
|
1359
1385
|
let found;
|
|
1360
|
-
for (const candidate of
|
|
1386
|
+
for (const candidate of candidates2(specifier2, fromFile)) {
|
|
1361
1387
|
const path = asFile(candidate);
|
|
1362
1388
|
const rel = path && (0, import_node_path5.relative)(root, path);
|
|
1363
1389
|
if (!path || !rel || rel.startsWith("..") || rel.split(import_node_path5.sep).includes("node_modules")) continue;
|
|
@@ -1389,11 +1415,11 @@ function writtenInput(n) {
|
|
|
1389
1415
|
n = unwrapTs(n);
|
|
1390
1416
|
if (n?.type !== "ObjectExpression") return "opaque";
|
|
1391
1417
|
const out = {};
|
|
1392
|
-
for (const
|
|
1393
|
-
const key =
|
|
1418
|
+
for (const prop2 of n.properties) {
|
|
1419
|
+
const key = prop2.type === "ObjectProperty" ? keyName(prop2.key) : void 0;
|
|
1394
1420
|
if (!key) continue;
|
|
1395
1421
|
let values;
|
|
1396
|
-
walk(
|
|
1422
|
+
walk(prop2.value, (c) => {
|
|
1397
1423
|
if (values || c.type !== "CallExpression" || keyName(c.callee?.property) !== "enum") return;
|
|
1398
1424
|
const list = unwrapTs(c.arguments[0]);
|
|
1399
1425
|
const items = list?.type === "ArrayExpression" ? list.elements.map((e) => stringValue(e)) : [];
|
|
@@ -1413,13 +1439,13 @@ function markerOf(init, imports) {
|
|
|
1413
1439
|
const meta = call.arguments[1];
|
|
1414
1440
|
const marker = { kind: "described" };
|
|
1415
1441
|
if (meta?.type === "ObjectExpression") {
|
|
1416
|
-
for (const
|
|
1417
|
-
if (
|
|
1418
|
-
const key = keyName(
|
|
1419
|
-
const value = stringValue(
|
|
1442
|
+
for (const prop2 of meta.properties) {
|
|
1443
|
+
if (prop2.type !== "ObjectProperty") continue;
|
|
1444
|
+
const key = keyName(prop2.key);
|
|
1445
|
+
const value = stringValue(prop2.value);
|
|
1420
1446
|
if (key === "name" && value) marker.name = value;
|
|
1421
1447
|
if (key === "description" && value) marker.description = value;
|
|
1422
|
-
if (key === "input") marker.input = writtenInput(
|
|
1448
|
+
if (key === "input") marker.input = writtenInput(prop2.value);
|
|
1423
1449
|
}
|
|
1424
1450
|
}
|
|
1425
1451
|
return { marker, handler };
|
|
@@ -1690,18 +1716,241 @@ function findInlineActions(source, file) {
|
|
|
1690
1716
|
const program = parseModule(source, file);
|
|
1691
1717
|
if (!program) return [];
|
|
1692
1718
|
const out = [];
|
|
1693
|
-
const
|
|
1719
|
+
const named = /* @__PURE__ */ new Map();
|
|
1694
1720
|
walk(program, (n) => {
|
|
1695
|
-
if (n.type === "VariableDeclarator" && n.id.type === "Identifier" && n.init)
|
|
1721
|
+
if (n.type === "VariableDeclarator" && n.id.type === "Identifier" && n.init) named.set(unwrapTs(n.init), n.id.name);
|
|
1696
1722
|
if (!FUNCTION_TYPES.has(n.type) || n.body?.type !== "BlockStatement") return;
|
|
1697
1723
|
if (!n.body.directives.some((d) => d.value?.value === "use server")) return;
|
|
1698
1724
|
if (isActionModule(program) && program.body.some((s) => s === n || s.declaration === n || s.declaration?.declarations?.some((d) => unwrapTs(d.init) === n))) return;
|
|
1699
|
-
const name = n.id?.name ??
|
|
1725
|
+
const name = n.id?.name ?? named.get(n);
|
|
1700
1726
|
out.push({ ...name ? { name } : {}, line: n.loc?.start.line ?? 1 });
|
|
1701
1727
|
});
|
|
1702
1728
|
return out;
|
|
1703
1729
|
}
|
|
1704
1730
|
|
|
1731
|
+
// src/supabase.ts
|
|
1732
|
+
var import_node_fs6 = require("fs");
|
|
1733
|
+
var import_node_path6 = require("path");
|
|
1734
|
+
var AUTH_CALLS = {
|
|
1735
|
+
signInWithPassword: "password",
|
|
1736
|
+
signInWithOtp: "email-code",
|
|
1737
|
+
verifyOtp: "email-code",
|
|
1738
|
+
signInWithOAuth: "social",
|
|
1739
|
+
signInWithIdToken: "other",
|
|
1740
|
+
signInWithSSO: "other",
|
|
1741
|
+
signInAnonymously: "other",
|
|
1742
|
+
signInWithWeb3: "other"
|
|
1743
|
+
};
|
|
1744
|
+
var MFA_CALLS = /* @__PURE__ */ new Set(["challenge", "verify", "challengeAndVerify"]);
|
|
1745
|
+
var APP_SERVER_FILE = /^(page|layout|template|route|default|loading|not-found|forbidden|unauthorized|error|global-error)\.[^.]+$/;
|
|
1746
|
+
var MIDDLEWARE_FILE = /^(src\/)?(middleware|proxy|instrumentation)\.[^./]+$/;
|
|
1747
|
+
var directive2 = (program) => program.directives?.[0]?.value?.value;
|
|
1748
|
+
var baseName = (f) => f.slice(f.lastIndexOf("/") + 1);
|
|
1749
|
+
function isAuthObject(n) {
|
|
1750
|
+
n = unwrapTs(n);
|
|
1751
|
+
if (n?.type === "Identifier") return n.name === "auth";
|
|
1752
|
+
return (n?.type === "MemberExpression" || n?.type === "OptionalMemberExpression") && keyName(n.property) === "auth" && !n.computed;
|
|
1753
|
+
}
|
|
1754
|
+
var propertyOf = (n) => n.computed ? stringValue(n.property) : keyName(n.property);
|
|
1755
|
+
function prop(obj, name) {
|
|
1756
|
+
obj = unwrapTs(obj);
|
|
1757
|
+
if (obj?.type !== "ObjectExpression") return void 0;
|
|
1758
|
+
return obj.properties.find((p) => p.type === "ObjectProperty" && keyName(p.key) === name)?.value;
|
|
1759
|
+
}
|
|
1760
|
+
function findSupabase(root, files, graph, dependencies, dirs) {
|
|
1761
|
+
const ssr = dependencies.includes("@supabase/ssr");
|
|
1762
|
+
if (!ssr) return dependencies.includes("@supabase/auth-helpers-nextjs") ? { authHelpers: true, signIns: [], callbacks: [] } : null;
|
|
1763
|
+
const facts = /* @__PURE__ */ new Map();
|
|
1764
|
+
const factsOf = (file) => {
|
|
1765
|
+
if (facts.has(file)) return facts.get(file);
|
|
1766
|
+
let found = null;
|
|
1767
|
+
try {
|
|
1768
|
+
const source = (0, import_node_fs6.readFileSync)((0, import_node_path6.join)(root, file), "utf8");
|
|
1769
|
+
const program = parseModule(source, file);
|
|
1770
|
+
if (program) {
|
|
1771
|
+
const d = directive2(program);
|
|
1772
|
+
found = {
|
|
1773
|
+
program,
|
|
1774
|
+
...d ? { directive: d } : {},
|
|
1775
|
+
serverClient: /\bcreateServerClient\s*\(|from\s+["']next\/headers["']/.test(source),
|
|
1776
|
+
browserClient: /\bcreateBrowserClient\s*\(/.test(source)
|
|
1777
|
+
};
|
|
1778
|
+
}
|
|
1779
|
+
} catch {
|
|
1780
|
+
found = null;
|
|
1781
|
+
}
|
|
1782
|
+
facts.set(file, found);
|
|
1783
|
+
return found;
|
|
1784
|
+
};
|
|
1785
|
+
const isPagesPage = (f) => Boolean(dirs.pagesDir && f.startsWith(`${dirs.pagesDir}/`) && !f.startsWith(`${dirs.pagesDir}/api/`));
|
|
1786
|
+
const isPagesApi = (f) => Boolean(dirs.pagesDir && f.startsWith(`${dirs.pagesDir}/api/`));
|
|
1787
|
+
const isAppRoute = (f) => Boolean(dirs.appDir && f.startsWith(`${dirs.appDir}/`) && /^route\./.test(baseName(f)));
|
|
1788
|
+
const candidates2 = files.filter((f) => {
|
|
1789
|
+
try {
|
|
1790
|
+
return /\b(signIn\w*|verifyOtp|exchangeCodeForSession|mfa|auth-ui-react|storageKey|cookieOptions)\b/.test((0, import_node_fs6.readFileSync)((0, import_node_path6.join)(root, f), "utf8"));
|
|
1791
|
+
} catch {
|
|
1792
|
+
return false;
|
|
1793
|
+
}
|
|
1794
|
+
});
|
|
1795
|
+
const clientRoots = [];
|
|
1796
|
+
const serverRoots = [];
|
|
1797
|
+
for (const f of files) {
|
|
1798
|
+
if (isPagesPage(f)) {
|
|
1799
|
+
clientRoots.push(f);
|
|
1800
|
+
continue;
|
|
1801
|
+
}
|
|
1802
|
+
const isEntry = isPagesApi(f) || MIDDLEWARE_FILE.test(f) || Boolean(dirs.appDir && f.startsWith(`${dirs.appDir}/`) && APP_SERVER_FILE.test(baseName(f)));
|
|
1803
|
+
let text2;
|
|
1804
|
+
try {
|
|
1805
|
+
text2 = (0, import_node_fs6.readFileSync)((0, import_node_path6.join)(root, f), "utf8");
|
|
1806
|
+
} catch {
|
|
1807
|
+
continue;
|
|
1808
|
+
}
|
|
1809
|
+
const head = /^\s*(?:\/\/[^\n]*\n|\/\*[\s\S]*?\*\/\s*)*\s*["']use (client|server)["']/.exec(text2)?.[1];
|
|
1810
|
+
if (head === "client") clientRoots.push(f);
|
|
1811
|
+
else if (head === "server" || isEntry) serverRoots.push(f);
|
|
1812
|
+
}
|
|
1813
|
+
const browserSet = graph.reachable(clientRoots);
|
|
1814
|
+
const serverSet = /* @__PURE__ */ new Set();
|
|
1815
|
+
const queue = [...serverRoots];
|
|
1816
|
+
while (queue.length > 0 && serverSet.size < 2e3) {
|
|
1817
|
+
const f = queue.shift();
|
|
1818
|
+
if (serverSet.has(f)) continue;
|
|
1819
|
+
if (!serverRoots.includes(f) && factsOf(f)?.directive === "use client") continue;
|
|
1820
|
+
serverSet.add(f);
|
|
1821
|
+
for (const e of graph.edges(f)) if (!serverSet.has(e.file)) queue.push(e.file);
|
|
1822
|
+
}
|
|
1823
|
+
const sideOf = (file, inServerFn) => {
|
|
1824
|
+
const f = factsOf(file);
|
|
1825
|
+
if (inServerFn || f?.directive === "use server") return { side: "server", why: "a Server Action" };
|
|
1826
|
+
if (isAppRoute(file) || isPagesApi(file)) return { side: "server", why: "a route handler" };
|
|
1827
|
+
if (f?.directive === "use client") return { side: "browser", why: '"use client"' };
|
|
1828
|
+
if (isPagesPage(file)) return { side: "browser", why: "a Pages Router page" };
|
|
1829
|
+
let server = f?.serverClient ?? false;
|
|
1830
|
+
let browser = f?.browserClient ?? false;
|
|
1831
|
+
let helper;
|
|
1832
|
+
if (!server && !browser) {
|
|
1833
|
+
for (const e of graph.edges(file)) {
|
|
1834
|
+
const g = factsOf(e.file);
|
|
1835
|
+
if (g?.serverClient && !g.browserClient) {
|
|
1836
|
+
server = true;
|
|
1837
|
+
helper = e.file;
|
|
1838
|
+
}
|
|
1839
|
+
if (g?.browserClient && !g.serverClient) {
|
|
1840
|
+
browser = true;
|
|
1841
|
+
helper = e.file;
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
if (server !== browser) return { side: server ? "server" : "browser", why: `uses the ${server ? "server" : "browser"} client${helper ? ` from ${helper}` : ""}` };
|
|
1846
|
+
const inBrowser = browserSet.has(file);
|
|
1847
|
+
const inServer = serverSet.has(file);
|
|
1848
|
+
if (inBrowser && !inServer) return { side: "browser", why: "imported by browser code" };
|
|
1849
|
+
if (inServer && !inBrowser) return { side: "server", why: "imported by server code" };
|
|
1850
|
+
return { side: "unknown", why: inBrowser ? "imported by both browser and server code" : "not imported by a page or route we could follow" };
|
|
1851
|
+
};
|
|
1852
|
+
const out = { signIns: [], callbacks: [] };
|
|
1853
|
+
for (const file of candidates2) {
|
|
1854
|
+
const f = factsOf(file);
|
|
1855
|
+
if (!f) continue;
|
|
1856
|
+
const top = f.program;
|
|
1857
|
+
const topName = (n) => {
|
|
1858
|
+
const d = n.type === "ExportNamedDeclaration" ? n.declaration : n.type === "ExportDefaultDeclaration" ? n.declaration : n;
|
|
1859
|
+
if (n.type === "ExportDefaultDeclaration") return "default";
|
|
1860
|
+
if (d?.type === "FunctionDeclaration") return d.id?.name;
|
|
1861
|
+
if (d?.type === "VariableDeclaration") return keyName(d.declarations?.[0]?.id);
|
|
1862
|
+
return void 0;
|
|
1863
|
+
};
|
|
1864
|
+
let authUi;
|
|
1865
|
+
walk(top, (n, parents) => {
|
|
1866
|
+
if (n.type === "ImportDeclaration" && n.source?.value === "@supabase/auth-ui-react") {
|
|
1867
|
+
authUi = n.specifiers.find((s) => s.type === "ImportSpecifier" && keyName(s.imported) === "Auth")?.local?.name;
|
|
1868
|
+
}
|
|
1869
|
+
if (n.type === "ObjectProperty" && !out.cookieName) {
|
|
1870
|
+
const key = keyName(n.key);
|
|
1871
|
+
const value = stringValue(unwrapTs(n.value));
|
|
1872
|
+
const parentProp = parents.at(-2);
|
|
1873
|
+
if (key === "storageKey" && value) out.cookieName = value;
|
|
1874
|
+
if (key === "name" && value && parentProp?.type === "ObjectProperty" && keyName(parentProp.key) === "cookieOptions") out.cookieName = value;
|
|
1875
|
+
}
|
|
1876
|
+
const push = (call, method2, extra = {}) => {
|
|
1877
|
+
const inServerFn = parents.some((p) => FUNCTION_TYPES.has(p.type) && p.body?.directives?.some((d) => d.value?.value === "use server"));
|
|
1878
|
+
const { side, why } = sideOf(file, inServerFn);
|
|
1879
|
+
const holder = parents.find((p) => top.body.includes(p));
|
|
1880
|
+
const name2 = holder ? topName(holder) : void 0;
|
|
1881
|
+
out.signIns.push({
|
|
1882
|
+
method: method2,
|
|
1883
|
+
call,
|
|
1884
|
+
...extra,
|
|
1885
|
+
file,
|
|
1886
|
+
line: n.loc?.start.line ?? 1,
|
|
1887
|
+
side,
|
|
1888
|
+
why,
|
|
1889
|
+
...side === "server" && name2 && (isActionModule(top) || inServerFn) ? { action: `${file}#${name2}` } : {},
|
|
1890
|
+
...side === "server" && (isAppRoute(file) || isPagesApi(file)) ? { route: file } : {}
|
|
1891
|
+
});
|
|
1892
|
+
};
|
|
1893
|
+
if (n.type === "JSXOpeningElement" && authUi && n.name?.type === "JSXIdentifier" && n.name.name === authUi) {
|
|
1894
|
+
push("<Auth />", "password");
|
|
1895
|
+
return;
|
|
1896
|
+
}
|
|
1897
|
+
if (n.type !== "CallExpression" && n.type !== "OptionalCallExpression") return;
|
|
1898
|
+
const callee = unwrapTs(n.callee);
|
|
1899
|
+
if (callee?.type !== "MemberExpression" && callee?.type !== "OptionalMemberExpression") return;
|
|
1900
|
+
const name = propertyOf(callee);
|
|
1901
|
+
if (!name) return;
|
|
1902
|
+
const object = unwrapTs(callee.object);
|
|
1903
|
+
if (MFA_CALLS.has(name) && (object?.type === "MemberExpression" || object?.type === "OptionalMemberExpression") && propertyOf(object) === "mfa" && isAuthObject(object.object)) {
|
|
1904
|
+
push(`mfa.${name}`, "totp");
|
|
1905
|
+
return;
|
|
1906
|
+
}
|
|
1907
|
+
if (!isAuthObject(object)) return;
|
|
1908
|
+
const arg = n.arguments?.[0];
|
|
1909
|
+
if (name === "exchangeCodeForSession" || name === "verifyOtp" && prop(arg, "token_hash")) {
|
|
1910
|
+
if (!out.callbacks.includes(file)) out.callbacks.push(file);
|
|
1911
|
+
return;
|
|
1912
|
+
}
|
|
1913
|
+
const method = Object.hasOwn(AUTH_CALLS, name) ? AUTH_CALLS[name] : void 0;
|
|
1914
|
+
if (!method) return;
|
|
1915
|
+
const provider = name === "signInWithOAuth" ? stringValue(unwrapTs(prop(arg, "provider"))) : void 0;
|
|
1916
|
+
push(name, method, provider ? { provider } : {});
|
|
1917
|
+
});
|
|
1918
|
+
}
|
|
1919
|
+
return out;
|
|
1920
|
+
}
|
|
1921
|
+
function signInLabel(s) {
|
|
1922
|
+
const side = s.side === "unknown" ? "side unclear, counted as server" : s.side;
|
|
1923
|
+
return `${s.call}${s.provider ? ` (${s.provider})` : ""} in ${s.file}:${s.line} (${side}: ${s.why})`;
|
|
1924
|
+
}
|
|
1925
|
+
function supabaseChoice(found, login, loginPages2 = []) {
|
|
1926
|
+
const onServer = (method) => found.signIns.filter((s) => s.method === method && s.side !== "browser");
|
|
1927
|
+
const direct = [];
|
|
1928
|
+
const unusable = [];
|
|
1929
|
+
for (const method of ["password", "email-code"]) {
|
|
1930
|
+
const server = onServer(method);
|
|
1931
|
+
if (server.length === 0) direct.push(method);
|
|
1932
|
+
else if (!login) unusable.push(...server.map(signInLabel));
|
|
1933
|
+
}
|
|
1934
|
+
direct.push("social");
|
|
1935
|
+
return {
|
|
1936
|
+
...found.cookieName ? { cookieName: found.cookieName } : {},
|
|
1937
|
+
direct,
|
|
1938
|
+
found: found.signIns.map(signInLabel),
|
|
1939
|
+
...unusable.length ? { unusable } : {},
|
|
1940
|
+
...found.callbacks.length ? { callbacks: found.callbacks } : {},
|
|
1941
|
+
...loginPages2.length ? { loginPages: loginPages2 } : {}
|
|
1942
|
+
};
|
|
1943
|
+
}
|
|
1944
|
+
function knownLogins(found) {
|
|
1945
|
+
const out = /* @__PURE__ */ new Set();
|
|
1946
|
+
for (const s of found?.signIns ?? []) {
|
|
1947
|
+
if (s.method !== "password" || s.side === "browser") continue;
|
|
1948
|
+
if (s.action) out.add(s.action);
|
|
1949
|
+
if (s.route) out.add(s.route);
|
|
1950
|
+
}
|
|
1951
|
+
return out;
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1705
1954
|
// src/codegen.ts
|
|
1706
1955
|
var DEFAULT_DOCS = ["README.md", "docs/**/*.md"];
|
|
1707
1956
|
var actionRoute = (mcpPath, key) => `POST ${(mcpPath ?? "/mcp").replace(/\/$/, "")}/actions/${key}`;
|
|
@@ -1709,16 +1958,16 @@ function actionSignature(route, source) {
|
|
|
1709
1958
|
const [method, path] = route.split(" ");
|
|
1710
1959
|
return (0, import_node_crypto.createHash)("sha256").update(JSON.stringify([method, path, [], source ?? ""])).digest("hex").slice(0, 16);
|
|
1711
1960
|
}
|
|
1712
|
-
var posix = (p) => p.split(
|
|
1961
|
+
var posix = (p) => p.split(import_node_path7.sep).join("/");
|
|
1713
1962
|
function firstDir(root, names) {
|
|
1714
|
-
return names.find((n) => (0,
|
|
1963
|
+
return names.find((n) => (0, import_node_fs7.existsSync)((0, import_node_path7.join)(root, n)));
|
|
1715
1964
|
}
|
|
1716
1965
|
function filesUnder(root, dir) {
|
|
1717
1966
|
const out = [];
|
|
1718
1967
|
const visit = (rel) => {
|
|
1719
1968
|
let entries;
|
|
1720
1969
|
try {
|
|
1721
|
-
entries = (0,
|
|
1970
|
+
entries = (0, import_node_fs7.readdirSync)((0, import_node_path7.join)(root, rel), { withFileTypes: true });
|
|
1722
1971
|
} catch {
|
|
1723
1972
|
return;
|
|
1724
1973
|
}
|
|
@@ -1745,7 +1994,7 @@ function discover(root, opts = {}) {
|
|
|
1745
1994
|
const dirSegs = file.slice(appDir.length + 1).split("/").slice(0, -1);
|
|
1746
1995
|
const route = appRoute(dirSegs);
|
|
1747
1996
|
if (!route) continue;
|
|
1748
|
-
const source = (0,
|
|
1997
|
+
const source = (0, import_node_fs7.readFileSync)((0, import_node_path7.join)(root, file), "utf8");
|
|
1749
1998
|
const module2 = analyzeRouteModule(source, file);
|
|
1750
1999
|
if (module2.mcpRoute) {
|
|
1751
2000
|
out.mcpRoute ??= { file, source };
|
|
@@ -1761,7 +2010,7 @@ function discover(root, opts = {}) {
|
|
|
1761
2010
|
const segs = file.slice(pagesDir.length + 1).replace(extPattern, "").split("/");
|
|
1762
2011
|
const route = pagesApiRoute(segs);
|
|
1763
2012
|
if (!route) continue;
|
|
1764
|
-
const source = (0,
|
|
2013
|
+
const source = (0, import_node_fs7.readFileSync)((0, import_node_path7.join)(root, file), "utf8");
|
|
1765
2014
|
const src = defaultExportSource(source, file);
|
|
1766
2015
|
out.routes.push({ router: "pages", file, ...route, detected: detectPagesMethods(source, file), ...src ? { source: src } : {} });
|
|
1767
2016
|
}
|
|
@@ -1773,14 +2022,22 @@ function discover(root, opts = {}) {
|
|
|
1773
2022
|
const catchAll = route ? /^(.*)\/:[A-Za-z0-9_]+\*\??$/.exec(route.path) : null;
|
|
1774
2023
|
if (catchAll) out.mcpPath = catchAll[1] || "/";
|
|
1775
2024
|
}
|
|
1776
|
-
discoverActions(root, out, exts);
|
|
1777
|
-
const
|
|
2025
|
+
const { files, graph, pages } = discoverActions(root, out, exts);
|
|
2026
|
+
const supabase = findSupabase(root, files, graph, (0, import_core2.dependencyNames)(readJson((0, import_node_path7.join)(root, "package.json"))), out);
|
|
2027
|
+
const found = findLogin(out, knownLogins(supabase));
|
|
1778
2028
|
if (found) {
|
|
1779
2029
|
out.login = found.login;
|
|
1780
2030
|
out.loginOthers = found.others;
|
|
1781
2031
|
}
|
|
2032
|
+
if (supabase && !supabase.authHelpers) out.supabase = { found: supabase, choice: supabaseChoice(supabase, out.login, loginPages(supabase, pages, graph)) };
|
|
2033
|
+
else if (supabase) out.supabase = { found: supabase, choice: { direct: [], found: [] } };
|
|
1782
2034
|
return out;
|
|
1783
2035
|
}
|
|
2036
|
+
function loginPages(found, pages, graph) {
|
|
2037
|
+
const files = new Set(found.signIns.filter((s) => s.method === "password" || s.method === "email-code").map((s) => s.file));
|
|
2038
|
+
if (files.size === 0) return [];
|
|
2039
|
+
return pages.filter((p) => p.params.length === 0 && [...graph.reachable(p.roots)].some((f) => files.has(f))).map((p) => p.path);
|
|
2040
|
+
}
|
|
1784
2041
|
var SOURCE_GLOB = "**/*.{ts,tsx,js,jsx,mjs,cjs}";
|
|
1785
2042
|
var NOT_SOURCE = ["**/node_modules/**", ".next/**", "out/**", "build/**", "dist/**", "coverage/**", "**/*.d.ts"];
|
|
1786
2043
|
function snakeCase(name) {
|
|
@@ -1794,7 +2051,7 @@ function discoverActions(root, out, exts) {
|
|
|
1794
2051
|
for (const file of files) {
|
|
1795
2052
|
let source;
|
|
1796
2053
|
try {
|
|
1797
|
-
source = (0,
|
|
2054
|
+
source = (0, import_node_fs7.readFileSync)((0, import_node_path7.join)(root, file), "utf8");
|
|
1798
2055
|
} catch {
|
|
1799
2056
|
continue;
|
|
1800
2057
|
}
|
|
@@ -1851,24 +2108,25 @@ function discoverActions(root, out, exts) {
|
|
|
1851
2108
|
const found2 = guards.action(a.file, a.action.name);
|
|
1852
2109
|
if (found2.length > 0) a.guards = found2;
|
|
1853
2110
|
}
|
|
2111
|
+
return { files, graph, pages };
|
|
1854
2112
|
}
|
|
1855
2113
|
function readJson(path) {
|
|
1856
2114
|
try {
|
|
1857
|
-
return JSON.parse((0,
|
|
2115
|
+
return JSON.parse((0, import_node_fs7.readFileSync)(path, "utf8"));
|
|
1858
2116
|
} catch {
|
|
1859
2117
|
return void 0;
|
|
1860
2118
|
}
|
|
1861
2119
|
}
|
|
1862
2120
|
function readProposedInputs(root) {
|
|
1863
|
-
const path = (0,
|
|
1864
|
-
if (!(0,
|
|
1865
|
-
const parsed = (0, import_core2.parseDescriptions)((0,
|
|
2121
|
+
const path = (0, import_node_path7.join)(root, import_core2.DESCRIPTIONS_FILE);
|
|
2122
|
+
if (!(0, import_node_fs7.existsSync)(path)) return {};
|
|
2123
|
+
const parsed = (0, import_core2.parseDescriptions)((0, import_node_fs7.readFileSync)(path, "utf8"));
|
|
1866
2124
|
return typeof parsed === "string" ? {} : parsed.inputs ?? {};
|
|
1867
2125
|
}
|
|
1868
2126
|
function readDescriptions(root, warn) {
|
|
1869
|
-
const path = (0,
|
|
1870
|
-
if (!(0,
|
|
1871
|
-
const parsed = (0, import_core2.parseDescriptions)((0,
|
|
2127
|
+
const path = (0, import_node_path7.join)(root, import_core2.DESCRIPTIONS_FILE);
|
|
2128
|
+
if (!(0, import_node_fs7.existsSync)(path)) return {};
|
|
2129
|
+
const parsed = (0, import_core2.parseDescriptions)((0, import_node_fs7.readFileSync)(path, "utf8"));
|
|
1872
2130
|
if (typeof parsed === "string") {
|
|
1873
2131
|
warn(`ignoring ${import_core2.DESCRIPTIONS_FILE}: ${parsed}`);
|
|
1874
2132
|
return {};
|
|
@@ -1880,22 +2138,28 @@ function readDocs(root, globs) {
|
|
|
1880
2138
|
const docs = [];
|
|
1881
2139
|
for (const path of paths) {
|
|
1882
2140
|
try {
|
|
1883
|
-
docs.push({ path, content: (0,
|
|
2141
|
+
docs.push({ path, content: (0, import_node_fs7.readFileSync)((0, import_node_path7.join)(root, path), "utf8") });
|
|
1884
2142
|
} catch {
|
|
1885
2143
|
}
|
|
1886
2144
|
}
|
|
1887
2145
|
return docs;
|
|
1888
2146
|
}
|
|
1889
2147
|
function defaultOutFile(root) {
|
|
1890
|
-
return (0,
|
|
2148
|
+
return (0, import_node_fs7.existsSync)((0, import_node_path7.join)(root, "tsconfig.json")) ? "exposify.generated.ts" : "exposify.generated.js";
|
|
1891
2149
|
}
|
|
1892
2150
|
function specifier(outFile, file) {
|
|
1893
|
-
const rel = posix((0,
|
|
2151
|
+
const rel = posix((0, import_node_path7.relative)((0, import_node_path7.dirname)(outFile), file)).replace(/\.[^./]+$/, "");
|
|
1894
2152
|
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
1895
2153
|
}
|
|
1896
2154
|
function manifestLogin(l) {
|
|
1897
|
-
|
|
2155
|
+
const verify = l.verify ? manifestStep(l.verify) : void 0;
|
|
2156
|
+
const where = l.verify ? `${whereOf(l)}, then a code: ${whereOf(l.verify)}` : whereOf(l);
|
|
2157
|
+
return { ...manifestStep(l), where, ...l.passwordless ? { passwordless: true } : {}, ...verify ? { verify } : {} };
|
|
1898
2158
|
}
|
|
2159
|
+
function manifestStep(l) {
|
|
2160
|
+
return l.kind === "action" ? { kind: "action", key: l.key, fields: l.fields, ...l.host ? { host: l.host } : {} } : { kind: "route", path: l.path, fields: l.fields, body: l.body };
|
|
2161
|
+
}
|
|
2162
|
+
var whereOf = (l) => l.kind === "action" ? `${l.export} in ${l.file}` : `POST ${l.path} in ${l.file}`;
|
|
1899
2163
|
function renderManifest(d, opts) {
|
|
1900
2164
|
const ts = opts.outFile.endsWith(".ts");
|
|
1901
2165
|
const j = (v) => JSON.stringify(v);
|
|
@@ -1930,22 +2194,23 @@ function renderManifest(d, opts) {
|
|
|
1930
2194
|
return ` { name: ${j(p.name)}, path: ${j(p.path)}, params: ${j(p.params)}, file: ${j(p.file)}${extra} },`;
|
|
1931
2195
|
});
|
|
1932
2196
|
const lines = [
|
|
1933
|
-
"// Generated by exposify
|
|
2197
|
+
"// Generated by @exposify/next from your route files. Don't edit: withExposify()",
|
|
1934
2198
|
"// in next.config regenerates it on `next dev` and `next build`, or run",
|
|
1935
|
-
"// `npx exposify
|
|
2199
|
+
"// `npx @exposify/next generate`.",
|
|
1936
2200
|
"/* eslint-disable */",
|
|
1937
2201
|
...ts ? ['import type { ExposifyManifest } from "@exposify/next";', ""] : ["", '/** @type {import("@exposify/next").ExposifyManifest} */'],
|
|
1938
2202
|
`export const exposify${ts ? ": ExposifyManifest" : ""} = {`,
|
|
1939
2203
|
` info: ${j(opts.info)},`,
|
|
1940
2204
|
...opts.basePath ? [` basePath: ${j(opts.basePath)},`] : [],
|
|
1941
2205
|
...opts.trailingSlash ? [" trailingSlash: true,"] : [],
|
|
1942
|
-
...d.mcpPath && (actions.length > 0 || d.login) ? [` mcpPath: ${j(d.mcpPath)},`] : [],
|
|
2206
|
+
...d.mcpPath && (actions.length > 0 || d.login || d.supabase) ? [` mcpPath: ${j(d.mcpPath)},`] : [],
|
|
1943
2207
|
" routes: [",
|
|
1944
2208
|
...routes,
|
|
1945
2209
|
" ],",
|
|
1946
2210
|
...actions.length > 0 ? [" actions: [", ...actions, " ],"] : [],
|
|
1947
2211
|
...pages.length > 0 ? [" pages: [", ...pages, " ],"] : [],
|
|
1948
2212
|
...d.login ? [` login: ${j(manifestLogin(d.login))},`] : [],
|
|
2213
|
+
...d.supabase && !d.supabase.found.authHelpers ? [` supabase: ${j(d.supabase.choice)},`] : [],
|
|
1949
2214
|
...opts.authStacks?.length ? [` authStacks: ${j(opts.authStacks)},`] : [],
|
|
1950
2215
|
` docs: ${j(opts.docs)},`,
|
|
1951
2216
|
...Object.keys(opts.descriptions).length > 0 ? [` descriptions: ${JSON.stringify(opts.descriptions, null, 2).replace(/\n/g, "\n ")},`] : [],
|
|
@@ -1957,7 +2222,7 @@ function renderManifest(d, opts) {
|
|
|
1957
2222
|
function generate(root, opts = {}, warn = (m) => console.warn(`[exposify] ${m}`)) {
|
|
1958
2223
|
const outFile = opts.out ?? defaultOutFile(root);
|
|
1959
2224
|
const discovery = discover(root, opts);
|
|
1960
|
-
const pkg = readJson((0,
|
|
2225
|
+
const pkg = readJson((0, import_node_path7.join)(root, "package.json"));
|
|
1961
2226
|
const info = { name: typeof pkg?.name === "string" ? pkg.name : "next-app", version: typeof pkg?.version === "string" ? pkg.version : "0.0.0" };
|
|
1962
2227
|
const content = renderManifest(discovery, {
|
|
1963
2228
|
outFile,
|
|
@@ -1968,22 +2233,28 @@ function generate(root, opts = {}, warn = (m) => console.warn(`[exposify] ${m}`)
|
|
|
1968
2233
|
...opts.trailingSlash ? { trailingSlash: true } : {},
|
|
1969
2234
|
authStacks: (0, import_core2.authStacksOf)((0, import_core2.dependencyNames)(pkg)).map((s) => s.pkg)
|
|
1970
2235
|
});
|
|
2236
|
+
if (discovery.supabase?.found.authHelpers) {
|
|
2237
|
+
warn("@supabase/auth-helpers-nextjs keeps its session in an older cookie format AI assistants can't sign in with; move to @supabase/ssr (Supabase's replacement for it) to use Connect");
|
|
2238
|
+
}
|
|
2239
|
+
for (const s of discovery.supabase?.choice.unusable ?? []) {
|
|
2240
|
+
warn(`your app signs users in to Supabase on the server (${s}), but not in a Server Action or route the sign-in form can run, so AI assistants can't sign in with a password; switch on "Sign in with Supabase" on the dashboard to have them sign in to Supabase directly`);
|
|
2241
|
+
}
|
|
1971
2242
|
for (const a of discovery.actions.filter((x) => x.proposed?.status === "stale")) {
|
|
1972
|
-
warn(`the input accepted for the ${a.action.name} server action (${a.file}) is out of date, since the action changed: it's not used until
|
|
2243
|
+
warn(`the input accepted for the ${a.action.name} server action (${a.file}) is out of date, since the action changed: it's not used until \`@exposify/next describe\` reviews it again`);
|
|
1973
2244
|
}
|
|
1974
2245
|
if (discovery.actions.length > 0 && !discovery.mcpPath) {
|
|
1975
2246
|
warn(
|
|
1976
|
-
discovery.mcpRoute ? `found ${discovery.actions.length} Server Action(s), but the MCP route (${discovery.mcpRoute.file}) isn't a catch-all route, which they're served under: move it to app/mcp/[[...transport]]/route.ts` : `found ${discovery.actions.length} Server Action(s), but no MCP route to serve them under: create app/mcp/[[...transport]]/route.ts (npx exposify
|
|
2247
|
+
discovery.mcpRoute ? `found ${discovery.actions.length} Server Action(s), but the MCP route (${discovery.mcpRoute.file}) isn't a catch-all route, which they're served under: move it to app/mcp/[[...transport]]/route.ts` : `found ${discovery.actions.length} Server Action(s), but no MCP route to serve them under: create app/mcp/[[...transport]]/route.ts (npx @exposify/next setup)`
|
|
1977
2248
|
);
|
|
1978
2249
|
}
|
|
1979
|
-
const file = (0,
|
|
2250
|
+
const file = (0, import_node_path7.join)(root, outFile);
|
|
1980
2251
|
let current;
|
|
1981
2252
|
try {
|
|
1982
|
-
current = (0,
|
|
2253
|
+
current = (0, import_node_fs7.readFileSync)(file, "utf8");
|
|
1983
2254
|
} catch {
|
|
1984
2255
|
current = void 0;
|
|
1985
2256
|
}
|
|
1986
|
-
if (current !== content) (0,
|
|
2257
|
+
if (current !== content) (0, import_node_fs7.writeFileSync)(file, content);
|
|
1987
2258
|
return { file, changed: current !== content, discovery };
|
|
1988
2259
|
}
|
|
1989
2260
|
|
|
@@ -2026,14 +2297,14 @@ function watchRoutes(root, run) {
|
|
|
2026
2297
|
timer = setTimeout(run, 150);
|
|
2027
2298
|
};
|
|
2028
2299
|
for (const dir of ["app", "pages", "src", "lib", "actions", "server"]) {
|
|
2029
|
-
if (!(0,
|
|
2300
|
+
if (!(0, import_node_fs8.existsSync)((0, import_node_path8.join)(root, dir))) continue;
|
|
2030
2301
|
try {
|
|
2031
|
-
(0,
|
|
2302
|
+
(0, import_node_fs8.watch)((0, import_node_path8.join)(root, dir), { recursive: true }, schedule).unref();
|
|
2032
2303
|
} catch {
|
|
2033
2304
|
}
|
|
2034
2305
|
}
|
|
2035
2306
|
try {
|
|
2036
|
-
(0,
|
|
2307
|
+
(0, import_node_fs8.watch)(root, (_event, name) => {
|
|
2037
2308
|
if (name === import_core3.DESCRIPTIONS_FILE) schedule();
|
|
2038
2309
|
}).unref();
|
|
2039
2310
|
} catch {
|