create-nextblock 0.11.1 → 0.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/package.json +1 -1
  2. package/templates/nextblock-template/app/actions/interactions.test.ts +301 -0
  3. package/templates/nextblock-template/app/actions/interactions.ts +372 -0
  4. package/templates/nextblock-template/app/api/ai/cortex/build-widget/route.ts +4 -4
  5. package/templates/nextblock-template/app/api/ai/generate-blocks/route.ts +2 -2
  6. package/templates/nextblock-template/app/api/ai/global-agent/route.ts +56 -57
  7. package/templates/nextblock-template/app/api/cron/reset-sandbox/route.ts +1 -1
  8. package/templates/nextblock-template/app/api/cron/reset-sandbox/sandboxResetSql.ts +951 -0
  9. package/templates/nextblock-template/app/article/[slug]/PostClientContent.tsx +6 -0
  10. package/templates/nextblock-template/app/cms/CmsClientLayout.tsx +4 -0
  11. package/templates/nextblock-template/app/cms/components/ConnectGitHubButton.tsx +7 -2
  12. package/templates/nextblock-template/app/cms/components/github-connect-actions.ts +4 -0
  13. package/templates/nextblock-template/app/cms/interactions/InteractionsModerationClient.tsx +408 -0
  14. package/templates/nextblock-template/app/cms/interactions/page.tsx +51 -0
  15. package/templates/nextblock-template/app/cms/settings/cortex-ai/SandboxCortexAiSettingsClient.tsx +4 -3
  16. package/templates/nextblock-template/app/cms/settings/cortex-ai/StoredCortexAiSettingsClient.tsx +1 -1
  17. package/templates/nextblock-template/app/cms/settings/cortex-ai/actions.ts +3 -5
  18. package/templates/nextblock-template/app/cms/settings/cortex-ai/page.tsx +1 -1
  19. package/templates/nextblock-template/app/page.tsx +2 -2
  20. package/templates/nextblock-template/app/product/[slug]/page.tsx +2 -0
  21. package/templates/nextblock-template/components/AppShell.tsx +1 -1
  22. package/templates/nextblock-template/components/PostCommentsSection.tsx +369 -0
  23. package/templates/nextblock-template/components/ProductReviewsSection.tsx +419 -0
  24. package/templates/nextblock-template/components/blocks/renderers/ProductDetailsBlockRenderer.tsx +2 -0
  25. package/templates/nextblock-template/components/privacy/ConsentBanner.tsx +62 -19
  26. package/templates/nextblock-template/docs/08-NEXTBLOCK-CORTEX-AI-ARCHITECTURE.md +19 -19
  27. package/templates/nextblock-template/docs/10-CUSTOM-BLOCKS.md +4 -4
  28. package/templates/nextblock-template/docs/13-STAYING-UP-TO-DATE.md +7 -0
  29. package/templates/nextblock-template/lib/blocks/ProductGridBlock.tsx +2 -0
  30. package/templates/nextblock-template/lib/setup/actions.ts +3 -1
  31. package/templates/nextblock-template/lib/setup/migrations-bundle.ts +40 -0
  32. package/templates/nextblock-template/lib/updates/check-upstream.ts +38 -4
  33. package/templates/nextblock-template/package.json +2 -1
  34. package/templates/nextblock-template/scripts/verify-cortex-ai-build-widget.tsx +2 -4
  35. package/templates/nextblock-template/scripts/verify-cortex-ai-generate-blocks.ts +1 -1
  36. package/templates/nextblock-template/scripts/verify-cortex-ai-global-tools.ts +1 -1
  37. package/templates/nextblock-template/scripts/verify-cortex-ai-routing.ts +1 -1
  38. package/templates/nextblock-template/tsconfig.tsbuildinfo +1 -1
  39. package/templates/nextblock-template/lib/ai-block-generation.ts +0 -339
  40. package/templates/nextblock-template/lib/ai-client.ts +0 -247
  41. package/templates/nextblock-template/lib/ai-config.ts +0 -98
  42. package/templates/nextblock-template/lib/ai-cortex-widget-builder.ts +0 -125
  43. package/templates/nextblock-template/lib/ai-global-agent-custom-block-tools.ts +0 -363
  44. package/templates/nextblock-template/lib/ai-global-agent-db-tools.test.ts +0 -405
  45. package/templates/nextblock-template/lib/ai-global-agent-db-tools.ts +0 -1228
  46. package/templates/nextblock-template/lib/ai-global-agent-ecommerce.ts +0 -5
  47. package/templates/nextblock-template/lib/ai-global-agent-tools-stats.test.ts +0 -223
  48. package/templates/nextblock-template/lib/ai-global-agent-tools.test.ts +0 -2183
  49. package/templates/nextblock-template/lib/ai-global-agent-tools.ts +0 -4807
  50. package/templates/nextblock-template/lib/ai-key-crypto.test.ts +0 -70
  51. package/templates/nextblock-template/lib/ai-key-crypto.ts +0 -132
  52. package/templates/nextblock-template/lib/ai-model-catalog.test.ts +0 -49
  53. package/templates/nextblock-template/lib/ai-model-catalog.ts +0 -41
  54. package/templates/nextblock-template/lib/ai-model-registry.test.ts +0 -231
  55. package/templates/nextblock-template/lib/ai-model-registry.ts +0 -522
  56. package/templates/nextblock-template/lib/cortex-widget-registry.test.ts +0 -199
  57. package/templates/nextblock-template/lib/cortex-widget-registry.ts +0 -88
  58. package/templates/nextblock-template/lib/cortex-widget-schema.test.tsx +0 -237
  59. package/templates/nextblock-template/lib/cortex-widget-schema.ts +0 -393
