@glissade/core 0.18.0 → 0.19.0-pre.1

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/index.d.ts CHANGED
@@ -242,9 +242,20 @@ interface TweenOpts<T = unknown> {
242
242
  }
243
243
  /** The shared tween shape applied to every staggered target (§2.6 stagger sugar). */
244
244
  interface StaggerSpec<T = unknown> {
245
- to: T;
246
- /** Explicit start value routes each target through `fromTo` when present. */
247
- from?: T;
245
+ /**
246
+ * The destination value. A plain `T` fans uniformly across every target; a
247
+ * function `(index, count) => T` resolves a PER-TARGET destination (a card
248
+ * flying to its own slot). Resolved by a runtime typeof branch — consistent
249
+ * with `each: number | fn` and scene `each()`; mildly lossy when `T` is itself
250
+ * callable (acceptable). Emits N ordinary tweens, byte-identical to hand-authored.
251
+ */
252
+ to: T | ((index: number, count: number) => T);
253
+ /**
254
+ * Explicit start value — routes each target through `fromTo` when present. A
255
+ * plain `T` fans uniformly; a function `(index, count) => T` resolves a
256
+ * per-target start (same runtime typeof branch as `to`).
257
+ */
258
+ from?: T | ((index: number, count: number) => T);
248
259
  duration?: number;
249
260
  ease?: EaseSpec;
250
261
  }
@@ -284,6 +295,16 @@ interface TimelineBuilder {
284
295
  * honestly to the cursor).
285
296
  */
286
297
  stagger<T>(targets: TweenTarget[], spec: StaggerSpec<T>, opts: StaggerOpts): TimelineBuilder;
298
+ /**
299
+ * Build-time bridge for the CLIP tier (Isuo8Gxn): inject pre-built `Track[]`
300
+ * (the `{ tracks }` returned by `presence`/`clip`/`each`/`morph` on
301
+ * `@glissade/core/clips`) straight into the document. The tracks carry their
302
+ * OWN absolute keyframe times — they land as ordinary track rows through the
303
+ * same finalize→coalesce path `add()` uses for child tracks (same-target rows
304
+ * coalesce, later wins). Scoped to raw absolute-time tracks: no cursor-offset
305
+ * or rebasing wrapper (deferred). Does NOT move the cursor.
306
+ */
307
+ tracks(tracks: Track[]): TimelineBuilder;
287
308
  /** Hold key: the value snaps at the resolved position (§2.6). */
