@flemo/core 1.30.0 → 2.1.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.
Files changed (68) hide show
  1. package/dist/core/createRouterScope.d.ts +1 -0
  2. package/dist/core/engine/__tests__/createSwipeController.dragHooks.test.d.ts +1 -0
  3. package/dist/core/engine/__tests__/createSwipeController.dragProgress.test.d.ts +1 -0
  4. package/dist/core/engine/__tests__/createSwipeController.forcedCancel.test.d.ts +1 -0
  5. package/dist/core/engine/__tests__/createSwipeController.layerRelease.test.d.ts +1 -0
  6. package/dist/core/engine/__tests__/createSwipeController.nativeDrag.test.d.ts +1 -0
  7. package/dist/core/engine/__tests__/createSwipeController.rideOffset.test.d.ts +1 -0
  8. package/dist/core/engine/__tests__/layerRiders.test.d.ts +1 -0
  9. package/dist/core/engine/__tests__/variantStub.d.ts +13 -0
  10. package/dist/core/engine/createSwipeController.d.ts +5 -0
  11. package/dist/core/engine/diagnosticFlags.d.ts +1 -0
  12. package/dist/core/engine/flightRouting.d.ts +21 -0
  13. package/dist/core/engine/layerRiders.d.ts +21 -0
  14. package/dist/core/engine/types.d.ts +1 -0
  15. package/dist/dom/__tests__/stacking.test.d.ts +1 -0
  16. package/dist/dom/attributes.d.ts +126 -2
  17. package/dist/dom/stacking.d.ts +28 -0
  18. package/dist/history/createHistorySync.d.ts +2 -0
  19. package/dist/history/store.d.ts +0 -1
  20. package/dist/index.d.ts +16 -1
  21. package/dist/index.mjs +2577 -1274
  22. package/dist/morph/__tests__/attachMorph.test.d.ts +1 -0
  23. package/dist/morph/__tests__/morphAnimations.test.d.ts +1 -0
  24. package/dist/morph/__tests__/morphClip.test.d.ts +1 -0
  25. package/dist/morph/__tests__/morphGeometry.test.d.ts +1 -0
  26. package/dist/morph/__tests__/morphKeyframes.test.d.ts +1 -0
  27. package/dist/morph/__tests__/morphLayer.test.d.ts +1 -0
  28. package/dist/morph/__tests__/morphPaint.test.d.ts +1 -0
  29. package/dist/morph/__tests__/morphPose.test.d.ts +1 -0
  30. package/dist/morph/__tests__/morphSheet.test.d.ts +1 -0
  31. package/dist/morph/__tests__/morphSide.test.d.ts +1 -0
  32. package/dist/morph/__tests__/morphSwipe.test.d.ts +1 -0
  33. package/dist/morph/attachMorph.d.ts +73 -0
  34. package/dist/morph/morphAnimations.d.ts +2 -0
  35. package/dist/morph/morphClip.d.ts +31 -0
  36. package/dist/morph/morphGeometry.d.ts +111 -0
  37. package/dist/morph/morphKeyframes.d.ts +190 -0
  38. package/dist/morph/morphLayer.d.ts +14 -0
  39. package/dist/morph/morphPaint.d.ts +31 -0
  40. package/dist/morph/morphPose.d.ts +44 -0
  41. package/dist/morph/morphSheet.d.ts +9 -0
  42. package/dist/morph/morphSide.d.ts +43 -0
  43. package/dist/morph/morphSwipe.d.ts +24 -0
  44. package/dist/navigate/createNavigationController.d.ts +0 -1
  45. package/dist/screen/__tests__/publishRideBox.test.d.ts +1 -0
  46. package/dist/screen/publishRideBox.d.ts +1 -0
  47. package/dist/transition/__tests__/compileTransitionStyles.test.d.ts +2 -0
  48. package/dist/transition/__tests__/rideOffset.test.d.ts +1 -0
  49. package/dist/transition/compileTransitionStyles.d.ts +2 -1
  50. package/dist/transition/cubicBezier.d.ts +17 -0
  51. package/dist/transition/decorator/__tests__/resolveDecoratorClock.test.d.ts +14 -0
  52. package/dist/transition/decorator/createDecorator.d.ts +4 -5
  53. package/dist/transition/decorator/createRawDecorator.d.ts +10 -11
  54. package/dist/transition/decorator/resolveDecoratorClock.d.ts +36 -0
  55. package/dist/transition/decorator/typing.d.ts +8 -2
  56. package/dist/transition/morphTransition/__tests__/createMorphTransition.test.d.ts +1 -0
  57. package/dist/transition/morphTransition/createMorphTransition.d.ts +13 -0
  58. package/dist/transition/morphTransition/createRawMorphTransition.d.ts +17 -0
  59. package/dist/transition/morphTransition/morphTransition.d.ts +2 -0
  60. package/dist/transition/morphTransition/shared.d.ts +2 -0
  61. package/dist/transition/morphTransition/text.d.ts +2 -0
  62. package/dist/transition/morphTransition/typing.d.ts +72 -0
  63. package/dist/transition/morphTransition/zoom.d.ts +2 -0
  64. package/dist/transition/registerTransitionDefinitions.d.ts +2 -1
  65. package/dist/transition/rideOffset.d.ts +5 -0
  66. package/dist/transition/swipeSettle.d.ts +5 -1
  67. package/dist/transition/typing.d.ts +12 -1
  68. package/package.json +7 -7
