@oxyhq/bloom 0.66.0 → 0.68.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 (30) hide show
  1. package/lib/commonjs/dialog/Dialog.web.js +31 -13
  2. package/lib/commonjs/dialog/Dialog.web.js.map +1 -1
  3. package/lib/commonjs/toast/Toaster.js +5 -0
  4. package/lib/commonjs/toast/Toaster.js.map +1 -1
  5. package/lib/commonjs/toast/use-single-outlet-guard.js +72 -0
  6. package/lib/commonjs/toast/use-single-outlet-guard.js.map +1 -0
  7. package/lib/module/dialog/Dialog.web.js +31 -12
  8. package/lib/module/dialog/Dialog.web.js.map +1 -1
  9. package/lib/module/toast/Toaster.js +5 -0
  10. package/lib/module/toast/Toaster.js.map +1 -1
  11. package/lib/module/toast/use-single-outlet-guard.js +65 -0
  12. package/lib/module/toast/use-single-outlet-guard.js.map +1 -0
  13. package/lib/typescript/commonjs/dialog/Dialog.web.d.ts +1 -3
  14. package/lib/typescript/commonjs/dialog/Dialog.web.d.ts.map +1 -1
  15. package/lib/typescript/commonjs/toast/Toaster.d.ts.map +1 -1
  16. package/lib/typescript/commonjs/toast/use-single-outlet-guard.d.ts +9 -0
  17. package/lib/typescript/commonjs/toast/use-single-outlet-guard.d.ts.map +1 -0
  18. package/lib/typescript/module/dialog/Dialog.web.d.ts +1 -3
  19. package/lib/typescript/module/dialog/Dialog.web.d.ts.map +1 -1
  20. package/lib/typescript/module/toast/Toaster.d.ts.map +1 -1
  21. package/lib/typescript/module/toast/use-single-outlet-guard.d.ts +9 -0
  22. package/lib/typescript/module/toast/use-single-outlet-guard.d.ts.map +1 -0
  23. package/package.json +347 -87
  24. package/src/__tests__/Dialog.web.test.tsx +5 -2
  25. package/src/__tests__/backdrop-fade-form.test.ts +91 -0
  26. package/src/__tests__/exports-map-contract.test.ts +112 -0
  27. package/src/__tests__/toast-single-outlet-guard.test.tsx +176 -0
  28. package/src/dialog/Dialog.web.tsx +32 -10
  29. package/src/toast/Toaster.tsx +6 -0
  30. package/src/toast/use-single-outlet-guard.ts +72 -0
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Bloom-original — the dev-only guard against a SECOND toast outlet.
5
+ *
6
+ * Every `Toaster` subscribes to the ONE module-level `toastStore`, which has no
7
+ * notion of an owning outlet, so N mounted outlets each render the full row set:
8
+ * a toast appears N times. Nothing in the engine can dedupe that — the outlets
9
+ * are legitimate, independent subscribers — so the contract is "mount exactly
10
+ * one" and this is what enforces it.
11
+ *
12
+ * WHY A WARNING IS WORTH ITS WEIGHT: the copies are not visually separated, so
13
+ * the defect does not look like duplication. Two bare outlets take identical
14
+ * defaults and land pixel-identical; two outlets whose `offset` differs by a
15
+ * point land 1px apart. Only counting the rendered rows reveals it, which is not
16
+ * something anyone thinks to do — the reports this guard comes from all read as
17
+ * "the toast looks wrong", never as "there are two of them". Stacking is on by
18
+ * default, so a duplicate outlet now doubles the rows INSIDE a stack.
19
+ *
20
+ * The counter is decremented on unmount, so a remount, a route change that moves
21
+ * the outlet, Fast Refresh and React's StrictMode double-invoke (setup, cleanup,
22
+ * setup) all pass through a count of 1 and stay silent. The latch keeps the
23
+ * warning to exactly one per module lifetime rather than one per extra outlet,
24
+ * and — because StrictMode replays the setups — one per app, not two.
25
+ *
26
+ * Gated on `process.env.NODE_ENV`, the same mechanism as
27
+ * `content-panel/nesting-context.ts`: Metro and Vite/Rolldown both fold it
28
+ * statically, so production keeps the counter, the branch and the message out of
29
+ * the bundle entirely. Deliberately NOT `__DEV__`, which is a Metro global that a
30
+ * plain web bundler does not define. It only ever warns — a duplicate outlet
31
+ * renders a redundant toast, it does not break the app, so it must never throw.
32
+ */
33
+ import * as React from 'react';
34
+ let mountedOutlets = 0;
35
+ let hasWarned = false;
36
+ export function useSingleOutletGuard() {
37
+ React.useEffect(() => {
38
+ if (process.env.NODE_ENV === 'production') {
39
+ return;
40
+ }
41
+ mountedOutlets += 1;
42
+ if (mountedOutlets > 1 && !hasWarned) {
43
+ hasWarned = true;
44
+ // Internal Bloom diagnostic: the consumer's tree is the only place this
45
+ // can be fixed, so it names the usual cause rather than just the symptom.
46
+ // eslint-disable-next-line no-console
47
+ console.warn(`[Bloom] Toaster: ${mountedOutlets} toast outlets are mounted, so every ` + 'toast renders once per outlet. The copies overlap almost exactly, so ' + 'this reads as a rendering glitch rather than as duplicate rows. Mount ' + "exactly one: @oxyhq/services' OxyProvider already renders a " + 'ToastOutlet, so an app that uses it must not render its own.');
48
+ }
49
+ return () => {
50
+ mountedOutlets -= 1;
51
+ };
52
+ }, []);
53
+ }
54
+
55
+ /**
56
+ * Test-only reset: both the counter and the one-shot latch are module state that
57
+ * outlives a render tree, so without this the first suite to mount two outlets
58
+ * would latch the warning and leave every later suite asserting against a guard
59
+ * that can no longer fire — passing whatever the implementation did.
60
+ */
61
+ export function resetSingleOutletGuardForTests() {
62
+ mountedOutlets = 0;
63
+ hasWarned = false;
64
+ }
65
+ //# sourceMappingURL=use-single-outlet-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["React","mountedOutlets","hasWarned","useSingleOutletGuard","useEffect","process","env","NODE_ENV","console","warn","resetSingleOutletGuardForTests"],"sourceRoot":"../../../src","sources":["toast/use-single-outlet-guard.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,KAAKA,KAAK,MAAM,OAAO;AAE9B,IAAIC,cAAc,GAAG,CAAC;AACtB,IAAIC,SAAS,GAAG,KAAK;AAErB,OAAO,SAASC,oBAAoBA,CAAA,EAAS;EAC3CH,KAAK,CAACI,SAAS,CAAC,MAAM;IACpB,IAAIC,OAAO,CAACC,GAAG,CAACC,QAAQ,KAAK,YAAY,EAAE;MACzC;IACF;IAEAN,cAAc,IAAI,CAAC;IACnB,IAAIA,cAAc,GAAG,CAAC,IAAI,CAACC,SAAS,EAAE;MACpCA,SAAS,GAAG,IAAI;MAChB;MACA;MACA;MACAM,OAAO,CAACC,IAAI,CACV,oBAAoBR,cAAc,uCAAuC,GACvE,uEAAuE,GACvE,wEAAwE,GACxE,8DAA8D,GAC9D,8DACJ,CAAC;IACH;IAEA,OAAO,MAAM;MACXA,cAAc,IAAI,CAAC;IACrB,CAAC;EACH,CAAC,EAAE,EAAE,CAAC;AACR;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASS,8BAA8BA,CAAA,EAAS;EACrDT,cAAc,GAAG,CAAC;EAClBC,SAAS,GAAG,KAAK;AACnB","ignoreList":[]}
@@ -47,8 +47,6 @@ export declare function AutoMountedDialog({ title, description, actions, onResol
47
47
  * injection is guarded by a unique style id.
48
48
  *
49
49
  * ```css
50
- * @keyframes bloomDialogFadeIn { from { opacity: 0; } to { opacity: 1; } }
51
- * @keyframes bloomDialogFadeOut { from { opacity: 1; } to { opacity: 0; } }
52
50
  * @keyframes bloomDialogZoomFadeIn { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } }
53
51
  * @keyframes bloomDialogZoomFadeOut { from { opacity: 1; transform: scale(1); } to { opacity: 0; transform: scale(0.95); } }
54
52
  * ```
@@ -57,5 +55,5 @@ export declare function AutoMountedDialog({ title, description, actions, onResol
57
55
  * self-contained inline CSS transitions and need no keyframe injection. The
58
56
  * `bottom` placement uses bloom's `BottomSheet` (reanimated) and needs none.
59
57
  */
60
- export declare const BLOOM_DIALOG_CSS = "\n@keyframes bloomDialogFadeIn { from { opacity: 0; } to { opacity: 1; } }\n@keyframes bloomDialogFadeOut { from { opacity: 1; } to { opacity: 0; } }\n@keyframes bloomDialogZoomFadeIn { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } }\n@keyframes bloomDialogZoomFadeOut { from { opacity: 1; transform: scale(1); } to { opacity: 0; transform: scale(0.95); } }\n";
58
+ export declare const BLOOM_DIALOG_CSS = "\n@keyframes bloomDialogZoomFadeIn { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } }\n@keyframes bloomDialogZoomFadeOut { from { opacity: 1; transform: scale(1); } to { opacity: 0; transform: scale(0.95); } }\n";
61
59
  //# sourceMappingURL=Dialog.web.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Dialog.web.d.ts","sourceRoot":"","sources":["../../../../src/dialog/Dialog.web.tsx"],"names":[],"mappings":"AAuCA,OAAO,EAOL,4BAA4B,EAK7B,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACV,YAAY,EAGZ,WAAW,EACZ,MAAM,SAAS,CAAC;AAuDjB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,WAAW,2CAazD;AA+ZD,OAAO,EAAE,4BAA4B,EAAE,CAAC;AAiPxC;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,WAAW,EACX,OAAO,EACP,SAAS,GACV,EAAE;IACD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB,2CAgBA;AAiCD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,gBAAgB,mZAK5B,CAAC"}
1
+ {"version":3,"file":"Dialog.web.d.ts","sourceRoot":"","sources":["../../../../src/dialog/Dialog.web.tsx"],"names":[],"mappings":"AA2CA,OAAO,EAOL,4BAA4B,EAK7B,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACV,YAAY,EAGZ,WAAW,EACZ,MAAM,SAAS,CAAC;AAwDjB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,WAAW,2CAazD;AAobD,OAAO,EAAE,4BAA4B,EAAE,CAAC;AAiPxC;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,WAAW,EACX,OAAO,EACP,SAAS,GACV,EAAE;IACD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB,2CAgBA;AAiCD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,gBAAgB,8PAG5B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"Toaster.d.ts","sourceRoot":"","sources":["../../../../src/toast/Toaster.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAS/B,OAAO,KAAK,EAGV,YAAY,EAGb,MAAM,SAAS,CAAC;AAyBjB,eAAO,MAAM,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CAyM1C,CAAC"}
1
+ {"version":3,"file":"Toaster.d.ts","sourceRoot":"","sources":["../../../../src/toast/Toaster.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAS/B,OAAO,KAAK,EAGV,YAAY,EAGb,MAAM,SAAS,CAAC;AA0BjB,eAAO,MAAM,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CA8M1C,CAAC"}
@@ -0,0 +1,9 @@
1
+ export declare function useSingleOutletGuard(): void;
2
+ /**
3
+ * Test-only reset: both the counter and the one-shot latch are module state that
4
+ * outlives a render tree, so without this the first suite to mount two outlets
5
+ * would latch the warning and leave every later suite asserting against a guard
6
+ * that can no longer fire — passing whatever the implementation did.
7
+ */
8
+ export declare function resetSingleOutletGuardForTests(): void;
9
+ //# sourceMappingURL=use-single-outlet-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-single-outlet-guard.d.ts","sourceRoot":"","sources":["../../../../src/toast/use-single-outlet-guard.ts"],"names":[],"mappings":"AAmCA,wBAAgB,oBAAoB,IAAI,IAAI,CAyB3C;AAED;;;;;GAKG;AACH,wBAAgB,8BAA8B,IAAI,IAAI,CAGrD"}
@@ -47,8 +47,6 @@ export declare function AutoMountedDialog({ title, description, actions, onResol
47
47
  * injection is guarded by a unique style id.
48
48
  *
49
49
  * ```css
50
- * @keyframes bloomDialogFadeIn { from { opacity: 0; } to { opacity: 1; } }
51
- * @keyframes bloomDialogFadeOut { from { opacity: 1; } to { opacity: 0; } }
52
50
  * @keyframes bloomDialogZoomFadeIn { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } }
53
51
  * @keyframes bloomDialogZoomFadeOut { from { opacity: 1; transform: scale(1); } to { opacity: 0; transform: scale(0.95); } }
54
52
  * ```
@@ -57,5 +55,5 @@ export declare function AutoMountedDialog({ title, description, actions, onResol
57
55
  * self-contained inline CSS transitions and need no keyframe injection. The
58
56
  * `bottom` placement uses bloom's `BottomSheet` (reanimated) and needs none.
59
57
  */
60
- export declare const BLOOM_DIALOG_CSS = "\n@keyframes bloomDialogFadeIn { from { opacity: 0; } to { opacity: 1; } }\n@keyframes bloomDialogFadeOut { from { opacity: 1; } to { opacity: 0; } }\n@keyframes bloomDialogZoomFadeIn { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } }\n@keyframes bloomDialogZoomFadeOut { from { opacity: 1; transform: scale(1); } to { opacity: 0; transform: scale(0.95); } }\n";
58
+ export declare const BLOOM_DIALOG_CSS = "\n@keyframes bloomDialogZoomFadeIn { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } }\n@keyframes bloomDialogZoomFadeOut { from { opacity: 1; transform: scale(1); } to { opacity: 0; transform: scale(0.95); } }\n";
61
59
  //# sourceMappingURL=Dialog.web.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Dialog.web.d.ts","sourceRoot":"","sources":["../../../../src/dialog/Dialog.web.tsx"],"names":[],"mappings":"AAuCA,OAAO,EAOL,4BAA4B,EAK7B,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACV,YAAY,EAGZ,WAAW,EACZ,MAAM,SAAS,CAAC;AAuDjB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,WAAW,2CAazD;AA+ZD,OAAO,EAAE,4BAA4B,EAAE,CAAC;AAiPxC;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,WAAW,EACX,OAAO,EACP,SAAS,GACV,EAAE;IACD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB,2CAgBA;AAiCD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,gBAAgB,mZAK5B,CAAC"}
1
+ {"version":3,"file":"Dialog.web.d.ts","sourceRoot":"","sources":["../../../../src/dialog/Dialog.web.tsx"],"names":[],"mappings":"AA2CA,OAAO,EAOL,4BAA4B,EAK7B,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EACV,YAAY,EAGZ,WAAW,EACZ,MAAM,SAAS,CAAC;AAwDjB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EAAE,WAAW,2CAazD;AAobD,OAAO,EAAE,4BAA4B,EAAE,CAAC;AAiPxC;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,WAAW,EACX,OAAO,EACP,SAAS,GACV,EAAE;IACD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB,2CAgBA;AAiCD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,gBAAgB,8PAG5B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"Toaster.d.ts","sourceRoot":"","sources":["../../../../src/toast/Toaster.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAS/B,OAAO,KAAK,EAGV,YAAY,EAGb,MAAM,SAAS,CAAC;AAyBjB,eAAO,MAAM,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CAyM1C,CAAC"}
1
+ {"version":3,"file":"Toaster.d.ts","sourceRoot":"","sources":["../../../../src/toast/Toaster.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAS/B,OAAO,KAAK,EAGV,YAAY,EAGb,MAAM,SAAS,CAAC;AA0BjB,eAAO,MAAM,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CA8M1C,CAAC"}
@@ -0,0 +1,9 @@
1
+ export declare function useSingleOutletGuard(): void;
2
+ /**
3
+ * Test-only reset: both the counter and the one-shot latch are module state that
4
+ * outlives a render tree, so without this the first suite to mount two outlets
5
+ * would latch the warning and leave every later suite asserting against a guard
6
+ * that can no longer fire — passing whatever the implementation did.
7
+ */
8
+ export declare function resetSingleOutletGuardForTests(): void;
9
+ //# sourceMappingURL=use-single-outlet-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-single-outlet-guard.d.ts","sourceRoot":"","sources":["../../../../src/toast/use-single-outlet-guard.ts"],"names":[],"mappings":"AAmCA,wBAAgB,oBAAoB,IAAI,IAAI,CAyB3C;AAED;;;;;GAKG;AACH,wBAAgB,8BAA8B,IAAI,IAAI,CAGrD"}