288
309
  set<T>(target: TweenTarget, value: T, opts?: {
289
310
  at?: Position;
package/dist/index.js CHANGED
@@ -580,6 +580,37 @@ var PositionError = class extends Error {
580
580
  this.name = "PositionError";
581
581
  }
582
582
  };
583
+ /**
584
+ * Reject UNKNOWN keys on a builder method's options object (k-g1zn). Each method
585
+ * destructures only the keys it understands and historically SWALLOWED the rest
586
+ * silently — a misspelled/wrong option vanished with no error. Validate against
587
+ * an allow-list and THROW a `TimelineValidationError` naming the offending key(s)
588
+ * and the method, matching the build-time-fail pattern (t<0 / non-finite each).
589
+ * Mildly breaking: stray keys that were ignored now throw.
590
+ */
591
+ function rejectUnknownOpts(method, opts, known) {
592
+ const allow = new Set(known);
593
+ const unknown = Object.keys(opts).filter((k) => !allow.has(k));
594
+ if (unknown.length > 0) throw new TimelineValidationError(`${method}: unknown option${unknown.length > 1 ? "s" : ""} ${unknown.map((k) => `'${k}'`).join(", ")} — known: ${known.map((k) => `'${k}'`).join(", ")}`);
595
+ }
596
+ const TO_OPTS_KEYS = [
597
+ "duration",
598
+ "ease",
599
+ "at",
600
+ "from"
601
+ ];
602
+ const SET_OPTS_KEYS = ["at"];
603
+ const STAGGER_SPEC_KEYS = [
604
+ "to",
605
+ "from",
606
+ "duration",
607
+ "ease"
608
+ ];
609
+ const STAGGER_OPTS_KEYS = [
610
+ "each",
611
+ "anchor",
612
+ "at"
613
+ ];
583
614
  function peekBase(target) {
584
615
  return typeof target !== "string" && typeof target.peek === "function" ? target.peek() : void 0;
585
616
  }
@@ -590,6 +621,7 @@ function getTimelineCallbacks(doc) {
590
621
  }
591
622
  function buildTimeline(build, init = {}) {
592
623
  const insertions = [];
624
+ const injectedTracks = [];
593
625
  const labels = { ...init.labels };
594
626
  const children = [];
595
627
  const markers = [];
@@ -616,6 +648,7 @@ function buildTimeline(build, init = {}) {
616
648
  }
617
649
  const builder = {
618
650
  to(target, value, opts = {}) {
651
+ rejectUnknownOpts("to", opts, TO_OPTS_KEYS);
619
652
  const ease = opts.ease ?? "easeInOutCubic";
620
653
  const isSpring = typeof ease === "object" && ease.kind === "spring";
621
654
  const duration = isSpring ? spring.duration(ease) : opts.duration ?? 1;
@@ -639,11 +672,14 @@ function buildTimeline(build, init = {}) {
639
672
  return builder;
640
673
  },
641
674
  fromTo(target, from, to, opts = {}) {
675
+ rejectUnknownOpts("fromTo", opts, TO_OPTS_KEYS);
642
676
  builder.to(target, to, opts);
643
677
  insertions[insertions.length - 1].explicitFrom = from;
644
678
  return builder;
645
679
  },
646
680
  stagger(targets, spec, opts) {
681
+ rejectUnknownOpts("stagger spec", spec, STAGGER_SPEC_KEYS);
682
+ rejectUnknownOpts("stagger opts", opts, STAGGER_OPTS_KEYS);
647
683
  const n = targets.length;
648
684
  const base = resolvePosition(opts.at);
649
685
  const c = (n - 1) / 2;
@@ -678,8 +714,11 @@ function buildTimeline(build, init = {}) {
678
714
  const start = base + d;
679
715
  if (start < 0) throw new TimelineValidationError(`stagger: target would land at t=${start} (< 0); shift opts.at`);
680
716
  const t = targets[i];
681
- if (spec.from !== void 0) builder.fromTo(t, spec.from, spec.to, tweenOpts(d));
682
- else builder.to(t, spec.to, tweenOpts(d));
717
+ const toVal = typeof spec.to === "function" ? spec.to(i, n) : spec.to;
718
+ if (spec.from !== void 0) {
719
+ const fromVal = typeof spec.from === "function" ? spec.from(i, n) : spec.from;
720
+ builder.fromTo(t, fromVal, toVal, tweenOpts(d));
721
+ } else builder.to(t, toVal, tweenOpts(d));
683
722
  }
684
723
  if (n > 0) {
685
724
  prevStart = base + minDelay;
@@ -688,6 +727,7 @@ function buildTimeline(build, init = {}) {
688
727
  return builder;
689
728
  },
690
729
  set(target, value, opts = {}) {
730
+ rejectUnknownOpts("set", opts, SET_OPTS_KEYS);
691
731
  const start = resolvePosition(opts.at);
692
732
  insertions.push({
693
733
  kind: "set",
@@ -704,6 +744,10 @@ function buildTimeline(build, init = {}) {
704
744
  prevEnd = start;
705
745
  return builder;
706
746
  },
747
+ tracks(tracks) {
748
+ for (const tr of tracks) injectedTracks.push(tr);
749
+ return builder;
750
+ },
707
751
  label(name, at) {
708
752
  labels[name] = at === void 0 ? prevEnd : resolvePosition(at);
709
753
  return builder;
@@ -842,6 +886,7 @@ function buildTimeline(build, init = {}) {
842
886
  if (editable) tr.editable = true;
843
887
  tracks.push(tr);
844
888
  }
889
+ for (const tr of injectedTracks) tracks.push(tr);
845
890
  const doc = timeline$1({
846
891
  ...init,
847
892
  tracks,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glissade/core",
3
- "version": "0.18.0",
3
+ "version": "0.19.0-pre.1",
4
4
  "description": "glissade core: signals, tracks, timeline document, evaluation, easing, springs, seeded RNG. Zero DOM/Node dependencies.",
5
5
  "license": "Apache-2.0",
6
6
  "engines": {