@@ -7,9 +7,10 @@ export type CssDecl = {
7
7
  value: string;
8
8
  };
9
9
  export declare const collectAnimatedProperties: (transition: Pick<Transition, "initial" | "variants">) => string[];
10
- export declare const targetToDecls: (target: TransitionVariantValue["value"] | InitialTarget) => CssDecl[];
10
+ export declare const targetToDecls: (target: TransitionVariantValue["value"] | InitialTarget, ride?: boolean) => CssDecl[];
11
11
  export declare const easingToCss: (ease: AnimationOptions["ease"] | undefined) => string;
12
12
  export declare const animationName: (scope: "screen" | "decorator" | "part", name: string, variant: TransitionVariant) => string;
13
+ export declare const decoratorAnimationName: (transitionName: string, decoratorName: string, variant: TransitionVariant) => string;
13
14
  export declare const HEAD_ANIMATION_SUFFIXES: readonly ["-gov", "-deskhead", "-govcreep"];
14
15
  export declare const matchesFlightAnimationName: (eventName: string, expectedName: string) => boolean;
15
16
  export declare const compileTransitionStyles: (transitions: Iterable<Transition>, decorators: Iterable<Decorator>, partTransitions?: Iterable<PartTransition>) => string;
@@ -1,6 +1,23 @@
1
1
  import { AnimationOptions } from './cssTypes';
2
2
  export type EasingFunction = (progress: number) => number;
3
3
  export declare const cubicBezier: (x1: number, y1: number, x2: number, y2: number) => EasingFunction;
