clientplane 0.0.0-reserved.0

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.
@@ -0,0 +1,818 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/bin/clientplane.ts
4
+ import { readFile as readFile3 } from "fs/promises";
5
+
6
+ // ../cli/dist/commands/scan.js
7
+ import { readFile } from "fs/promises";
8
+
9
+ // ../cli/dist/scan/next-source-scan.js
10
+ import ts from "typescript";
11
+
12
+ // ../shared/dist/actors.js
13
+ import { z } from "zod";
14
+ var ACTOR_TYPES = ["developer", "client", "agent", "system"];
15
+ var actorTypeSchema = z.enum(ACTOR_TYPES);
16
+
17
+ // ../shared/dist/assets.js
18
+ import { z as z2 } from "zod";
19
+ var ASSET_STAGES = ["staging", "final", "deleted"];
20
+ var ASSET_VISIBILITIES = ["private", "public", "deleted"];
21
+ var ASSET_USAGE_ENTITY_TYPES = [
22
+ "branding",
23
+ "draft",
24
+ "change_set",
25
+ "published_value"
26
+ ];
27
+ var assetStageSchema = z2.enum(ASSET_STAGES);
28
+ var assetVisibilitySchema = z2.enum(ASSET_VISIBILITIES);
29
+ var assetUsageEntityTypeSchema = z2.enum(ASSET_USAGE_ENTITY_TYPES);
30
+ var brandingAccentColorSchema = z2.string().regex(/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/);
31
+
32
+ // ../shared/dist/bridge-protocol.js
33
+ import { z as z5 } from "zod";
34
+
35
+ // ../shared/dist/field-id.js
36
+ import { z as z3 } from "zod";
37
+ var FIELD_ID_PATTERN = /^[a-z][a-z0-9_-]*(\.[a-z][a-z0-9_-]*)+$/;
38
+ var fieldIdSchema = z3.string().regex(FIELD_ID_PATTERN, "Invalid field id");
39
+ function isFieldId(value) {
40
+ return typeof value === "string" && fieldIdSchema.safeParse(value).success;
41
+ }
42
+
43
+ // ../shared/dist/field-types.js
44
+ import { z as z4 } from "zod";
45
+ var FIELD_TYPES = ["text", "longText", "image", "link"];
46
+ var fieldTypeSchema = z4.enum(FIELD_TYPES);
47
+
48
+ // ../shared/dist/bridge-protocol.js
49
+ var BRIDGE_PROTOCOL_VERSION = 1;
50
+ var BRIDGE_MESSAGE_TYPES = [
51
+ "clientplane:bridge:init",
52
+ "clientplane:bridge:ready",
53
+ "clientplane:bridge:fields",
54
+ "clientplane:bridge:select-field",
55
+ "clientplane:bridge:apply-preview-values",
56
+ "clientplane:bridge:test-preview-value",
57
+ "clientplane:bridge:ack",
58
+ "clientplane:bridge:error"
59
+ ];
60
+ var bridgeMessageTypeSchema = z5.enum(BRIDGE_MESSAGE_TYPES);
61
+ var bridgeBaseSchema = z5.object({
62
+ version: z5.literal(BRIDGE_PROTOCOL_VERSION),
63
+ type: bridgeMessageTypeSchema,
64
+ siteId: z5.string().uuid(),
65
+ sessionId: z5.string().trim().min(1),
66
+ requestId: z5.string().trim().min(1),
67
+ sentAt: z5.string().datetime()
68
+ });
69
+ var bridgeRectSchema = z5.object({
70
+ x: z5.number(),
71
+ y: z5.number(),
72
+ width: z5.number().nonnegative(),
73
+ height: z5.number().nonnegative()
74
+ });
75
+ var bridgeFieldSchema = z5.object({
76
+ fieldId: fieldIdSchema,
77
+ fieldType: fieldTypeSchema,
78
+ route: z5.string().regex(/^\/.*/),
79
+ routeId: z5.string().trim().min(1).optional(),
80
+ sourcePath: z5.string().trim().min(1).optional(),
81
+ editTarget: fieldIdSchema,
82
+ manifestVersion: z5.string().trim().min(1).optional(),
83
+ rect: bridgeRectSchema,
84
+ editable: z5.boolean(),
85
+ label: z5.string().trim().min(1).optional()
86
+ });
87
+ var bridgeInitPayloadSchema = z5.object({
88
+ adminOrigin: z5.string().url(),
89
+ customerOrigin: z5.string().url(),
90
+ route: z5.string().regex(/^\/.*/)
91
+ });
92
+ var bridgeReadyPayloadSchema = z5.object({
93
+ route: z5.string().regex(/^\/.*/),
94
+ fieldCount: z5.number().int().nonnegative()
95
+ });
96
+ var bridgeFieldsPayloadSchema = z5.object({
97
+ route: z5.string().regex(/^\/.*/),
98
+ fields: z5.array(bridgeFieldSchema)
99
+ });
100
+ var bridgeSelectFieldPayloadSchema = z5.object({
101
+ fieldId: fieldIdSchema,
102
+ source: z5.enum(["runtime", "editor"]).default("runtime")
103
+ });
104
+ var previewTextValueSchema = z5.object({
105
+ fieldType: z5.enum(["text", "longText"]),
106
+ value: z5.string()
107
+ });
108
+ var previewLinkValueSchema = z5.object({
109
+ fieldType: z5.literal("link"),
110
+ value: z5.object({
111
+ href: z5.string().trim().min(1),
112
+ label: z5.string().trim().min(1).optional()
113
+ })
114
+ });
115
+ var previewImageValueSchema = z5.object({
116
+ fieldType: z5.literal("image"),
117
+ value: z5.object({
118
+ src: z5.string().trim().min(1),
119
+ alt: z5.string().trim().min(1)
120
+ })
121
+ });
122
+ var bridgePreviewValueSchema = z5.discriminatedUnion("fieldType", [
123
+ previewTextValueSchema,
124
+ previewLinkValueSchema,
125
+ previewImageValueSchema
126
+ ]);
127
+ var bridgeApplyPreviewValuesPayloadSchema = z5.object({
128
+ values: z5.record(fieldIdSchema, bridgePreviewValueSchema)
129
+ });
130
+ var bridgeTestPreviewValuePayloadSchema = z5.object({
131
+ fieldIds: z5.array(fieldIdSchema).min(1)
132
+ });
133
+ var bridgeAckPayloadSchema = z5.object({
134
+ ackRequestId: z5.string().trim().min(1)
135
+ });
136
+ var bridgeErrorPayloadSchema = z5.object({
137
+ code: z5.string().trim().min(1),
138
+ message: z5.string().trim().min(1),
139
+ recoverable: z5.boolean().default(false)
140
+ });
141
+ var bridgeInitMessageSchema = bridgeBaseSchema.extend({
142
+ type: z5.literal("clientplane:bridge:init"),
143
+ payload: bridgeInitPayloadSchema
144
+ });
145
+ var bridgeReadyMessageSchema = bridgeBaseSchema.extend({
146
+ type: z5.literal("clientplane:bridge:ready"),
147
+ payload: bridgeReadyPayloadSchema
148
+ });
149
+ var bridgeFieldsMessageSchema = bridgeBaseSchema.extend({
150
+ type: z5.literal("clientplane:bridge:fields"),
151
+ payload: bridgeFieldsPayloadSchema
152
+ });
153
+ var bridgeSelectFieldMessageSchema = bridgeBaseSchema.extend({
154
+ type: z5.literal("clientplane:bridge:select-field"),
155
+ payload: bridgeSelectFieldPayloadSchema
156
+ });
157
+ var bridgeApplyPreviewValuesMessageSchema = bridgeBaseSchema.extend({
158
+ type: z5.literal("clientplane:bridge:apply-preview-values"),
159
+ payload: bridgeApplyPreviewValuesPayloadSchema
160
+ });
161
+ var bridgeTestPreviewValueMessageSchema = bridgeBaseSchema.extend({
162
+ type: z5.literal("clientplane:bridge:test-preview-value"),
163
+ payload: bridgeTestPreviewValuePayloadSchema
164
+ });
165
+ var bridgeAckMessageSchema = bridgeBaseSchema.extend({
166
+ type: z5.literal("clientplane:bridge:ack"),
167
+ payload: bridgeAckPayloadSchema
168
+ });
169
+ var bridgeErrorMessageSchema = bridgeBaseSchema.extend({
170
+ type: z5.literal("clientplane:bridge:error"),
171
+ payload: bridgeErrorPayloadSchema
172
+ });
173
+ var bridgeMessageSchema = z5.discriminatedUnion("type", [
174
+ bridgeInitMessageSchema,
175
+ bridgeReadyMessageSchema,
176
+ bridgeFieldsMessageSchema,
177
+ bridgeSelectFieldMessageSchema,
178
+ bridgeApplyPreviewValuesMessageSchema,
179
+ bridgeTestPreviewValueMessageSchema,
180
+ bridgeAckMessageSchema,
181
+ bridgeErrorMessageSchema
182
+ ]);
183
+
184
+ // ../shared/dist/commands.js
185
+ import { z as z7 } from "zod";
186
+
187
+ // ../shared/dist/errors.js
188
+ import { z as z6 } from "zod";
189
+ var UI_ERROR_CATEGORIES = [
190
+ "client",
191
+ "developer",
192
+ "internal"
193
+ ];
194
+ var uiErrorCategorySchema = z6.enum(UI_ERROR_CATEGORIES);
195
+ var commandErrorPayloadSchema = z6.object({
196
+ code: z6.string().min(1),
197
+ message: z6.string().min(1),
198
+ category: uiErrorCategorySchema,
199
+ details: z6.record(z6.string(), z6.unknown()).optional()
200
+ });
201
+
202
+ // ../shared/dist/commands.js
203
+ var commandSuccessSchema = z7.object({
204
+ ok: z7.literal(true),
205
+ data: z7.unknown()
206
+ });
207
+ var commandErrorSchema = z7.object({
208
+ ok: z7.literal(false),
209
+ error: commandErrorPayloadSchema
210
+ });
211
+ var commandResultSchema = z7.discriminatedUnion("ok", [
212
+ commandSuccessSchema,
213
+ commandErrorSchema
214
+ ]);
215
+
216
+ // ../shared/dist/field-manifest.js
217
+ import { z as z8 } from "zod";
218
+ var FIELD_MANIFEST_CHANGE_TYPES = [
219
+ "new",
220
+ "changed",
221
+ "removed",
222
+ "rename_candidate"
223
+ ];
224
+ var FIELD_MANIFEST_REVIEW_STATUSES = [
225
+ "pending",
226
+ "approved",
227
+ "rejected",
228
+ "needs_fix",
229
+ "resolved"
230
+ ];
231
+ var FIELD_DEFINITION_STATUSES = [
232
+ "active",
233
+ "hidden",
234
+ "orphaned",
235
+ "archived"
236
+ ];
237
+ var fieldManifestChangeTypeSchema = z8.enum(FIELD_MANIFEST_CHANGE_TYPES);
238
+ var fieldManifestReviewStatusSchema = z8.enum(FIELD_MANIFEST_REVIEW_STATUSES);
239
+ var fieldDefinitionStatusSchema = z8.enum(FIELD_DEFINITION_STATUSES);
240
+ var manifestRouteSchema = z8.object({
241
+ path: z8.string().regex(/^\/.*/),
242
+ label: z8.string().trim().min(1).optional()
243
+ });
244
+ var fieldManifestFieldSchema = z8.object({
245
+ fieldId: fieldIdSchema,
246
+ fieldType: fieldTypeSchema,
247
+ fallback: z8.unknown().optional(),
248
+ label: z8.string().trim().min(1).optional(),
249
+ description: z8.string().trim().min(1).optional(),
250
+ required: z8.boolean().optional(),
251
+ maxLength: z8.number().int().positive().optional(),
252
+ routeHint: z8.string().regex(/^\/.*/).optional(),
253
+ routeId: z8.string().trim().min(1).optional(),
254
+ sourcePath: z8.string().trim().min(1).optional(),
255
+ editTarget: fieldIdSchema.optional(),
256
+ lastSeenRoute: z8.string().regex(/^\/.*/).optional(),
257
+ positionHint: z8.record(z8.string(), z8.unknown()).optional(),
258
+ domSelectorHint: z8.string().trim().min(1).optional(),
259
+ source: z8.enum(["component", "function", "runtime"]).default("component"),
260
+ sourceFilePath: z8.string().trim().min(1).optional(),
261
+ sourceExportName: z8.string().trim().min(1).optional(),
262
+ componentHint: z8.string().trim().min(1).optional(),
263
+ validation: z8.record(z8.string(), z8.unknown()).optional(),
264
+ metadata: z8.record(z8.string(), z8.unknown()).optional(),
265
+ warnings: z8.array(z8.string().trim().min(1)).default([]),
266
+ suggestions: z8.array(z8.string().trim().min(1)).default([])
267
+ });
268
+ var fieldManifestSchema = z8.object({
269
+ version: z8.literal(1),
270
+ siteId: z8.string().uuid().optional(),
271
+ fields: z8.array(fieldManifestFieldSchema),
272
+ routes: z8.array(manifestRouteSchema).default([])
273
+ }).superRefine((manifest, context) => {
274
+ const seen = /* @__PURE__ */ new Set();
275
+ for (const field of manifest.fields) {
276
+ if (seen.has(field.fieldId)) {
277
+ context.addIssue({
278
+ code: "custom",
279
+ path: ["fields"],
280
+ message: `Duplicate field id: ${field.fieldId}`
281
+ });
282
+ }
283
+ seen.add(field.fieldId);
284
+ }
285
+ });
286
+
287
+ // ../shared/dist/fallback-portal.js
288
+ import { z as z9 } from "zod";
289
+ var FALLBACK_PORTAL_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
290
+ var fallbackPortalSlugSchema = z9.string().regex(FALLBACK_PORTAL_SLUG_PATTERN, "Invalid fallback portal slug");
291
+
292
+ // ../shared/dist/entitlements.js
293
+ import { z as z10 } from "zod";
294
+ var entitlementSnapshotSchema = z10.object({
295
+ planKey: z10.string().trim().min(1),
296
+ sitesLimit: z10.number().int().positive(),
297
+ clientAccountsPerSiteLimit: z10.number().int().positive(),
298
+ customAdminDomainsEnabled: z10.boolean(),
299
+ agentMcpEnabled: z10.boolean(),
300
+ emailNotificationsEnabled: z10.boolean()
301
+ });
302
+
303
+ // ../shared/dist/image-value.js
304
+ import { z as z11 } from "zod";
305
+ var SUPPORTED_IMAGE_CONTENT_TYPES = [
306
+ "image/jpeg",
307
+ "image/png",
308
+ "image/webp",
309
+ "image/avif"
310
+ ];
311
+ var maxImageUploadBytes = 10 * 1024 * 1024;
312
+ var largeImageWarningBytes = 2 * 1024 * 1024;
313
+ var imageContentTypeSchema = z11.enum(SUPPORTED_IMAGE_CONTENT_TYPES);
314
+ var imageCropSchema = z11.object({
315
+ x: z11.number().min(0).max(100),
316
+ y: z11.number().min(0).max(100),
317
+ zoom: z11.number().min(1).max(4),
318
+ aspectRatio: z11.number().positive().max(10)
319
+ });
320
+ var imageValueSchema = z11.object({
321
+ src: z11.string().trim().min(1),
322
+ alt: z11.string().trim().min(1).max(300),
323
+ width: z11.number().int().positive().optional(),
324
+ height: z11.number().int().positive().optional(),
325
+ crop: imageCropSchema.optional(),
326
+ assetId: z11.string().uuid().optional(),
327
+ finalStorageProvider: z11.enum([
328
+ "repo_static_assets",
329
+ "repo_handoff",
330
+ "site_owned_storage",
331
+ "clientplane_managed_fallback"
332
+ ]).optional()
333
+ });
334
+
335
+ // ../shared/dist/readiness.js
336
+ import { z as z12 } from "zod";
337
+ var READINESS_CHECK_KEYS = [
338
+ "agent_instructions_installed",
339
+ "manual_setup_confirmed",
340
+ "runtime_package_connected",
341
+ "secure_server_access_configured",
342
+ "manifest_synced",
343
+ "approved_field_exists",
344
+ "pending_manifest_changes_clear",
345
+ "preview_bridge_working",
346
+ "admin_access_url_selected",
347
+ "default_publishing_mode_selected"
348
+ ];
349
+ var READINESS_STATUSES = [
350
+ "pending",
351
+ "passing",
352
+ "failing",
353
+ "warning"
354
+ ];
355
+ var readinessCheckKeySchema = z12.enum(READINESS_CHECK_KEYS);
356
+ var readinessStatusSchema = z12.enum(READINESS_STATUSES);
357
+
358
+ // ../shared/dist/site.js
359
+ import { z as z13 } from "zod";
360
+ var SITE_STATUSES = [
361
+ "setup",
362
+ "client_ready",
363
+ "disabled",
364
+ "archived"
365
+ ];
366
+ var siteStatusSchema = z13.enum(SITE_STATUSES);
367
+ var websiteUrlSchema = z13.string().trim().url().refine((value) => value.startsWith("https://") || value.startsWith("http://"), "Website URL must use http or https");
368
+
369
+ // ../cli/dist/scan/next-source-scan.js
370
+ var editableComponentNames = /* @__PURE__ */ new Set([
371
+ "EditableImage",
372
+ "EditableText",
373
+ "EditableLongText",
374
+ "EditableLink"
375
+ ]);
376
+ var functionTypeByName = {
377
+ text: "text",
378
+ longText: "longText",
379
+ link: "link",
380
+ image: "image"
381
+ };
382
+ function scanNextSourceFiles(files) {
383
+ const fields = [];
384
+ const hardErrors = [];
385
+ const warnings = [];
386
+ const suggestions = [];
387
+ for (const file of files) {
388
+ scanSourceFile(file, fields, hardErrors);
389
+ }
390
+ validateScannedFields(fields, hardErrors, warnings, suggestions);
391
+ const manifest = fieldManifestSchema.parse({
392
+ version: 1,
393
+ fields: uniqueFields(fields).filter((field) => isFieldId(field.fieldId)),
394
+ routes: createRoutes(fields)
395
+ });
396
+ return {
397
+ manifest,
398
+ hardErrors,
399
+ warnings,
400
+ suggestions
401
+ };
402
+ }
403
+ function uniqueFields(fields) {
404
+ const seen = /* @__PURE__ */ new Set();
405
+ const unique = [];
406
+ for (const field of fields) {
407
+ if (seen.has(field.fieldId)) {
408
+ continue;
409
+ }
410
+ seen.add(field.fieldId);
411
+ unique.push(field);
412
+ }
413
+ return unique;
414
+ }
415
+ function scanSourceFile(file, fields, hardErrors) {
416
+ const fieldsInFile = [];
417
+ const attrFieldIds = /* @__PURE__ */ new Set();
418
+ const attrRouteRefs = /* @__PURE__ */ new Set();
419
+ const sourceFile = ts.createSourceFile(file.filePath, file.sourceText, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
420
+ function visit(node) {
421
+ if (ts.isJsxSelfClosingElement(node) || ts.isJsxOpeningElement(node)) {
422
+ const componentName = getJsxTagName(node.tagName);
423
+ if (editableComponentNames.has(componentName)) {
424
+ hardErrors.push({
425
+ code: "field_scan.value_first_required",
426
+ message: "Clientplane MVP uses value-first APIs. Replace this field with clientplane.route(...) or a field value call.",
427
+ filePath: file.filePath
428
+ });
429
+ }
430
+ }
431
+ if (ts.isCallExpression(node)) {
432
+ collectAttrsCall(node, attrFieldIds, attrRouteRefs);
433
+ const extractedFields = extractFunctionFields(node, file);
434
+ for (const field of extractedFields) {
435
+ fieldsInFile.push(field);
436
+ fields.push(field);
437
+ }
438
+ }
439
+ ts.forEachChild(node, visit);
440
+ }
441
+ visit(sourceFile);
442
+ markMissingAttrsWarnings(fieldsInFile, attrFieldIds, attrRouteRefs);
443
+ }
444
+ function extractFunctionFields(node, file) {
445
+ if (!ts.isPropertyAccessExpression(node.expression)) {
446
+ return [];
447
+ }
448
+ if (node.expression.expression.getText() !== "clientplane") {
449
+ return [];
450
+ }
451
+ const functionName = node.expression.name.text;
452
+ if (functionName === "route") {
453
+ return extractRouteFields(node, file);
454
+ }
455
+ const fieldType = functionTypeByName[functionName];
456
+ if (!fieldType) {
457
+ return [];
458
+ }
459
+ const fieldId = staticExpressionValue(node.arguments[0]);
460
+ if (typeof fieldId !== "string") {
461
+ return [];
462
+ }
463
+ const input = {
464
+ fieldId,
465
+ fieldType,
466
+ fallback: staticExpressionValue(node.arguments[1]),
467
+ source: "function",
468
+ sourceFilePath: file.filePath,
469
+ sourceExportName: functionName,
470
+ editTarget: fieldId
471
+ };
472
+ const routeHint = inferRouteHint(file.filePath);
473
+ const options = staticExpressionValue(node.arguments[2]);
474
+ const fieldOptions = options && typeof options === "object" && !Array.isArray(options) ? options : {};
475
+ assignIfDefined(input, "routeHint", routeHint);
476
+ assignIfDefined(input, "label", stringValue(fieldOptions.label));
477
+ assignIfDefined(input, "description", stringValue(fieldOptions.description));
478
+ assignIfDefined(input, "required", booleanValue(fieldOptions.required));
479
+ assignIfDefined(input, "maxLength", numberValue(fieldOptions.maxLength));
480
+ return [createField(input)];
481
+ }
482
+ function extractRouteFields(node, file) {
483
+ const routeId = staticExpressionValue(node.arguments[0]);
484
+ const fallbacks = staticExpressionValue(node.arguments[1]);
485
+ const options = staticExpressionValue(node.arguments[2]);
486
+ if (typeof routeId !== "string" || !fallbacks || typeof fallbacks !== "object" || Array.isArray(fallbacks)) {
487
+ return [];
488
+ }
489
+ const routeVariableName = getAssignedVariableName(node);
490
+ const fieldsConfig = options && typeof options === "object" && !Array.isArray(options) ? options.fields : void 0;
491
+ const routeHint = inferRouteHint(file.filePath);
492
+ const fields = [];
493
+ for (const [key, fallback] of Object.entries(fallbacks)) {
494
+ const config = fieldsConfig && typeof fieldsConfig === "object" && !Array.isArray(fieldsConfig) ? fieldsConfig[key] : void 0;
495
+ if (!config || typeof config !== "object" || Array.isArray(config)) {
496
+ continue;
497
+ }
498
+ const fieldConfig = config;
499
+ const fieldId = stringValue(fieldConfig.id);
500
+ const fieldType = fieldTypeValue(fieldConfig.type);
501
+ if (!fieldId || !fieldType) {
502
+ continue;
503
+ }
504
+ const input = {
505
+ fieldId,
506
+ fieldType,
507
+ fallback,
508
+ source: "function",
509
+ sourceFilePath: file.filePath,
510
+ sourceExportName: "route",
511
+ routeId,
512
+ sourcePath: `$.${key}`,
513
+ editTarget: fieldId,
514
+ routeFieldKey: key
515
+ };
516
+ assignIfDefined(input, "routeVariableName", routeVariableName);
517
+ assignIfDefined(input, "routeHint", routeHint);
518
+ assignIfDefined(input, "label", stringValue(fieldConfig.label));
519
+ assignIfDefined(input, "description", stringValue(fieldConfig.description));
520
+ assignIfDefined(input, "required", booleanValue(fieldConfig.required));
521
+ assignIfDefined(input, "maxLength", numberValue(fieldConfig.maxLength));
522
+ fields.push(createField(input));
523
+ }
524
+ return fields;
525
+ }
526
+ function createField(input) {
527
+ const field = {
528
+ fieldId: input.fieldId,
529
+ fieldType: input.fieldType,
530
+ fallback: input.fallback,
531
+ source: input.source,
532
+ sourceFilePath: input.sourceFilePath,
533
+ warnings: [],
534
+ suggestions: []
535
+ };
536
+ assignIfDefined(field, "label", input.label);
537
+ assignIfDefined(field, "description", input.description);
538
+ assignIfDefined(field, "required", input.required);
539
+ assignIfDefined(field, "maxLength", input.maxLength);
540
+ assignIfDefined(field, "sourceExportName", input.sourceExportName);
541
+ assignIfDefined(field, "componentHint", input.componentHint);
542
+ assignIfDefined(field, "routeHint", input.routeHint);
543
+ assignIfDefined(field, "routeId", input.routeId);
544
+ assignIfDefined(field, "sourcePath", input.sourcePath);
545
+ assignIfDefined(field, "editTarget", input.editTarget);
546
+ assignIfDefined(field, "routeVariableName", input.routeVariableName);
547
+ assignIfDefined(field, "routeFieldKey", input.routeFieldKey);
548
+ if (field.fieldType === "text" && field.maxLength === void 0) {
549
+ field.warnings.push("field_scan.text_max_length_missing");
550
+ }
551
+ if (isPositionalFieldReference(field.fieldId) || isPositionalFieldReference(field.sourcePath)) {
552
+ field.warnings.push("field_scan.positional_repeating_id");
553
+ }
554
+ return field;
555
+ }
556
+ function collectAttrsCall(node, attrFieldIds, attrRouteRefs) {
557
+ if (!ts.isPropertyAccessExpression(node.expression)) {
558
+ return;
559
+ }
560
+ if (node.expression.expression.getText() !== "clientplane" || node.expression.name.text !== "attrs") {
561
+ return;
562
+ }
563
+ const firstArg = node.arguments[0];
564
+ const secondArg = staticExpressionValue(node.arguments[1]);
565
+ const directFieldId = staticExpressionValue(firstArg);
566
+ if (typeof directFieldId === "string") {
567
+ attrFieldIds.add(directFieldId);
568
+ return;
569
+ }
570
+ if (firstArg && ts.isIdentifier(firstArg) && typeof secondArg === "string") {
571
+ attrRouteRefs.add(`${firstArg.text}:${secondArg}`);
572
+ }
573
+ }
574
+ function markMissingAttrsWarnings(fields, attrFieldIds, attrRouteRefs) {
575
+ for (const field of fields) {
576
+ if (field.fieldType !== "text" && field.fieldType !== "longText") {
577
+ continue;
578
+ }
579
+ const hasDirectAttrs = attrFieldIds.has(field.fieldId);
580
+ const hasRouteAttrs = field.routeVariableName && field.routeFieldKey ? attrRouteRefs.has(`${field.routeVariableName}:${field.routeFieldKey}`) : false;
581
+ if (!hasDirectAttrs && !hasRouteAttrs) {
582
+ field.warnings.push("field_scan.attrs_missing");
583
+ }
584
+ }
585
+ }
586
+ function validateScannedFields(fields, hardErrors, warnings, suggestions) {
587
+ const seen = /* @__PURE__ */ new Map();
588
+ for (const field of fields) {
589
+ if (!isFieldId(field.fieldId)) {
590
+ hardErrors.push({
591
+ code: "field_scan.invalid_field_id",
592
+ message: `Invalid field id: ${field.fieldId}`,
593
+ fieldId: field.fieldId,
594
+ filePath: field.sourceFilePath ?? "unknown"
595
+ });
596
+ }
597
+ const existing = seen.get(field.fieldId);
598
+ if (existing) {
599
+ hardErrors.push({
600
+ code: "field_scan.duplicate_id",
601
+ message: `Duplicate field id: ${field.fieldId}`,
602
+ fieldId: field.fieldId,
603
+ filePath: field.sourceFilePath ?? "unknown"
604
+ });
605
+ if (existing.fieldType !== field.fieldType) {
606
+ hardErrors.push({
607
+ code: "field_scan.type_conflict",
608
+ message: `Field id ${field.fieldId} is used with multiple types.`,
609
+ fieldId: field.fieldId,
610
+ filePath: field.sourceFilePath ?? "unknown"
611
+ });
612
+ }
613
+ }
614
+ seen.set(field.fieldId, field);
615
+ for (const warning of field.warnings) {
616
+ warnings.push({
617
+ code: warning,
618
+ message: getScanIssueMessage(warning),
619
+ fieldId: field.fieldId,
620
+ filePath: field.sourceFilePath ?? "unknown"
621
+ });
622
+ }
623
+ for (const suggestion of field.suggestions) {
624
+ suggestions.push({
625
+ code: suggestion,
626
+ message: getScanIssueMessage(suggestion),
627
+ fieldId: field.fieldId,
628
+ filePath: field.sourceFilePath ?? "unknown"
629
+ });
630
+ }
631
+ }
632
+ }
633
+ function getScanIssueMessage(code) {
634
+ if (code === "field_scan.attrs_missing") {
635
+ return "Field renders a value but has no DOM discovery attrs. Add clientplane.attrs(...) to the nearest stable element so the visual editor can select it.";
636
+ }
637
+ if (code === "field_scan.positional_repeating_id") {
638
+ return "Repeating content needs stable Clientplane field IDs for the MVP. Replace positional array/index field ids with stable item ids such as services.haircut.price.";
639
+ }
640
+ if (code === "field_scan.animation_wrapper") {
641
+ return 'Put clientplane.attrs(...) on the stable wrapper and make the animation respect data-clientplane-editor="true" or useClientplaneEditorMode().';
642
+ }
643
+ return code;
644
+ }
645
+ function staticExpressionValue(expression) {
646
+ if (!expression) {
647
+ return void 0;
648
+ }
649
+ if (ts.isStringLiteral(expression) || ts.isNoSubstitutionTemplateLiteral(expression)) {
650
+ return expression.text;
651
+ }
652
+ if (ts.isNumericLiteral(expression)) {
653
+ return Number(expression.text);
654
+ }
655
+ if (expression.kind === ts.SyntaxKind.TrueKeyword) {
656
+ return true;
657
+ }
658
+ if (expression.kind === ts.SyntaxKind.FalseKeyword) {
659
+ return false;
660
+ }
661
+ if (ts.isObjectLiteralExpression(expression)) {
662
+ return readObjectLiteral(expression);
663
+ }
664
+ return void 0;
665
+ }
666
+ function readObjectLiteral(expression) {
667
+ const value = {};
668
+ for (const property of expression.properties) {
669
+ if (!ts.isPropertyAssignment(property)) {
670
+ continue;
671
+ }
672
+ const name = getPropertyName(property.name);
673
+ if (!name) {
674
+ continue;
675
+ }
676
+ value[name] = staticExpressionValue(property.initializer);
677
+ }
678
+ return value;
679
+ }
680
+ function getPropertyName(name) {
681
+ if (ts.isIdentifier(name) || ts.isStringLiteral(name)) {
682
+ return name.text;
683
+ }
684
+ return null;
685
+ }
686
+ function getAssignedVariableName(node) {
687
+ let current = node;
688
+ while (ts.isAwaitExpression(current.parent)) {
689
+ current = current.parent;
690
+ }
691
+ if (ts.isVariableDeclaration(current.parent) && ts.isIdentifier(current.parent.name)) {
692
+ return current.parent.name.text;
693
+ }
694
+ return void 0;
695
+ }
696
+ function getJsxTagName(tagName) {
697
+ return tagName.getText();
698
+ }
699
+ function stringValue(value) {
700
+ return typeof value === "string" ? value : void 0;
701
+ }
702
+ function fieldTypeValue(value) {
703
+ if (value === "text" || value === "longText" || value === "image" || value === "link") {
704
+ return value;
705
+ }
706
+ return void 0;
707
+ }
708
+ function numberValue(value) {
709
+ return typeof value === "number" ? value : void 0;
710
+ }
711
+ function booleanValue(value) {
712
+ return typeof value === "boolean" ? value : void 0;
713
+ }
714
+ function assignIfDefined(object, key, value) {
715
+ if (value !== void 0) {
716
+ object[key] = value;
717
+ }
718
+ }
719
+ function inferRouteHint(filePath) {
720
+ const normalized = filePath.replaceAll("\\", "/");
721
+ const appIndex = normalized.indexOf("app/");
722
+ if (appIndex === -1 || !normalized.endsWith("page.tsx")) {
723
+ return void 0;
724
+ }
725
+ const route = normalized.slice(appIndex + 4, -"page.tsx".length).split("/").filter((segment) => segment && !segment.startsWith("(")).join("/");
726
+ return route ? `/${route}` : "/";
727
+ }
728
+ function isPositionalFieldReference(value) {
729
+ return value ? /\[\d+\]|\.\d+(?:\.|$)/.test(value) : false;
730
+ }
731
+ function createRoutes(fields) {
732
+ const paths = /* @__PURE__ */ new Set();
733
+ for (const field of fields) {
734
+ if (field.routeHint) {
735
+ paths.add(field.routeHint);
736
+ }
737
+ }
738
+ return [...paths].map((path) => ({ path }));
739
+ }
740
+
741
+ // ../cli/dist/commands/scan.js
742
+ async function scanCommand(filePaths) {
743
+ const files = await Promise.all(filePaths.map(async (filePath) => ({
744
+ filePath,
745
+ sourceText: await readFile(filePath, "utf8")
746
+ })));
747
+ return scanNextSourceFiles(files);
748
+ }
749
+
750
+ // ../cli/dist/commands/check.js
751
+ async function checkCommand(filePaths) {
752
+ const result = await scanCommand(filePaths);
753
+ return {
754
+ ok: result.hardErrors.length === 0,
755
+ hardErrorCount: result.hardErrors.length,
756
+ warningCount: result.warnings.length,
757
+ warnings: result.warnings
758
+ };
759
+ }
760
+
761
+ // ../cli/dist/config/project-config.js
762
+ import { access, readFile as readFile2, writeFile } from "fs/promises";
763
+ import { join } from "path";
764
+ import { z as z14 } from "zod";
765
+ var clientplaneProjectConfigSchema = z14.object({
766
+ apiBaseUrl: z14.string().url(),
767
+ siteId: z14.string().trim().min(1),
768
+ workspaceId: z14.string().trim().min(1),
769
+ publicSiteKey: z14.string().trim().min(1),
770
+ manifestPath: z14.string().trim().min(1).default("clientplane.manifest.json")
771
+ });
772
+
773
+ // ../cli/dist/config/credentials.js
774
+ import { mkdir, writeFile as writeFile2 } from "fs/promises";
775
+ import { dirname, join as join2 } from "path";
776
+ import { z as z15 } from "zod";
777
+ var localCredentialsSchema = z15.object({
778
+ apiBaseUrl: z15.string().url(),
779
+ accessToken: z15.string().trim().min(1)
780
+ });
781
+
782
+ // ../cli/dist/manifest/write-manifest.js
783
+ import { mkdir as mkdir2, writeFile as writeFile3 } from "fs/promises";
784
+ import { dirname as dirname2, join as join3 } from "path";
785
+
786
+ // src/bin/clientplane.ts
787
+ async function main() {
788
+ const [, , command, ...args] = process.argv;
789
+ if (command === "check") {
790
+ const result = await checkCommand(args);
791
+ console.log(JSON.stringify(result, null, 2));
792
+ process.exitCode = result.ok ? 0 : 1;
793
+ return;
794
+ }
795
+ if (command === "scan") {
796
+ const result = await scanCommand(args);
797
+ console.log(JSON.stringify(result.manifest, null, 2));
798
+ process.exitCode = result.hardErrors.length === 0 ? 0 : 1;
799
+ return;
800
+ }
801
+ if (command === "init") {
802
+ console.log("Run Clientplane init from the app-generated setup command.");
803
+ console.log("Required next step: npm install @clientplane/runtime-next");
804
+ return;
805
+ }
806
+ if (command === "--version" || command === "-v") {
807
+ const packageJson = JSON.parse(
808
+ await readFile3(new URL("../../package.json", import.meta.url), "utf8")
809
+ );
810
+ console.log(packageJson.version);
811
+ return;
812
+ }
813
+ console.log("Usage: clientplane <init|check|scan>");
814
+ }
815
+ await main().catch((error) => {
816
+ console.error(error instanceof Error ? error.message : String(error));
817
+ process.exitCode = 1;
818
+ });