@@ -1,88 +0,0 @@
1
- import type { Database, Json } from '@nextblock-cms/db';
2
- import {
3
- customBlockDefinitionCreateSchema,
4
- customBlockDefinitionRowSchema,
5
- type CustomBlockDefinition,
6
- } from '@nextblock-cms/utils';
7
-
8
- import type { CortexWidgetDefinition } from './cortex-widget-schema';
9
-
10
- export const CORTEX_WIDGET_DEFINITION_SELECT =
11
- 'id, slug, name, description, fields, layout_schema, is_original';
12
-
13
- type CustomBlockDefinitionInsert =
14
- Database['public']['Tables']['custom_block_definitions']['Insert'];
15
-
16
- type SupabaseInsertClient = {
17
- from: (table: string) => any;
18
- };
19
-
20
- export class CortexWidgetRegistryInsertError extends Error {
21
- readonly code?: string;
22
- readonly status: number;
23
-
24
- constructor(message: string, params?: { code?: string; status?: number }) {
25
- super(message);
26
- this.name = 'CortexWidgetRegistryInsertError';
27
- this.code = params?.code;
28
- this.status = params?.status ?? 500;
29
- }
30
- }
31
-
32
- function toJson(value: unknown): Json {
33
- return JSON.parse(JSON.stringify(value)) as Json;
34
- }
35
-
36
- function getInsertErrorStatus(code?: string) {
37
- if (code === '23505') {
38
- return 409;
39
- }
40
-
41
- if (code === '42501') {
42
- return 403;
43
- }
44
-
45
- return 500;
46
- }
47
-
48
- export function buildCortexWidgetDefinitionInsertPayload(
49
- definition: CortexWidgetDefinition
50
- ): CustomBlockDefinitionInsert {
51
- const parsed = customBlockDefinitionCreateSchema.parse({
52
- ...definition,
53
- is_original: true,
54
- });
55
-
56
- return {
57
- description: parsed.description,
58
- fields: toJson(parsed.fields),
59
- is_original: true,
60
- layout_schema: toJson(parsed.layout_schema),
61
- name: parsed.name,
62
- slug: parsed.slug,
63
- };
64
- }
65
-
66
- export async function insertCortexWidgetDefinition(
67
- supabase: SupabaseInsertClient,
68
- definition: CortexWidgetDefinition
69
- ): Promise<CustomBlockDefinition> {
70
- const payload = buildCortexWidgetDefinitionInsertPayload(definition);
71
- const { data, error } = await supabase
72
- .from('custom_block_definitions')
73
- .insert(payload)
74
- .select(CORTEX_WIDGET_DEFINITION_SELECT)
75
- .single();
76
-
77
- if (error || !data) {
78
- throw new CortexWidgetRegistryInsertError(
79
- error?.message ?? 'Failed to insert Cortex custom block definition.',
80
- {
81
- code: error?.code,
82
- status: getInsertErrorStatus(error?.code),
83
- }
84
- );
85
- }
86
-
87
- return customBlockDefinitionRowSchema.parse(data);
88
- }
@@ -1,237 +0,0 @@
1
- import React from 'react';
2
- import { renderToStaticMarkup } from 'react-dom/server';
3
- import { describe, expect, it, vi } from 'vitest';
4
-
5
- vi.mock('@nextblock-cms/utils', async () => {
6
- const { z } = await import('zod');
7
- const fieldKeyPattern = /^[a-z][a-z0-9_]*$/;
8
- const slugPattern = /^[a-z][a-z0-9-]*$/;
9
- const customBlockFieldKeySchema = z.string().trim().min(1).max(80).regex(fieldKeyPattern);
10
- const customBlockSlugSchema = z.string().trim().min(1).max(120).regex(slugPattern);
11
- const fieldBaseSchema = z.strictObject({
12
- description: z.string().trim().max(500).optional(),
13
- key: customBlockFieldKeySchema,
14
- label: z.string().trim().min(1).max(120),
15
- required: z.boolean().default(false),
16
- });
17
- const fieldSchema = z.discriminatedUnion('type', [
18
- fieldBaseSchema.extend({
19
- default_value: z.string().max(5000).optional(),
20
- max_length: z.number().int().positive().max(10000).optional(),
21
- min_length: z.number().int().min(0).max(10000).optional(),
22
- placeholder: z.string().max(250).optional(),
23
- type: z.literal('text'),
24
- }),
25
- fieldBaseSchema.extend({
26
- default_value: z.string().max(50000).optional(),
27
- placeholder: z.string().max(250).optional(),
28
- type: z.literal('rich-text'),
29
- }),
30
- fieldBaseSchema.extend({
31
- accept: z.array(z.string()).max(20).optional(),
32
- default_value: z
33
- .strictObject({
34
- alt: z.string().max(300).optional(),
35
- file_name: z.string().trim().min(1).max(255).optional(),
36
- file_type: z.string().trim().min(1).max(120).optional(),
37
- height: z.number().int().positive().optional(),
38
- object_key: z.string().trim().min(1).max(1024),
39
- size_bytes: z.number().int().positive().optional(),
40
- url: z.string().trim().min(1).max(2048),
41
- width: z.number().int().positive().optional(),
42
- })
43
- .optional(),
44
- max_bytes: z.number().int().positive().max(50 * 1024 * 1024).optional(),
45
- type: z.literal('image_r2'),
46
- }),
47
- fieldBaseSchema.extend({
48
- default_value: z.union([z.string(), z.array(z.string()), z.null()]).optional(),
49
- display_column: z.string().trim().min(1).max(80).default('title'),
50
- filters: z.record(z.string(), z.unknown()).optional(),
51
- multiple: z.boolean().default(false),
52
- table: z.string().trim().min(1).max(80).regex(fieldKeyPattern),
53
- type: z.literal('db_relation'),
54
- value_column: z.string().trim().min(1).max(80).default('id'),
55
- }),
56
- ]);
57
- const elementSchema = z.enum([
58
- 'article',
59
- 'aside',
60
- 'blockquote',
61
- 'div',
62
- 'figure',
63
- 'figcaption',
64
- 'h2',
65
- 'h3',
66
- 'img',
67
- 'p',
68
- 'section',
69
- 'span',
70
- ]);
71
- const layoutNodeSchema: any = z.lazy(() =>
72
- z.discriminatedUnion('type', [
73
- z.strictObject({
74
- as: elementSchema.optional(),
75
- children: z.array(layoutNodeSchema).max(200).default([]),
76
- className: z.string().trim().max(4000).optional(),
77
- type: z.literal('container'),
78
- }),
79
- z.strictObject({
80
- as: elementSchema.optional(),
81
- className: z.string().trim().max(4000).optional(),
82
- emptyFallback: z.string().max(300).optional(),
83
- field_key: customBlockFieldKeySchema,
84
- type: z.literal('field_render'),
85
- }),
86
- ])
87
- );
88
- const collectKeys = (node: any): string[] =>
89
- node.type === 'field_render' ? [node.field_key] : node.children.flatMap(collectKeys);
90
- const customBlockDefinitionCreateSchema = z
91
- .strictObject({
92
- description: z.string().trim().max(1000).default(''),
93
- fields: z.array(fieldSchema).max(80).default([]),
94
- is_original: z.boolean().default(true),
95
- layout_schema: layoutNodeSchema,
96
- name: z.string().trim().min(1).max(160),
97
- slug: customBlockSlugSchema,
98
- })
99
- .superRefine((definition, context) => {
100
- const fieldKeys = new Set(definition.fields.map((field) => field.key));
101
- for (const fieldKey of collectKeys(definition.layout_schema)) {
102
- if (!fieldKeys.has(fieldKey)) {
103
- context.addIssue({
104
- code: 'custom',
105
- message: `Layout references unknown field "${fieldKey}".`,
106
- path: ['layout_schema'],
107
- });
108
- }
109
- }
110
- });
111
-
112
- return {
113
- customBlockDefinitionCreateSchema,
114
- customBlockFieldKeySchema,
115
- customBlockSlugSchema,
116
- };
117
- });
118
-
119
- import { DynamicLayoutEngine } from '../components/renderers/DynamicLayoutEngine';
120
- import {
121
- buildCortexProfileCardVerificationDefinition,
122
- buildCortexWidgetBuilderPrompt,
123
- buildCortexWidgetBuilderSystemPrompt,
124
- cortexWidgetDefinitionSchema,
125
- validateCortexWidgetDefinitionOutput,
126
- } from './cortex-widget-schema';
127
-
128
- describe('cortex widget schema', () => {
129
- it('validates a recursive multi-tier profile card widget', () => {
130
- const definition = buildCortexProfileCardVerificationDefinition();
131
-
132
- expect(definition.slug).toBe('cortex-profile-card');
133
- expect(definition.is_original).toBe(true);
134
- expect(definition.fields.map((field) => field.type)).toEqual([
135
- 'image_r2',
136
- 'text',
137
- 'text',
138
- 'rich-text',
139
- 'db_relation',
140
- ]);
141
- expect(definition.layout_schema.type).toBe('container');
142
- if (definition.layout_schema.type !== 'container') {
143
- throw new Error('Expected verification layout to be a container.');
144
- }
145
- expect(definition.layout_schema.children[0]?.type).toBe('container');
146
- });
147
-
148
- it('renders the generated recursive layout without runtime compilation', () => {
149
- const definition = buildCortexProfileCardVerificationDefinition();
150
- const html = renderToStaticMarkup(
151
- <DynamicLayoutEngine
152
- definition={{
153
- fields: definition.fields,
154
- id: '55555555-5555-4555-8555-555555555555',
155
- layout_schema: definition.layout_schema,
156
- name: definition.name,
157
- slug: definition.slug,
158
- }}
159
- data={{
160
- customer_list: ['profile-1', 'profile-2'],
161
- profile_name: 'Ada Lovelace',
162
- profile_photo: {
163
- alt: 'Ada Lovelace',
164
- object_key: 'cortex/profile-card/ada.webp',
165
- url: 'https://cdn.nextblock.dev/cortex/profile-card/ada.webp',
166
- },
167
- profile_role: 'Principal Architect',
168
- profile_summary: '<p>Builds analytical systems with human taste.</p>',
169
- resolved_relations: {
170
- customer_list: [
171
- {
172
- record: { full_name: 'Analytical Engine Society', id: 'profile-1' },
173
- table: 'profiles',
174
- value: 'profile-1',
175
- },
176
- {
177
- record: { full_name: 'Difference Labs', id: 'profile-2' },
178
- table: 'profiles',
179
- value: 'profile-2',
180
- },
181
- ],
182
- },
183
- }}
184
- />
185
- );
186
-
187
- expect(html).toContain('mx-auto max-w-3xl p-4');
188
- expect(html).toContain('src="https://cdn.nextblock.dev/cortex/profile-card/ada.webp"');
189
- expect(html).toContain('Ada Lovelace');
190
- expect(html).toContain('Principal Architect');
191
- expect(html).toContain('Analytical Engine Society, Difference Labs');
192
- });
193
-
194
- it('rejects layout fields that are not declared', () => {
195
- expect(() =>
196
- validateCortexWidgetDefinitionOutput({
197
- description: '',
198
- fields: [{ key: 'headline', label: 'Headline', required: true, type: 'text' }],
199
- is_original: true,
200
- layout_schema: {
201
- field_key: 'missing_field',
202
- type: 'field_render',
203
- },
204
- name: 'Broken Widget',
205
- slug: 'broken-widget',
206
- })
207
- ).toThrow(/unknown field/);
208
- });
209
-
210
- it('keeps the prompt constrained to raw JSON, Tailwind classes, and data rows', () => {
211
- const systemPrompt = buildCortexWidgetBuilderSystemPrompt();
212
- const userPrompt = buildCortexWidgetBuilderPrompt({
213
- prompt:
214
- 'Synthesize a multi-tier profile card with an inner flex column housing an R2 picture asset slot and a customer list relation link.',
215
- });
216
-
217
- expect(systemPrompt).toContain('Return ONLY one clean raw JSON object');
218
- expect(systemPrompt).toContain('Tailwind utility classes');
219
- expect(systemPrompt).toContain('Never emit TSX');
220
- expect(userPrompt).toContain('multi-tier profile card');
221
- });
222
-
223
- it('exposes a recursive Zod layout schema for constrained decoding', () => {
224
- const parsed = cortexWidgetDefinitionSchema.parse(
225
- buildCortexProfileCardVerificationDefinition()
226
- );
227
-
228
- expect(parsed.layout_schema).toMatchObject({
229
- type: 'container',
230
- children: expect.arrayContaining([
231
- expect.objectContaining({
232
- type: 'container',
233
- }),
234
- ]),
235
- });
236
- });
237
- });