4
+ /**
5
+ * The INVERSE of a CSS timing function: given the value progress you want to
6
+ * see, the time progress that produces it.
7
+ *
8
+ * A scrubbed animation needs this. Setting an animation's clock to the drag's
9
+ * fraction moves the CLOCK linearly, and what the eye follows is the curve's
10
+ * output — with an ease that opens slowly, a finger a tenth of the way across
11
+ * moves the element a fiftieth, and everything it did not do is left for the
12
+ * release to rush through. Inverting the curve makes the element track the
13
+ * finger and leaves the release only what is really left.
14
+ *
15
+ * Binary search on y, the same shape as `cubicBezier`'s search on x. An
16
+ * overshooting curve (`backOut`, `anticipate`) is not monotonic in y, so the
17
+ * search returns the first crossing — which is the one before the overshoot,
18
+ * and the only one a drag can mean.
19
+ */
20
+ export declare const invertEasing: (ease: AnimationOptions["ease"] | undefined) => EasingFunction;
4
21
  /**
5
22
  * The control points behind an authored ease, named or spelled out — `null`
6
23
  * for `linear`, which has no handles to work with.
@@ -0,0 +1,14 @@
1
+ declare module '../../typing' {
2
+ interface RegisterTransition {
3
+ "clock-long": "clock-long";
4
+ "clock-short": "clock-short";
5
+ "clock-directional": "clock-directional";
6
+ }
7
+ }
8
+ declare module '../typing' {
9
+ interface RegisterDecorator {
10
+ "clock-dim": "clock-dim";
11
+ "clock-fixed": "clock-fixed";
12
+ }
13
+ }
14
+ export {};
@@ -1,12 +1,11 @@
1
1
  import { InitialTarget } from '../cssTypes';
2
- import { TransitionVariantValue } from '../typing';
3
- import { DecoratorName, Decorator, DecoratorOptions } from './typing';
2
+ import { DecoratorName, Decorator, DecoratorOptions, DecoratorVariantValue } from './typing';
4
3
  interface CreateDecoratorProps {
5
4
  name: DecoratorName;
6
5
  initial: InitialTarget;
7
- idle: TransitionVariantValue;
8
- enter: TransitionVariantValue;
9
- exit: TransitionVariantValue;
6
+ idle: DecoratorVariantValue;
7
+ enter: DecoratorVariantValue;
8
+ exit: DecoratorVariantValue;
10
9
  options?: DecoratorOptions;
11
10
  }
12
11
  export default function createDecorator({ name, initial, idle, enter, exit, options }: CreateDecoratorProps): Decorator;
@@ -1,18 +1,17 @@
1
1
  import { InitialTarget } from '../cssTypes';
2
- import { TransitionVariantValue } from '../typing';
3
- import { DecoratorName, Decorator, DecoratorOptions } from './typing';
2
+ import { DecoratorName, Decorator, DecoratorOptions, DecoratorVariantValue } from './typing';
4
3
  interface CreateRawDecoratorProps {
5
4
  name: DecoratorName;
6
5
  initial: InitialTarget;
7
- idle: TransitionVariantValue;
8
- pushOnEnter: TransitionVariantValue;
9
- pushOnExit: TransitionVariantValue;
10
- replaceOnEnter: TransitionVariantValue;
11
- replaceOnExit: TransitionVariantValue;
12
- popOnEnter: TransitionVariantValue;
13
- popOnExit: TransitionVariantValue;
14
- completedOnEnter: TransitionVariantValue;
15
- completedOnExit: TransitionVariantValue;
6
+ idle: DecoratorVariantValue;
7
+ pushOnEnter: DecoratorVariantValue;
8
+ pushOnExit: DecoratorVariantValue;
9
+ replaceOnEnter: DecoratorVariantValue;
10
+ replaceOnExit: DecoratorVariantValue;
11
+ popOnEnter: DecoratorVariantValue;
12
+ popOnExit: DecoratorVariantValue;
13
+ completedOnEnter: DecoratorVariantValue;
14
+ completedOnExit: DecoratorVariantValue;
16
15
  options?: DecoratorOptions;
17
16
  }
18
17
  export default function createRawDecorator({ name, initial, idle, pushOnEnter, pushOnExit, replaceOnEnter, replaceOnExit, popOnEnter, popOnExit, completedOnEnter, completedOnExit, options }: CreateRawDecoratorProps): Decorator;
@@ -0,0 +1,36 @@
1
+ import { BaseTransition, Transition } from '../typing';
2
+ import { Decorator } from './typing';
3
+ /**
4
+ * A decorator's variant table with its clock filled in from the screen
5
+ * transition that names it.
6
+ *
7
+ * A decorator is only ever reached through `transition.decoratorName`, so the
8
+ * flight it dresses already decides how long it has. Authoring the length a
9
+ * second time is how the two drift apart, and this repository worked around
10
+ * that three times before removing the duplication:
11
+ *
12
+ * - `overlay` and `cupertino` each restated 0.7s, with a comment on the
13
+ * decorator saying the number came from the transition.
14
+ * - the playground's `drift` and `recess` share a constants file for no
15
+ * other reason than to keep two hand-written clocks equal.
16
+ * - `layout` dropped its dim ALTOGETHER rather than inherit `overlay`'s
17
+ * 0.7s over a 0.4s flight: measured on a pop, the dismissing screen was
18
+ * fully gone at 335ms while the screen underneath still carried a 10%
19
+ * black wash, which reads as a grey cast appearing from nowhere and then
20
+ * lifting for no reason.
21
+ *
22
+ * The rule is the SAME VARIANT KEY, which is exactly the mapping those
23
+ * workarounds were doing by hand: a decorator's `enter` sits at PUSHING-false
24
+ * with the screen's `exit`, its `exit` at POPPING-false with the screen's
25
+ * `exitBack`. It therefore also carries direction for free — a preset whose
26
+ * push and pop differ (material runs 0.35s and 0.25s) gives its dim the same
27
+ * asymmetry without the author restating it.
28
+ *
29
+ * Resolution is COMPILE TIME and produces a literal. It must never become a
30
+ * `var()` in `animation-duration`: timing that depended on custom properties
31
+ * lost WebKit's accelerated playback and collapsed to a 2-frame snap under
32
+ * main-thread starvation (device-bisected 2026-08-13, see
33
+ * compileTransitionStyles.ts).
34
+ */
35
+ export declare const resolveDecoratorClock: (transition: Pick<Transition, "variants">, decorator: Pick<Decorator, "initial" | "variants">) => Pick<BaseTransition, "initial" | "variants">;
36
+ export default resolveDecoratorClock;
@@ -1,4 +1,5 @@
1
- import { BaseTransition, SwipeAnimate } from '../typing';
1
+ import { AnimationOptions, TransitionTarget } from '../cssTypes';
2
+ import { BaseTransition, SwipeAnimate, TransitionVariant } from '../typing';
2
3
  export interface RegisterDecorator {
3
4
  }
