@nextsparkjs/core 0.1.0-beta.130 → 0.1.0-beta.132
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/dashboard/block-editor/builder-editor-view.d.ts +2 -1
- package/dist/components/dashboard/block-editor/builder-editor-view.d.ts.map +1 -1
- package/dist/components/dashboard/block-editor/builder-editor-view.js +3 -2
- package/dist/components/entities/EntityFieldRenderer.d.ts.map +1 -1
- package/dist/components/entities/EntityFieldRenderer.js +3 -2
- package/dist/components/media/MediaSelector.d.ts +3 -3
- package/dist/components/media/MediaSelector.d.ts.map +1 -1
- package/dist/components/media/MediaSelector.js +4 -3
- package/dist/lib/api/entity/generic-handler.js +4 -4
- package/dist/lib/entities/query-generator.js +2 -1
- package/dist/lib/entities/schema-generator.d.ts.map +1 -1
- package/dist/lib/entities/schema-generator.js +2 -1
- package/dist/messages/en/admin.json +17 -2
- package/dist/messages/en/index.d.ts +15 -0
- package/dist/messages/en/index.d.ts.map +1 -1
- package/dist/messages/es/admin.json +17 -2
- package/dist/messages/es/index.d.ts +15 -0
- package/dist/messages/es/index.d.ts.map +1 -1
- package/dist/styles/classes.json +1 -1
- package/dist/templates/app/(auth)/accept-invite/[token]/page.tsx +1 -1
- package/dist/templates/app/api/v1/billing/webhooks/polar/route.ts +10 -2
- package/dist/templates/app/api/v1/billing/webhooks/stripe/route.ts +12 -3
- package/package.json +2 -2
- package/scripts/build/registry/discovery/templates.mjs +59 -5
- package/scripts/build/registry/generators/template-registry.mjs +7 -5
- package/templates/app/(auth)/accept-invite/[token]/page.tsx +1 -1
- package/templates/app/api/v1/billing/webhooks/polar/route.ts +10 -2
- package/templates/app/api/v1/billing/webhooks/stripe/route.ts +12 -3
|
@@ -4,6 +4,7 @@ export interface BuilderEditorViewProps {
|
|
|
4
4
|
entityConfig: ClientEntityConfig;
|
|
5
5
|
id?: string;
|
|
6
6
|
mode: 'create' | 'edit';
|
|
7
|
+
onEntityFieldChange?: (field: string, value: unknown) => void;
|
|
7
8
|
}
|
|
8
|
-
export declare function BuilderEditorView({ entitySlug, entityConfig, id, mode }: BuilderEditorViewProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export declare function BuilderEditorView({ entitySlug, entityConfig, id, mode, onEntityFieldChange: onEntityFieldChangeProp }: BuilderEditorViewProps): import("react/jsx-runtime").JSX.Element;
|
|
9
10
|
//# sourceMappingURL=builder-editor-view.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder-editor-view.d.ts","sourceRoot":"","sources":["../../../../src/components/dashboard/block-editor/builder-editor-view.tsx"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gDAAgD,CAAA;AAwCxF,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,kBAAkB,CAAA;IAChC,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"builder-editor-view.d.ts","sourceRoot":"","sources":["../../../../src/components/dashboard/block-editor/builder-editor-view.tsx"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gDAAgD,CAAA;AAwCxF,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,kBAAkB,CAAA;IAChC,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAA;IACvB,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;CAC9D;AAED,wBAAgB,iBAAiB,CAAC,EAAE,UAAU,EAAE,YAAY,EAAE,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,EAAE,sBAAsB,2CAgsB7I"}
|
|
@@ -41,7 +41,7 @@ function buildApiHeaders(includeContentType = false) {
|
|
|
41
41
|
function slugify(text) {
|
|
42
42
|
return text.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9\s-]/g, "").trim().replace(/\s+/g, "-").replace(/-+/g, "-");
|
|
43
43
|
}
|
|
44
|
-
function BuilderEditorView({ entitySlug, entityConfig, id, mode }) {
|
|
44
|
+
function BuilderEditorView({ entitySlug, entityConfig, id, mode, onEntityFieldChange: onEntityFieldChangeProp }) {
|
|
45
45
|
var _a;
|
|
46
46
|
const router = useRouter();
|
|
47
47
|
const t = useTranslations("admin.builder");
|
|
@@ -311,7 +311,8 @@ function BuilderEditorView({ entitySlug, entityConfig, id, mode }) {
|
|
|
311
311
|
}, []);
|
|
312
312
|
const handleEntityFieldChange = useCallback((field, value) => {
|
|
313
313
|
setEntityFields((prev) => ({ ...prev, [field]: value }));
|
|
314
|
-
|
|
314
|
+
onEntityFieldChangeProp == null ? void 0 : onEntityFieldChangeProp(field, value);
|
|
315
|
+
}, [onEntityFieldChangeProp]);
|
|
315
316
|
const handleLeftSidebarToggle = useCallback((value) => {
|
|
316
317
|
if (!value || value === leftSidebarMode) {
|
|
317
318
|
setLeftSidebarMode("none");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityFieldRenderer.d.ts","sourceRoot":"","sources":["../../../src/components/entities/EntityFieldRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AA6CH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"EntityFieldRenderer.d.ts","sourceRoot":"","sources":["../../../src/components/entities/EntityFieldRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AA6CH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAK3D,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,WAAW,CAAA;IAClB,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IACnC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAA;IACjC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE;QACR,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KACvB,CAAA;CACF;AAg0BD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,QAAgB,EAChB,QAAQ,EACR,SAAS,EACT,MAAM,EACN,OAAO,GACR,EAAE,wBAAwB,2CAyC1B"}
|
|
@@ -30,6 +30,7 @@ import { MediaSelector } from "../media/MediaSelector.js";
|
|
|
30
30
|
import { UserSelect } from "../ui/user-select.js";
|
|
31
31
|
import { ButtonGroup } from "../ui/button-group.js";
|
|
32
32
|
import { DoubleRange } from "../ui/double-range.js";
|
|
33
|
+
import { resolveMediaUrl } from "../../types/blocks.js";
|
|
33
34
|
function formatDisplayValue(value, field) {
|
|
34
35
|
if (value === null || value === void 0) {
|
|
35
36
|
return "-";
|
|
@@ -89,7 +90,7 @@ function formatDisplayValue(value, field) {
|
|
|
89
90
|
}
|
|
90
91
|
return String(value || "");
|
|
91
92
|
case "media-library":
|
|
92
|
-
return
|
|
93
|
+
return resolveMediaUrl(value) || "";
|
|
93
94
|
case "file":
|
|
94
95
|
case "image":
|
|
95
96
|
case "video":
|
|
@@ -402,7 +403,7 @@ function renderFormField(field, value, onChange, error, disabled, required, test
|
|
|
402
403
|
MediaSelector,
|
|
403
404
|
{
|
|
404
405
|
value,
|
|
405
|
-
onChange: (
|
|
406
|
+
onChange: (ref) => onChange(ref),
|
|
406
407
|
disabled
|
|
407
408
|
}
|
|
408
409
|
);
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
* Opens MediaLibrary modal and displays selected media preview.
|
|
6
6
|
* Integrates with React Hook Form if used in a form context.
|
|
7
7
|
*/
|
|
8
|
-
import type {
|
|
8
|
+
import type { MediaRef } from '../../types/blocks';
|
|
9
9
|
interface MediaSelectorProps {
|
|
10
|
-
value?:
|
|
11
|
-
onChange?: (
|
|
10
|
+
value?: MediaRef | null;
|
|
11
|
+
onChange?: (ref: MediaRef | null) => void;
|
|
12
12
|
mode?: 'single';
|
|
13
13
|
allowedTypes?: ('image' | 'video')[];
|
|
14
14
|
className?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MediaSelector.d.ts","sourceRoot":"","sources":["../../../src/components/media/MediaSelector.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"MediaSelector.d.ts","sourceRoot":"","sources":["../../../src/components/media/MediaSelector.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAcH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAGlD,UAAU,kBAAkB;IAC1B,KAAK,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAA;IACvB,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAA;IACzC,IAAI,CAAC,EAAE,QAAQ,CAAA;IACf,YAAY,CAAC,EAAE,CAAC,OAAO,GAAG,OAAO,CAAC,EAAE,CAAA;IACpC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,QAAQ,EACR,IAAe,EACf,YAAY,EACZ,SAAS,EACT,QAAgB,GACjB,EAAE,kBAAkB,2CA8IpB"}
|
|
@@ -19,16 +19,17 @@ function MediaSelector({
|
|
|
19
19
|
}) {
|
|
20
20
|
const t = useTranslations("media");
|
|
21
21
|
const [isLibraryOpen, setIsLibraryOpen] = useState(false);
|
|
22
|
-
const
|
|
22
|
+
const mediaId = value ? typeof value === "string" ? value : value.mediaId : null;
|
|
23
|
+
const { data: selectedMedia, isLoading, isError } = useMediaItem(mediaId);
|
|
23
24
|
const handleSelect = (media) => {
|
|
24
25
|
if (Array.isArray(media)) {
|
|
25
26
|
return;
|
|
26
27
|
}
|
|
27
|
-
onChange == null ? void 0 : onChange(media.id, media);
|
|
28
|
+
onChange == null ? void 0 : onChange({ mediaId: media.id, url: media.url });
|
|
28
29
|
setIsLibraryOpen(false);
|
|
29
30
|
};
|
|
30
31
|
const handleRemove = () => {
|
|
31
|
-
onChange == null ? void 0 : onChange(null
|
|
32
|
+
onChange == null ? void 0 : onChange(null);
|
|
32
33
|
};
|
|
33
34
|
const handleOpenLibrary = () => {
|
|
34
35
|
if (!disabled) {
|
|
@@ -239,7 +239,7 @@ async function handleGenericList(request) {
|
|
|
239
239
|
let teamId = null;
|
|
240
240
|
let isBypass = false;
|
|
241
241
|
if (!authResult.success && ((_a = resolution.entityConfig.access) == null ? void 0 : _a.public)) {
|
|
242
|
-
|
|
242
|
+
teamId = request.headers.get("x-team-id") ?? null;
|
|
243
243
|
} else if (!authResult.success) {
|
|
244
244
|
return NextResponse.json(
|
|
245
245
|
{ success: false, error: "Authentication required", code: "AUTHENTICATION_FAILED" },
|
|
@@ -338,7 +338,7 @@ async function handleGenericList(request) {
|
|
|
338
338
|
const idPlaceholders = specificIds.map(() => `$${paramIndex++}`).join(", ");
|
|
339
339
|
query = `SELECT ${fields} FROM "${tableName}" t WHERE t.id IN (${idPlaceholders})`;
|
|
340
340
|
queryParams = [...specificIds];
|
|
341
|
-
if (teamId
|
|
341
|
+
if (teamId) {
|
|
342
342
|
query += ` AND t."teamId" = $${paramIndex++}`;
|
|
343
343
|
queryParams.push(teamId);
|
|
344
344
|
}
|
|
@@ -354,7 +354,7 @@ async function handleGenericList(request) {
|
|
|
354
354
|
const parentId = url.searchParams.get("parentId");
|
|
355
355
|
query = `SELECT ${fields} FROM "${tableName}" t WHERE 1=1`;
|
|
356
356
|
queryParams = [];
|
|
357
|
-
if (teamId
|
|
357
|
+
if (teamId) {
|
|
358
358
|
query += ` AND t."teamId" = $${paramIndex++}`;
|
|
359
359
|
queryParams.push(teamId);
|
|
360
360
|
}
|
|
@@ -375,7 +375,7 @@ async function handleGenericList(request) {
|
|
|
375
375
|
const whereConditions = [];
|
|
376
376
|
queryParams = [];
|
|
377
377
|
paramIndex = 1;
|
|
378
|
-
if (teamId
|
|
378
|
+
if (teamId) {
|
|
379
379
|
whereConditions.push(`t."teamId" = $${paramIndex++}`);
|
|
380
380
|
queryParams.push(teamId);
|
|
381
381
|
}
|
|
@@ -404,7 +404,8 @@ function formatFieldValue(value, field) {
|
|
|
404
404
|
}
|
|
405
405
|
switch (field.type) {
|
|
406
406
|
case "json":
|
|
407
|
-
|
|
407
|
+
case "media-library":
|
|
408
|
+
return typeof value === "object" ? JSON.stringify(value) : value;
|
|
408
409
|
case "boolean":
|
|
409
410
|
return Boolean(value);
|
|
410
411
|
case "number":
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-generator.d.ts","sourceRoot":"","sources":["../../../src/lib/entities/schema-generator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,YAAY,EAAsC,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"schema-generator.d.ts","sourceRoot":"","sources":["../../../src/lib/entities/schema-generator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,YAAY,EAAsC,MAAM,SAAS,CAAA;AAG/E,MAAM,WAAW,uBAAuB;IACtC,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,CAAA;IAC9C,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,CAAC,CAAC,SAAS,CAAA;IACnB,MAAM,EAAE,CAAC,CAAC,SAAS,CAAA;IACnB,QAAQ,EAAE,CAAC,CAAC,SAAS,CAAA;IACrB,IAAI,EAAE,CAAC,CAAC,SAAS,CAAA;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,CAAA;CAC3C;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,YAAY,EAC1B,OAAO,GAAE,uBAA4B,GACpC,gBAAgB,CA+FlB;AAuwBD;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,YAAY,EAC1B,OAAO,EAAE,gBAAgB,GACxB,MAAM,CAeR;AASD;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,YAAY,EAC1B,IAAI,EAAE,OAAO,EACb,UAAU,GAAE,QAAQ,GAAG,QAAQ,GAAG,UAAqB,EACvD,OAAO,GAAE,uBAA4B,GACpC;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAA;CAAE,CAW3E;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CACzC,YAAY,EAAE,YAAY,GACzB,MAAM,CAyCR;AAMD,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAOD;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,YAAY,EAAE,YAAY,GAAG,uBAAuB,CAsE/F;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,YAAY,GAAG,uBAAuB,CA6C5F;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GACrC,YAAY,EAAE,CAIhB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAE1E;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GACrC;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CA4CpE"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { mediaRefSchema } from "../../types/blocks.js";
|
|
2
3
|
function generateEntitySchemas(entityConfig, options = {}) {
|
|
3
4
|
var _a;
|
|
4
5
|
const {
|
|
@@ -499,7 +500,7 @@ function generateFieldSchema(field, customValidation, strict = true) {
|
|
|
499
500
|
break;
|
|
500
501
|
// Media types
|
|
501
502
|
case "media-library":
|
|
502
|
-
schema =
|
|
503
|
+
schema = mediaRefSchema.nullable();
|
|
503
504
|
break;
|
|
504
505
|
case "file":
|
|
505
506
|
case "video":
|
|
@@ -275,8 +275,23 @@
|
|
|
275
275
|
"tabs": {
|
|
276
276
|
"content": "Content",
|
|
277
277
|
"design": "Design",
|
|
278
|
-
"advanced": "Advanced"
|
|
279
|
-
|
|
278
|
+
"advanced": "Advanced",
|
|
279
|
+
"noContentFields": "No content fields",
|
|
280
|
+
"noDesignFields": "No design fields",
|
|
281
|
+
"noAdvancedFields": "No advanced fields"
|
|
282
|
+
},
|
|
283
|
+
"pattern": {
|
|
284
|
+
"title": "Pattern Reference",
|
|
285
|
+
"locked": {
|
|
286
|
+
"title": "Locked Block",
|
|
287
|
+
"description": "This is a pattern reference. Edit the pattern directly to make changes."
|
|
288
|
+
},
|
|
289
|
+
"actions": {
|
|
290
|
+
"edit": "Edit pattern",
|
|
291
|
+
"remove": "Unlink pattern"
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
"noFields": "This block has no configurable fields"
|
|
280
295
|
},
|
|
281
296
|
"config": {
|
|
282
297
|
"entityFields": "Entity Fields",
|
|
@@ -277,7 +277,22 @@ declare const _default: {
|
|
|
277
277
|
content: string;
|
|
278
278
|
design: string;
|
|
279
279
|
advanced: string;
|
|
280
|
+
noContentFields: string;
|
|
281
|
+
noDesignFields: string;
|
|
282
|
+
noAdvancedFields: string;
|
|
283
|
+
};
|
|
284
|
+
pattern: {
|
|
285
|
+
title: string;
|
|
286
|
+
locked: {
|
|
287
|
+
title: string;
|
|
288
|
+
description: string;
|
|
289
|
+
};
|
|
290
|
+
actions: {
|
|
291
|
+
edit: string;
|
|
292
|
+
remove: string;
|
|
293
|
+
};
|
|
280
294
|
};
|
|
295
|
+
noFields: string;
|
|
281
296
|
};
|
|
282
297
|
config: {
|
|
283
298
|
entityFields: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/messages/en/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/messages/en/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqBA,wBAqBU"}
|
|
@@ -275,8 +275,23 @@
|
|
|
275
275
|
"tabs": {
|
|
276
276
|
"content": "Contenido",
|
|
277
277
|
"design": "Diseño",
|
|
278
|
-
"advanced": "Avanzado"
|
|
279
|
-
|
|
278
|
+
"advanced": "Avanzado",
|
|
279
|
+
"noContentFields": "No hay campos de contenido",
|
|
280
|
+
"noDesignFields": "No hay campos de diseno",
|
|
281
|
+
"noAdvancedFields": "No hay campos avanzados"
|
|
282
|
+
},
|
|
283
|
+
"pattern": {
|
|
284
|
+
"title": "Referencia a Pattern",
|
|
285
|
+
"locked": {
|
|
286
|
+
"title": "Bloque bloqueado",
|
|
287
|
+
"description": "Este bloque es una referencia a un pattern. Edita el pattern directamente para hacer cambios."
|
|
288
|
+
},
|
|
289
|
+
"actions": {
|
|
290
|
+
"edit": "Editar pattern",
|
|
291
|
+
"remove": "Desvincular pattern"
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
"noFields": "Este bloque no tiene campos configurables"
|
|
280
295
|
},
|
|
281
296
|
"config": {
|
|
282
297
|
"entityFields": "Campos de Entidad",
|
|
@@ -277,7 +277,22 @@ declare const _default: {
|
|
|
277
277
|
content: string;
|
|
278
278
|
design: string;
|
|
279
279
|
advanced: string;
|
|
280
|
+
noContentFields: string;
|
|
281
|
+
noDesignFields: string;
|
|
282
|
+
noAdvancedFields: string;
|
|
283
|
+
};
|
|
284
|
+
pattern: {
|
|
285
|
+
title: string;
|
|
286
|
+
locked: {
|
|
287
|
+
title: string;
|
|
288
|
+
description: string;
|
|
289
|
+
};
|
|
290
|
+
actions: {
|
|
291
|
+
edit: string;
|
|
292
|
+
remove: string;
|
|
293
|
+
};
|
|
280
294
|
};
|
|
295
|
+
noFields: string;
|
|
281
296
|
};
|
|
282
297
|
config: {
|
|
283
298
|
entityFields: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/messages/es/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/messages/es/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqBA,wBAqBU"}
|
package/dist/styles/classes.json
CHANGED
|
@@ -68,7 +68,7 @@ function AcceptInvitePage() {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
// Check email match
|
|
71
|
-
if (user.email.toLowerCase()
|
|
71
|
+
if ((user.email ?? '').toLowerCase() !== data.data.email.toLowerCase()) {
|
|
72
72
|
setStatus('email_mismatch')
|
|
73
73
|
setErrorMessage(`This invitation was sent to ${data.data.email}, but you are logged in as ${user.email}`)
|
|
74
74
|
return
|
|
@@ -22,7 +22,15 @@ import { query, queryOne } from '@nextsparkjs/core/lib/db'
|
|
|
22
22
|
import { getBillingGateway } from '@nextsparkjs/core/lib/billing/gateways/factory'
|
|
23
23
|
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
24
24
|
import type { PolarWebhookExtensions } from '@nextsparkjs/core/lib/billing/polar-webhook'
|
|
25
|
-
|
|
25
|
+
|
|
26
|
+
async function loadExtensions(): Promise<PolarWebhookExtensions> {
|
|
27
|
+
try {
|
|
28
|
+
const mod = await import('@/lib/billing/polar-webhook-extensions')
|
|
29
|
+
return mod.polarWebhookExtensions
|
|
30
|
+
} catch {
|
|
31
|
+
return {}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
26
34
|
|
|
27
35
|
async function handlePolarWebhook(request: NextRequest) {
|
|
28
36
|
// 1. Get raw body and ALL headers (Polar needs full headers for verification)
|
|
@@ -86,7 +94,7 @@ async function handlePolarWebhook(request: NextRequest) {
|
|
|
86
94
|
break
|
|
87
95
|
|
|
88
96
|
case 'order.paid':
|
|
89
|
-
await handleOrderPaid(event.data, eventId,
|
|
97
|
+
await handleOrderPaid(event.data, eventId, await loadExtensions())
|
|
90
98
|
break
|
|
91
99
|
|
|
92
100
|
default:
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Handles Stripe subscription lifecycle events.
|
|
5
5
|
* To add one-time payment handling (credit packs, LTD, upsells),
|
|
6
|
-
*
|
|
6
|
+
* create lib/billing/stripe-webhook-extensions.ts in your project.
|
|
7
7
|
*
|
|
8
8
|
* Rate limiting: 500 requests/hour per IP (tier: webhook).
|
|
9
9
|
* Stripe signature verification is the primary security layer;
|
|
@@ -14,12 +14,21 @@
|
|
|
14
14
|
|
|
15
15
|
import { NextRequest } from 'next/server'
|
|
16
16
|
import { handleStripeWebhook } from '@nextsparkjs/core/lib/billing/stripe-webhook'
|
|
17
|
-
import { stripeWebhookExtensions } from '@/lib/billing/stripe-webhook-extensions'
|
|
18
17
|
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
19
18
|
|
|
19
|
+
async function loadExtensions() {
|
|
20
|
+
try {
|
|
21
|
+
const mod = await import('@/lib/billing/stripe-webhook-extensions')
|
|
22
|
+
return mod.stripeWebhookExtensions
|
|
23
|
+
} catch {
|
|
24
|
+
return {}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
20
28
|
export const POST = withRateLimitTier(
|
|
21
29
|
async (request: NextRequest) => {
|
|
22
|
-
|
|
30
|
+
const extensions = await loadExtensions()
|
|
31
|
+
return handleStripeWebhook(request, extensions)
|
|
23
32
|
},
|
|
24
33
|
'webhook'
|
|
25
34
|
)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextsparkjs/core",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.132",
|
|
4
4
|
"description": "NextSpark - The complete SaaS framework for Next.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "NextSpark <hello@nextspark.dev>",
|
|
@@ -462,7 +462,7 @@
|
|
|
462
462
|
"tailwind-merge": "^3.3.1",
|
|
463
463
|
"uuid": "^13.0.0",
|
|
464
464
|
"zod": "^4.1.5",
|
|
465
|
-
"@nextsparkjs/testing": "0.1.0-beta.
|
|
465
|
+
"@nextsparkjs/testing": "0.1.0-beta.132"
|
|
466
466
|
},
|
|
467
467
|
"scripts": {
|
|
468
468
|
"postinstall": "node scripts/postinstall.mjs || true",
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @module core/scripts/build/registry/discovery/templates
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { readdir, stat, readFile } from 'fs/promises'
|
|
9
|
+
import { readdir, stat, readFile, access } from 'fs/promises'
|
|
10
10
|
import { join } from 'path'
|
|
11
11
|
|
|
12
12
|
import { CONFIG as DEFAULT_CONFIG } from '../config.mjs'
|
|
@@ -100,6 +100,42 @@ export async function discoverThemeTemplates(templatesPath, themeName, relativeP
|
|
|
100
100
|
continue
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
// Handle .meta.ts files: skip if companion .tsx exists (handled there),
|
|
104
|
+
// otherwise register as standalone metadata-only template
|
|
105
|
+
if (entry.name.endsWith('.meta.ts')) {
|
|
106
|
+
const companionTsx = entry.name.replace('.meta.ts', '.tsx')
|
|
107
|
+
const companionTs = entry.name.replace('.meta.ts', '.ts')
|
|
108
|
+
const hasTsxCompanion = entries.some(e => e.name === companionTsx)
|
|
109
|
+
const hasTsCompanion = entries.some(e => e.name === companionTs && !e.name.endsWith('.meta.ts'))
|
|
110
|
+
|
|
111
|
+
if (hasTsxCompanion || hasTsCompanion) {
|
|
112
|
+
verbose(`Skipping meta file (companion exists): ${currentRelativePath}`)
|
|
113
|
+
continue
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Standalone .meta.ts — register as metadata-only template
|
|
117
|
+
const baseFileName = entry.name.replace('.meta.ts', '.tsx')
|
|
118
|
+
const appPath = relativePath ? `app/${relativePath}/${baseFileName}` : `app/${baseFileName}`
|
|
119
|
+
const templatePath = `@/contents/themes/${themeName}/templates/${currentRelativePath}`
|
|
120
|
+
const templateMetadata = await extractTemplateMetadata(currentPath)
|
|
121
|
+
|
|
122
|
+
if (templateMetadata) {
|
|
123
|
+
verbose(`Standalone metadata template: ${appPath} (from ${currentRelativePath})`)
|
|
124
|
+
templates.push({
|
|
125
|
+
name: `${relativePath || 'root'}/${entry.name.replace('.meta.ts', '')}`.replace(/^\//, ''),
|
|
126
|
+
themeName,
|
|
127
|
+
templateType: entry.name.replace('.meta.ts', ''),
|
|
128
|
+
fileName: entry.name,
|
|
129
|
+
relativePath: currentRelativePath,
|
|
130
|
+
appPath,
|
|
131
|
+
templatePath,
|
|
132
|
+
priority: calculateTemplatePriority(themeName, relativePath, entry.name.replace('.meta.ts', '')),
|
|
133
|
+
metadata: templateMetadata
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
continue
|
|
137
|
+
}
|
|
138
|
+
|
|
103
139
|
// Found a template file
|
|
104
140
|
const templateType = entry.name.replace(/\.(tsx|ts)$/, '')
|
|
105
141
|
const appPath = relativePath ? `app/${relativePath}/${entry.name}` : `app/${entry.name}`
|
|
@@ -141,10 +177,28 @@ export async function discoverThemeTemplates(templatesPath, themeName, relativeP
|
|
|
141
177
|
// Ignore read errors for non-critical validation
|
|
142
178
|
}
|
|
143
179
|
|
|
144
|
-
// Extract metadata
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
180
|
+
// Extract metadata: check for companion .meta.ts file first, then inline export
|
|
181
|
+
let templateMetadata = null
|
|
182
|
+
|
|
183
|
+
// Check for companion .meta.ts file (e.g., layout.meta.ts for layout.tsx)
|
|
184
|
+
const metaFileName = entry.name.replace(/\.(tsx|ts)$/, '.meta.ts')
|
|
185
|
+
const metaFilePath = join(templatesPath, metaFileName)
|
|
186
|
+
try {
|
|
187
|
+
await access(metaFilePath)
|
|
188
|
+
templateMetadata = await extractTemplateMetadata(metaFilePath)
|
|
189
|
+
if (templateMetadata) {
|
|
190
|
+
verbose(`Metadata extracted from companion .meta.ts: ${metaFileName}`)
|
|
191
|
+
}
|
|
192
|
+
} catch {
|
|
193
|
+
// No companion .meta.ts file - fall through to inline extraction
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Fall back to inline metadata export from the template file itself
|
|
197
|
+
if (!templateMetadata) {
|
|
198
|
+
templateMetadata = await extractTemplateMetadata(currentPath)
|
|
199
|
+
if (templateMetadata) {
|
|
200
|
+
verbose(`Metadata extracted from ${templatePath}`)
|
|
201
|
+
}
|
|
148
202
|
}
|
|
149
203
|
|
|
150
204
|
templates.push({
|
|
@@ -57,9 +57,10 @@ export function generateTemplateRegistry(templates, config) {
|
|
|
57
57
|
templatePath = templatePath.slice(0, -3)
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
// Skip import for metadata-only templates (PROTECTED_RENDER)
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
// Skip import for metadata-only templates (PROTECTED_RENDER or standalone .meta.ts)
|
|
61
|
+
const isMetaOnly = highestPriorityTemplate.fileName?.endsWith('.meta.ts')
|
|
62
|
+
if (!canOverrideComponent(appPath) || isMetaOnly) {
|
|
63
|
+
return `// ${safeName} skipped - metadata-only${isMetaOnly ? ' (standalone .meta.ts)' : ' (PROTECTED_RENDER)'}`
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
return `import ${safeName} from '${templatePath}'`
|
|
@@ -72,8 +73,9 @@ export function generateTemplateRegistry(templates, config) {
|
|
|
72
73
|
const highestPriorityTemplate = pathTemplates[0]
|
|
73
74
|
const safeName = `Template_${index}`
|
|
74
75
|
|
|
75
|
-
// For metadata-only templates (PROTECTED_RENDER), don't reference component
|
|
76
|
-
const
|
|
76
|
+
// For metadata-only templates (PROTECTED_RENDER or standalone .meta.ts), don't reference component
|
|
77
|
+
const isMetaOnly = highestPriorityTemplate.fileName?.endsWith('.meta.ts')
|
|
78
|
+
const componentRef = (!canOverrideComponent(appPath) || isMetaOnly) ? 'null' : safeName
|
|
77
79
|
|
|
78
80
|
return ` '${appPath}': {
|
|
79
81
|
appPath: '${appPath}',
|
|
@@ -68,7 +68,7 @@ function AcceptInvitePage() {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
// Check email match
|
|
71
|
-
if (user.email.toLowerCase()
|
|
71
|
+
if ((user.email ?? '').toLowerCase() !== data.data.email.toLowerCase()) {
|
|
72
72
|
setStatus('email_mismatch')
|
|
73
73
|
setErrorMessage(`This invitation was sent to ${data.data.email}, but you are logged in as ${user.email}`)
|
|
74
74
|
return
|
|
@@ -22,7 +22,15 @@ import { query, queryOne } from '@nextsparkjs/core/lib/db'
|
|
|
22
22
|
import { getBillingGateway } from '@nextsparkjs/core/lib/billing/gateways/factory'
|
|
23
23
|
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
24
24
|
import type { PolarWebhookExtensions } from '@nextsparkjs/core/lib/billing/polar-webhook'
|
|
25
|
-
|
|
25
|
+
|
|
26
|
+
async function loadExtensions(): Promise<PolarWebhookExtensions> {
|
|
27
|
+
try {
|
|
28
|
+
const mod = await import('@/lib/billing/polar-webhook-extensions')
|
|
29
|
+
return mod.polarWebhookExtensions
|
|
30
|
+
} catch {
|
|
31
|
+
return {}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
26
34
|
|
|
27
35
|
async function handlePolarWebhook(request: NextRequest) {
|
|
28
36
|
// 1. Get raw body and ALL headers (Polar needs full headers for verification)
|
|
@@ -86,7 +94,7 @@ async function handlePolarWebhook(request: NextRequest) {
|
|
|
86
94
|
break
|
|
87
95
|
|
|
88
96
|
case 'order.paid':
|
|
89
|
-
await handleOrderPaid(event.data, eventId,
|
|
97
|
+
await handleOrderPaid(event.data, eventId, await loadExtensions())
|
|
90
98
|
break
|
|
91
99
|
|
|
92
100
|
default:
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Handles Stripe subscription lifecycle events.
|
|
5
5
|
* To add one-time payment handling (credit packs, LTD, upsells),
|
|
6
|
-
*
|
|
6
|
+
* create lib/billing/stripe-webhook-extensions.ts in your project.
|
|
7
7
|
*
|
|
8
8
|
* Rate limiting: 500 requests/hour per IP (tier: webhook).
|
|
9
9
|
* Stripe signature verification is the primary security layer;
|
|
@@ -14,12 +14,21 @@
|
|
|
14
14
|
|
|
15
15
|
import { NextRequest } from 'next/server'
|
|
16
16
|
import { handleStripeWebhook } from '@nextsparkjs/core/lib/billing/stripe-webhook'
|
|
17
|
-
import { stripeWebhookExtensions } from '@/lib/billing/stripe-webhook-extensions'
|
|
18
17
|
import { withRateLimitTier } from '@nextsparkjs/core/lib/api/rate-limit'
|
|
19
18
|
|
|
19
|
+
async function loadExtensions() {
|
|
20
|
+
try {
|
|
21
|
+
const mod = await import('@/lib/billing/stripe-webhook-extensions')
|
|
22
|
+
return mod.stripeWebhookExtensions
|
|
23
|
+
} catch {
|
|
24
|
+
return {}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
20
28
|
export const POST = withRateLimitTier(
|
|
21
29
|
async (request: NextRequest) => {
|
|
22
|
-
|
|
30
|
+
const extensions = await loadExtensions()
|
|
31
|
+
return handleStripeWebhook(request, extensions)
|
|
23
32
|
},
|
|
24
33
|
'webhook'
|
|
25
34
|
)
|