@nodaro/shared 2.27.0 → 3.0.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.
package/src/scene3d.ts CHANGED
@@ -194,7 +194,13 @@ export interface Scene3DReference {
194
194
  endSeconds?: number
195
195
  }
196
196
 
197
- export interface Scene3DPlan {
197
+ /**
198
+ * The v1 plan. `Scene3DPlan` is the DISCRIMINATED UNION of this and
199
+ * `Scene3DPlanV2` (see `scene3d-v2.ts`) — a consumer holding one must narrow
200
+ * with `isScene3DPlanV1` / `isScene3DPlanV2` before reading version-specific
201
+ * fields. Nothing about v1's shape, bounds or messages changed when v2 landed.
202
+ */
203
+ export interface Scene3DPlanV1 {
198
204
  planType: typeof SCENE3D_PLAN_TYPE
199
205
  schemaVersion: typeof SCENE3D_SCHEMA_VERSION
200
206
  /** UUID. Changes on EVERY accepted edit. */
@@ -382,11 +388,16 @@ export const scene3DReferenceSchema = z
382
388
  // Semantic (cross-field) validation
383
389
  // ---------------------------------------------------------------------------
384
390
 
385
- interface SemanticIssue {
391
+ /** One cross-field failure, in the shape `ctx.addIssue` wants. Shared by the
392
+ * v1 and v2 validators so both report the same way. */
393
+ export interface Scene3DSemanticIssue {
386
394
  path: (string | number)[]
387
395
  message: string
388
396
  }
389
397
 
398
+ /** @internal Historic in-file name. */
399
+ type SemanticIssue = Scene3DSemanticIssue
400
+
390
401
  function checkKeyframeTrack(
391
402
  frames: readonly { frame: number }[],
392
403
  durationInFrames: number,
@@ -421,7 +432,7 @@ function checkKeyframeTrack(
421
432
  * report the SAME sentences without re-parsing, and so a caller holding an
422
433
  * already-parsed plan can re-check it cheaply.
423
434
  */
424
- export function scene3DPlanIssues(plan: Scene3DPlan): SemanticIssue[] {
435
+ export function scene3DPlanV1Issues(plan: Scene3DPlanV1): SemanticIssue[] {
425
436
  const issues: SemanticIssue[] = []
426
437
 
427
438
  const seconds = plan.durationInFrames / plan.fps
@@ -526,7 +537,12 @@ export function scene3DPlanIssues(plan: Scene3DPlan): SemanticIssue[] {
526
537
  * consumer that parses with this cannot be handed a cycle, a dangling parent,
527
538
  * an out-of-range keyframe or a 90-second "one-minute-max" scene.
528
539
  */
529
- export const scene3DPlanSchema = z
540
+ /**
541
+ * The v1 object shape WITHOUT the cross-field pass. Exported only so
542
+ * `scene3DAnyPlanSchema` can discriminate on `schemaVersion` (zod cannot
543
+ * discriminate through a `superRefine`); parse with `scene3DPlanV1Schema`.
544
+ */
545
+ export const scene3DPlanV1ObjectSchema = z
530
546
  .object({
531
547
  planType: z.literal(SCENE3D_PLAN_TYPE),
532
548
  schemaVersion: z.literal(SCENE3D_SCHEMA_VERSION),
@@ -547,11 +563,20 @@ export const scene3DPlanSchema = z
547
563
  references: z.array(scene3DReferenceSchema).max(SCENE3D_LIMITS.maxReferences).optional(),
548
564
  })
549
565
  .strict()
550
- .superRefine((plan, ctx) => {
551
- for (const issue of scene3DPlanIssues(plan as Scene3DPlan)) {
552
- ctx.addIssue({ code: "custom", path: issue.path, message: issue.message })
553
- }
554
- })
566
+
567
+ export const scene3DPlanV1Schema = scene3DPlanV1ObjectSchema.superRefine((plan, ctx) => {
568
+ for (const issue of scene3DPlanV1Issues(plan as Scene3DPlanV1)) {
569
+ ctx.addIssue({ code: "custom", path: issue.path, message: issue.message })
570
+ }
571
+ })
572
+
573
+ /** @deprecated v1-only, and it always was. Kept so every existing v1 call site
574
+ * keeps EXACTLY its current accept/reject set. Use `scene3DPlanV1Schema` for
575
+ * v1, or `scene3DAnyPlanSchema` when either version is acceptable. */
576
+ export const scene3DPlanSchema = scene3DPlanV1Schema
577
+
578
+ /** @deprecated Renamed to `scene3DPlanV1Issues`. */
579
+ export const scene3DPlanIssues = scene3DPlanV1Issues
555
580
 
556
581
  /** Order-insensitive deep equality over the JSON subset a plan is made of. */
557
582
  export function scene3DDeepEqual(a: unknown, b: unknown): boolean {
@@ -588,9 +613,10 @@ export function newScene3DRevisionId(): string {
588
613
  return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`
589
614
  }
590
615
 
591
- /** Narrowing helper for callers holding `unknown` (job output, workflow JSON). */
592
- export function isScene3DPlan(value: unknown): value is Scene3DPlan {
593
- return scene3DPlanSchema.safeParse(value).success
616
+ /** Narrowing helper for callers holding `unknown` (job output, workflow JSON).
617
+ * V1 ONLY — `isScene3DPlan` (in `scene3d-v2.ts`) accepts either version. */
618
+ export function isScene3DPlanV1(value: unknown): value is Scene3DPlanV1 {
619
+ return scene3DPlanV1Schema.safeParse(value).success
594
620
  }
595
621
 
596
622
  // ---------------------------------------------------------------------------
@@ -601,7 +627,7 @@ export function isScene3DPlan(value: unknown): value is Scene3DPlan {
601
627
  * `output_data`. The canvas, the SDK and the DAG output extractor all read
602
628
  * THIS shape — `scenePlan` is also the node's stored plan field. */
603
629
  export interface Scene3DJobOutput {
604
- scenePlan: Scene3DPlan
630
+ scenePlan: Scene3DPlanV1
605
631
  /** One paragraph naming what changed. Absent on a first generation. */
606
632
  changeSummary?: string
607
633
  }