4
5
  export type DecoratorName = RegisterDecorator[keyof RegisterDecorator] | "overlay";
@@ -19,6 +20,11 @@ export type DecoratorOptions = {
19
20
  prevDecorator: HTMLDivElement;
20
21
  }) => void;
21
22
  };
22
- export interface Decorator extends Omit<BaseTransition, "name">, DecoratorOptions {
23
+ export type DecoratorVariantValue = {
24
+ value: TransitionTarget;
25
+ options?: AnimationOptions;
26
+ };
27
+ export interface Decorator extends Omit<BaseTransition, "name" | "variants">, DecoratorOptions {
23
28
  name: DecoratorName;
29
+ variants: Record<TransitionVariant, DecoratorVariantValue>;
24
30
  }
@@ -0,0 +1,13 @@
1
+ import { InitialTarget } from '../cssTypes';
2
+ import { TransitionVariantValue } from '../typing';
3
+ import { MorphTransition, MorphTransitionName, MorphTransitionOptions } from './typing';
4
+ interface CreateMorphProps {
5
+ name: MorphTransitionName;
6
+ initial: InitialTarget;
7
+ idle: TransitionVariantValue;
8
+ enter: TransitionVariantValue;
9
+ exit: TransitionVariantValue;
10
+ options?: MorphTransitionOptions;
11
+ }
12
+ export default function createMorphTransition({ name, initial, idle, enter, exit, options }: CreateMorphProps): MorphTransition;
13
+ export {};
@@ -0,0 +1,17 @@
1
+ import { InitialTarget } from '../cssTypes';
2
+ import { TransitionVariantValue } from '../typing';
3
+ import { MorphTransition, MorphTransitionName, MorphTransitionOptions } from './typing';
4
+ interface CreateRawMorphProps {
5
+ name: MorphTransitionName;
6
+ initial: InitialTarget;
7
+ idle: TransitionVariantValue;
8
+ pushOnEnter: TransitionVariantValue;
9
+ pushOnExit: TransitionVariantValue;
10
+ replaceOnEnter: TransitionVariantValue;
11
+ replaceOnExit: TransitionVariantValue;
12
+ popOnEnter: TransitionVariantValue;
13
+ popOnExit: TransitionVariantValue;
14
+ options?: MorphTransitionOptions;
15
+ }
16
+ export default function createRawMorphTransition({ name, initial, idle, pushOnEnter, pushOnExit, replaceOnEnter, replaceOnExit, popOnEnter, popOnExit, options }: CreateRawMorphProps): MorphTransition;
17
+ export {};
@@ -0,0 +1,2 @@
1
+ import { MorphTransition, MorphTransitionName } from './typing';
2
+ export declare const morphTransitionMap: Map<MorphTransitionName, MorphTransition>;
@@ -0,0 +1,2 @@
1
+ declare const shared: import('./typing').MorphTransition;
2
+ export default shared;
@@ -0,0 +1,2 @@
1
+ declare const text: import('./typing').MorphTransition;
2
+ export default text;
@@ -0,0 +1,72 @@
1
+ import { BaseTransition, TransitionVariant } from '../typing';
2
+ export interface RegisterMorphTransition {
3
+ }
4
+ export type MorphTransitionName = RegisterMorphTransition[keyof RegisterMorphTransition] | "shared" | "text" | "zoom" | (string & {});
5
+ /** The preset every morph falls back to when no name is given. */
6
+ export declare const DEFAULT_MORPH_TRANSITION_NAME = "shared";
7
+ export type MorphTransitionOptions = {
8
+ /**
9
+ * The share of the flight over which the GHOST dissolves — the copy of the
10
+ * element being replaced that the runtime carries inside the flight (0–1,
11
+ * default 0.55).
12
+ *
13
+ * It is what makes a travel show the right thing at both ends: the flight
14
+ * begins as an exact copy of what was on glass and dissolves into the real
15
+ * arriving element while the box moves. Set it to 0 to carry no copy, which
16
+ * cuts straight to the arrival's content on the first frame.
17
+ *
18
+ * The arriving element stays opaque underneath throughout. Fading both would
19
+ * bleed the background through the pair by a(1 - a) — a luminance dip
20
+ * peaking at 25% in the middle of the hand-over.
21
+ *
22
+ * THE WINDOW IS WHAT THE DEPARTURE'S OWN CONTENT LEAVES OVER. This used to be
23
+ * 0.22 — three frames of a short flight — on the reasoning that holding the
24
+ * copy longer let the two layouts drift apart underneath it and print as
25
+ * doubled text. That reasoning was written when the copy painted everything,
26
+ * including the paired elements. It does not now: a paired descendant is
27
+ * already invisible in the copy (the real one is morphing underneath it), so
28
+ * all the copy is holding is content with NO counterpart — a caption, a body
29
+ * paragraph, a button that exists on one side only. Three frames is not that
30
+ * content leaving; it is that content being cut, which is exactly what a pop
31
+ * looked like next to the arrival it was supposed to reverse.
32
+ */
33
+ crossFade?: number;
34
+ /**
35
+ * Interpolate `border-radius` across the flight (default true). Nothing is
36
+ * scaled, so the two ends' own values are the whole story.
37
+ *
38
+ * It rides the CONTENT animation, never the geometry one: a keyframe that
39
+ * lists a property the compositor cannot animate drops that whole animation
40
+ * to the main thread, and the geometry keyframe is the one that must never
41
+ * leave the compositor.
42
+ */
43
+ radius?: boolean;
44
+ /**
45
+ * Move the whole screen with the element, not just the element (default off).
46
+ *
47
+ * A plain morph flies ALONE: the element crosses while the screens do
48
+ * whatever their own transition says, which is right when the element is one
49
+ * thing among many. It is wrong when the element IS the navigation — a grid
50
+ * cell opening into a full-screen view. There the eye reads the tap as
51
+ * "the camera moved to this card", and a grid that stays put underneath
52
+ * reads as the card escaping from it.
53
+ *
54
+ * `"screen"` gives the flight a camera: the screen the element is SMALL on
55
+ * (the grid — the departing screen on a push, the arriving one on a pop) is
56
+ * scaled and translated by exactly the zoom that takes the element from one
57
+ * end to the other, so every other card moves as though the viewport had
58
+ * pushed in on the tapped one. Material calls the pattern a container
59
+ * transform; iOS 18 calls its version a zoom transition.
60
+ *
61
+ * It SUPERSEDES that screen's own transform for the length of the flight —
62
+ * the camera IS the screen's motion here, and two authors of one transform
63
+ * is not a thing CSS can compose. Pair it with a transition that does not
64
+ * move the screen itself (`none`, or an opacity-only one); a slide would be
65
+ * replaced rather than combined.
66
+ */
67
+ carry?: "screen";
68
+ };
69
+ export interface MorphTransition extends Omit<BaseTransition, "name">, MorphTransitionOptions {
70
+ name: MorphTransitionName;
71
+ }
72
+ export declare const MORPH_FROM_VARIANT: Record<TransitionVariant, "initial" | "IDLE-true" | "self">;
@@ -0,0 +1,2 @@
1
+ declare const zoom: import('./typing').MorphTransition;
2
+ export default zoom;
@@ -1,4 +1,5 @@
1
1
  import { Transition } from './typing';
2
2
  import { Decorator } from './decorator/typing';
3
+ import { MorphTransition } from './morphTransition/typing';
3
4
  import { PartTransition } from './partTransition/typing';
4
- export default function registerTransitionDefinitions(transitions: Transition[], decorators: Decorator[], partTransitions?: PartTransition[]): () => void;
5
+ export default function registerTransitionDefinitions(transitions: Transition[], decorators: Decorator[], partTransitions?: PartTransition[], morphTransitions?: MorphTransition[]): () => void;
@@ -0,0 +1,5 @@
1
+ import { TransitionTarget } from './cssTypes';
2
+ export declare const RIDE_HEIGHT_VAR = "--flemo-ride-y";
3
+ export declare const percentRatio: (value: unknown) => number | null;
4
+ export declare const rideLength: (ratio: number) => string;
5
+ export declare const resolveRideTarget: <T extends TransitionTarget>(target: T, screenHeight: number) => T;
@@ -29,8 +29,12 @@ export interface SwipeSettleInput {
29
29
  * time that curve itself spends on the stretch that is left; without them it
30
30
  * falls back to reading the motion as linear, which is what this used to do
31
31
  * for every transition.
32
+ *
33
+ * `null` is accepted as well as omission, because the callers that build
34
+ * this read their curve from `easeControlPoints`, which reports "no curve"
35
+ * as null. Converting at every call site bought nothing but a branch.
32
36
  */
33
- authoredEase?: readonly [number, number, number, number];
37
+ authoredEase?: readonly [number, number, number, number] | null;
34
38
  minSeconds?: number;
35
39
  }
36
40
  /**
@@ -42,7 +42,18 @@ export type TransitionOptions = {
42
42
  animate: SwipeAnimate;
43
43
  currentScreen: HTMLDivElement;
44
44
  prevScreen: HTMLDivElement;
45
- onProgress?: (triggered: boolean, progress: number) => void;
45
+ /**
46
+ * Report the gesture's VERDICT so the decorator and the parts can
47
+ * follow it. The controller supplies their progress itself, against
48
+ * the box the screen is actually dragged over, so a second argument
49
+ * is accepted for source compatibility and is not read.
50
+ *
51
+ * It used to be. A transition's own progress is in the transition's
52
+ * own unit — material's is a pull in pixels, layout's is a constant
53
+ * — so what reached a decorator depended on which preset it was
54
+ * paired with, and could not honour the 0-100 those hooks document.
55
+ */
56
+ onProgress?: (triggered: boolean, progress?: number) => void;
46
57
  }) => number;
47
58
  onSwipeEnd: (event: PointerEvent, info: SwipeInfo, options: {
48
59
  animate: SwipeAnimate;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flemo/core",
3
- "version": "1.30.0",
3
+ "version": "2.1.0",
4
4
  "description": "Framework-agnostic primitives for flemo: history, navigation, transitions, task manager.",
5
5
  "main": "./dist/index.mjs",
6
6
  "module": "./dist/index.mjs",
@@ -33,20 +33,20 @@
33
33
  "license": "MIT",
34
34
  "dependencies": {
35
35
  "path-to-regexp": "^8.4.2",
36
- "zustand": "^5.0.14"
36
+ "zustand": "^5.0.15"
37
37
  },
38
38
  "devDependencies": {
39
+ "@flemo/eslint-config": "0.0.0",
40
+ "@flemo/tsconfig": "0.0.0",
39
41
  "@types/node": "^24.13.1",
40
- "@vitest/coverage-v8": "^4.1.10",
42
+ "@vitest/coverage-v8": "^4.1.11",
41
43
  "csstype": "^3.2.3",
42
44
  "eslint": "^9.39.5",
43
45
  "jsdom": "^29.1.1",
44
46
  "typescript": "^6.0.3",
45
- "vite": "^8.2.1",
47
+ "vite": "^8.2.2",
46
48
  "vite-plugin-dts": "^5.0.3",
47
- "vitest": "^4.1.10",
48
- "@flemo/eslint-config": "0.0.0",
49
- "@flemo/tsconfig": "0.0.0"
49
+ "vitest": "^4.1.11"
50
50
  },
51
51
  "publishConfig": {
52
52
  "access": "public"