@joycostudio/iris 0.0.0-stage → 0.0.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/react.cjs ADDED
@@ -0,0 +1,628 @@
1
+ "use strict";
2
+ "use client";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // packages/react/index.tsx
22
+ var react_exports = {};
23
+ __export(react_exports, {
24
+ IrisPlanImage: () => IrisPlanImage,
25
+ IrisProvider: () => IrisProvider,
26
+ useIrisDebugger: () => useIrisDebugger
27
+ });
28
+ module.exports = __toCommonJS(react_exports);
29
+ var import_react2 = require("react");
30
+
31
+ // packages/browser/index.ts
32
+ function inspectImageElement(image, manifest = {}, lcpElements = /* @__PURE__ */ new Set(), timing = null) {
33
+ const rect = image.getBoundingClientRect();
34
+ const renderedWidth = image.clientWidth || rect.width;
35
+ const renderedHeight = image.clientHeight || rect.height;
36
+ const id = image.dataset.iris || image.currentSrc || image.src;
37
+ const source = manifest[image.dataset.irisSrc || id] ?? manifest[id] ?? manifest[image.currentSrc] ?? manifest[image.src] ?? null;
38
+ const candidates = parseSrcSet(image.srcset, renderedWidth);
39
+ const selectedUrl = image.currentSrc || image.src;
40
+ const selectedWidth = selectedCandidateWidth(selectedUrl, candidates);
41
+ const dpr = window.devicePixelRatio || 1;
42
+ const idealWidth = Math.ceil(renderedWidth * dpr);
43
+ const declaredSlotWidth = resolveSizes(image.sizes);
44
+ const resource = resourceTiming(selectedUrl);
45
+ const diagnostics = diagnoseRuntime({
46
+ source,
47
+ renderedWidth,
48
+ renderedHeight,
49
+ visualWidth: rect.width,
50
+ visualHeight: rect.height,
51
+ idealWidth,
52
+ selectedWidth,
53
+ availableWidths: candidates.map((candidate) => candidate.width),
54
+ declaredSlotWidth,
55
+ objectFit: getComputedStyle(image).objectFit
56
+ });
57
+ return {
58
+ id,
59
+ source,
60
+ renderedWidth,
61
+ renderedHeight,
62
+ visualWidth: rect.width,
63
+ visualHeight: rect.height,
64
+ naturalWidth: image.naturalWidth,
65
+ dpr,
66
+ idealWidth,
67
+ selectedWidth,
68
+ selectedUrl,
69
+ availableWidths: candidates.map((candidate) => candidate.width),
70
+ declaredSizes: image.sizes,
71
+ declaredSlotWidth,
72
+ loading: image.loading,
73
+ objectFit: getComputedStyle(image).objectFit,
74
+ transferBytes: resource?.transferSize ?? null,
75
+ duration: resource?.duration ?? null,
76
+ timing,
77
+ isLcp: lcpElements.has(image),
78
+ diagnostics
79
+ };
80
+ }
81
+ function createIrisDebugger(options = {}) {
82
+ const reports = /* @__PURE__ */ new Map();
83
+ const lcpElements = /* @__PURE__ */ new Set();
84
+ let mutationObserver = null;
85
+ let resizeObserver = null;
86
+ let performanceObserver = null;
87
+ let frame = 0;
88
+ let tableTimer = null;
89
+ let lastTableSignature = "";
90
+ const pending = /* @__PURE__ */ new Set();
91
+ const timelines = /* @__PURE__ */ new WeakMap();
92
+ const selector = options.scope === "document" ? "img" : "img[data-iris]";
93
+ const measure = (image) => {
94
+ const report2 = inspectImageElement(
95
+ image,
96
+ options.manifest,
97
+ lcpElements,
98
+ timelines.get(image) ?? null
99
+ );
100
+ reports.set(image, report2);
101
+ scheduleTable();
102
+ };
103
+ const table = () => logReportTable(report(), options.console ?? "warnings");
104
+ const scheduleTable = () => {
105
+ if (options.console === "silent") return;
106
+ if (tableTimer) clearTimeout(tableTimer);
107
+ tableTimer = setTimeout(() => {
108
+ tableTimer = null;
109
+ const signature = reportSignature(report());
110
+ if (signature === lastTableSignature) return;
111
+ lastTableSignature = signature;
112
+ table();
113
+ }, 250);
114
+ };
115
+ const schedule = (image) => {
116
+ pending.add(image);
117
+ if (frame) return;
118
+ frame = requestAnimationFrame(() => {
119
+ frame = 0;
120
+ for (const pendingImage of pending) measure(pendingImage);
121
+ pending.clear();
122
+ });
123
+ };
124
+ const observe = (root = document) => {
125
+ resizeObserver = new ResizeObserver((entries) => {
126
+ for (const entry of entries) {
127
+ if (entry.target instanceof HTMLImageElement) schedule(entry.target);
128
+ }
129
+ });
130
+ const register = (image) => {
131
+ resizeObserver?.observe(image);
132
+ image.addEventListener("load", () => schedule(image), { once: true });
133
+ if (image.complete) schedule(image);
134
+ };
135
+ root.querySelectorAll(selector).forEach(register);
136
+ mutationObserver = new MutationObserver((records) => {
137
+ for (const record of records) {
138
+ for (const node of record.addedNodes) {
139
+ if (!(node instanceof Element)) continue;
140
+ if (node.matches(selector)) register(node);
141
+ node.querySelectorAll(selector).forEach(register);
142
+ }
143
+ }
144
+ });
145
+ mutationObserver.observe(root, { childList: true, subtree: true });
146
+ if (typeof PerformanceObserver !== "undefined") {
147
+ performanceObserver = new PerformanceObserver((list) => {
148
+ for (const entry of list.getEntries()) {
149
+ const element = entry.element;
150
+ if (element) {
151
+ lcpElements.add(element);
152
+ if (element instanceof HTMLImageElement) schedule(element);
153
+ }
154
+ }
155
+ });
156
+ try {
157
+ performanceObserver.observe({
158
+ type: "largest-contentful-paint",
159
+ buffered: true
160
+ });
161
+ } catch {
162
+ performanceObserver = null;
163
+ }
164
+ }
165
+ return api;
166
+ };
167
+ const disconnect = () => {
168
+ mutationObserver?.disconnect();
169
+ resizeObserver?.disconnect();
170
+ performanceObserver?.disconnect();
171
+ cancelAnimationFrame(frame);
172
+ if (tableTimer) clearTimeout(tableTimer);
173
+ tableTimer = null;
174
+ frame = 0;
175
+ pending.clear();
176
+ };
177
+ const report = () => [...reports.values()];
178
+ const startTimeline = (image) => {
179
+ timelines.set(image, {
180
+ blurMs: null,
181
+ previewMs: null,
182
+ finalMs: null,
183
+ startedAt: null,
184
+ previewAt: null,
185
+ finalAt: null
186
+ });
187
+ };
188
+ const markPreview = (image, preview, at = performance.now()) => {
189
+ const timing = timelines.get(image);
190
+ if (!timing) return;
191
+ timing.startedAt = resourceTiming(preview.currentSrc || preview.src)?.startTime ?? at;
192
+ timing.previewAt = at;
193
+ timing.blurMs = roundDuration(at - timing.startedAt);
194
+ schedule(image);
195
+ };
196
+ const markFinal = (image, at = performance.now()) => {
197
+ const timing = timelines.get(image);
198
+ if (!timing) return;
199
+ const finalStartedAt = resourceTiming(image.currentSrc || image.src)?.startTime ?? at;
200
+ timing.finalAt = at;
201
+ timing.finalMs = roundDuration(at - finalStartedAt);
202
+ if (timing.previewAt === null) {
203
+ timing.startedAt = finalStartedAt;
204
+ timing.blurMs = timing.finalMs;
205
+ } else timing.previewMs = roundDuration(at - timing.previewAt);
206
+ schedule(image);
207
+ };
208
+ const summary = () => ({
209
+ inspected: reports.size,
210
+ healthy: report().filter((item) => item.diagnostics.length === 0).length,
211
+ warnings: report().flatMap((item) => item.diagnostics).filter((item) => item.severity === "warning").length,
212
+ errors: report().flatMap((item) => item.diagnostics).filter((item) => item.severity === "error").length
213
+ });
214
+ const api = {
215
+ observe,
216
+ disconnect,
217
+ report,
218
+ table,
219
+ summary,
220
+ startTimeline,
221
+ markPreview,
222
+ markFinal
223
+ };
224
+ return api;
225
+ }
226
+ function roundDuration(value) {
227
+ return Math.round(value * 10) / 10;
228
+ }
229
+ function resourceTiming(url) {
230
+ const normalized = normalizeUrl(url);
231
+ const entries = performance.getEntriesByType(
232
+ "resource"
233
+ );
234
+ for (let index = entries.length - 1; index >= 0; index--) {
235
+ if (normalizeUrl(entries[index].name) === normalized) return entries[index];
236
+ }
237
+ return void 0;
238
+ }
239
+ function parseSrcSet(srcset, renderedWidth) {
240
+ return srcset.split(",").map((value) => value.trim().split(/\s+/)).map(([url, descriptor]) => ({
241
+ url,
242
+ width: descriptor?.endsWith("w") ? Number.parseInt(descriptor, 10) : descriptor?.endsWith("x") ? Math.round(Number.parseFloat(descriptor) * renderedWidth) : 0
243
+ })).filter((candidate) => candidate.url && candidate.width > 0);
244
+ }
245
+ function selectedCandidateWidth(selectedUrl, candidates) {
246
+ const selected = normalizeUrl(selectedUrl);
247
+ const candidate = candidates.find(
248
+ (item) => normalizeUrl(item.url) === selected
249
+ );
250
+ if (candidate) return candidate.width;
251
+ const queryWidth = new URL(
252
+ selectedUrl,
253
+ window.location.href
254
+ ).searchParams.get("w");
255
+ return queryWidth ? Number(queryWidth) : null;
256
+ }
257
+ function normalizeUrl(url) {
258
+ try {
259
+ return new URL(url, window.location.href).href;
260
+ } catch {
261
+ return url;
262
+ }
263
+ }
264
+ function diagnoseRuntime(input) {
265
+ const diagnostics = [];
266
+ if (input.visualWidth !== void 0 && input.renderedWidth > 0 && Math.abs(input.visualWidth - input.renderedWidth) / input.renderedWidth > 0.02) {
267
+ diagnostics.push(
268
+ runtimeDiagnostic(
269
+ "css-transform-affects-visual-size",
270
+ "info",
271
+ "high",
272
+ "A CSS transform changes the visual width; responsive selection still uses the untransformed layout slot.",
273
+ {
274
+ renderedWidth: input.renderedWidth,
275
+ visualWidth: input.visualWidth
276
+ }
277
+ )
278
+ );
279
+ }
280
+ if (input.declaredSlotWidth && input.renderedWidth > 0) {
281
+ const pixelDifference = input.declaredSlotWidth - input.renderedWidth;
282
+ const difference = pixelDifference / input.renderedWidth;
283
+ const isMaterialMismatch = Math.abs(pixelDifference) >= 32;
284
+ if (difference < -0.1 && isMaterialMismatch) {
285
+ diagnostics.push(
286
+ runtimeDiagnostic(
287
+ "sizes-understates-layout",
288
+ "warning",
289
+ "high",
290
+ "The active sizes slot is smaller than the rendered CSS width.",
291
+ {
292
+ declaredSlotWidth: input.declaredSlotWidth,
293
+ renderedWidth: input.renderedWidth,
294
+ pixelDifference,
295
+ difference
296
+ }
297
+ )
298
+ );
299
+ } else if (difference > 0.25 && isMaterialMismatch) {
300
+ diagnostics.push(
301
+ runtimeDiagnostic(
302
+ "sizes-overstates-layout",
303
+ "warning",
304
+ "high",
305
+ "The active sizes slot is substantially larger than the rendered CSS width.",
306
+ {
307
+ declaredSlotWidth: input.declaredSlotWidth,
308
+ renderedWidth: input.renderedWidth,
309
+ pixelDifference,
310
+ difference
311
+ }
312
+ )
313
+ );
314
+ }
315
+ }
316
+ if (input.source && input.source.width < input.idealWidth) {
317
+ diagnostics.push(
318
+ runtimeDiagnostic(
319
+ "source-too-small",
320
+ "error",
321
+ "high",
322
+ "The source is smaller than the rendered width \xD7 DPR target.",
323
+ {
324
+ sourceWidth: input.source.width,
325
+ idealWidth: input.idealWidth
326
+ }
327
+ )
328
+ );
329
+ }
330
+ if (input.selectedWidth && input.selectedWidth < input.idealWidth) {
331
+ const hasLarger = input.availableWidths.some(
332
+ (width) => width >= input.idealWidth
333
+ );
334
+ diagnostics.push(
335
+ runtimeDiagnostic(
336
+ hasLarger ? "browser-downselected" : "missing-useful-candidate",
337
+ hasLarger ? "info" : "warning",
338
+ "high",
339
+ hasLarger ? "The browser selected below the theoretical target although a larger candidate exists." : "No available candidate reaches the theoretical target.",
340
+ { selectedWidth: input.selectedWidth, idealWidth: input.idealWidth }
341
+ )
342
+ );
343
+ }
344
+ if (input.source && input.objectFit === "cover") {
345
+ const sourceRatio = input.source.width / input.source.height;
346
+ const renderedRatio = input.renderedWidth / input.renderedHeight;
347
+ if (Math.abs(sourceRatio - renderedRatio) / sourceRatio > 0.02) {
348
+ diagnostics.push(
349
+ runtimeDiagnostic(
350
+ "cropped-image",
351
+ "info",
352
+ "high",
353
+ "object-fit: cover crops part of the downloaded source.",
354
+ {
355
+ sourceAspectRatio: sourceRatio,
356
+ renderedAspectRatio: renderedRatio
357
+ }
358
+ )
359
+ );
360
+ }
361
+ }
362
+ return diagnostics;
363
+ }
364
+ function resolveSizes(sizes) {
365
+ if (!sizes || sizes.trim() === "auto") return null;
366
+ for (const rule of splitTopLevel(sizes)) {
367
+ const value = rule.trim();
368
+ let length = value;
369
+ if (value.startsWith("(")) {
370
+ const end = matchingParenthesis(value);
371
+ if (end < 0) continue;
372
+ if (!matchMedia(value.slice(0, end + 1)).matches) continue;
373
+ length = value.slice(end + 1).trim();
374
+ }
375
+ if (!length || length === "auto") continue;
376
+ const probe = document.createElement("div");
377
+ probe.style.cssText = `position:fixed;visibility:hidden;pointer-events:none;width:${length};height:0`;
378
+ document.body.append(probe);
379
+ const width = probe.getBoundingClientRect().width;
380
+ probe.remove();
381
+ return Number.isFinite(width) && width > 0 ? width : null;
382
+ }
383
+ return null;
384
+ }
385
+ function splitTopLevel(value) {
386
+ const output = [];
387
+ let depth = 0;
388
+ let start = 0;
389
+ for (let index = 0; index < value.length; index++) {
390
+ if (value[index] === "(") depth++;
391
+ if (value[index] === ")") depth--;
392
+ if (value[index] === "," && depth === 0) {
393
+ output.push(value.slice(start, index));
394
+ start = index + 1;
395
+ }
396
+ }
397
+ output.push(value.slice(start));
398
+ return output;
399
+ }
400
+ function matchingParenthesis(value) {
401
+ let depth = 0;
402
+ for (let index = 0; index < value.length; index++) {
403
+ if (value[index] === "(") depth++;
404
+ if (value[index] === ")" && --depth === 0) return index;
405
+ }
406
+ return -1;
407
+ }
408
+ function runtimeDiagnostic(code, severity, confidence, message, evidence) {
409
+ return { code, severity, confidence, message, evidence };
410
+ }
411
+ function logReportTable(reports, mode) {
412
+ if (mode === "silent") return;
413
+ const visible = reports.filter(
414
+ (report) => mode === "all" || report.diagnostics.some((diagnostic) => diagnostic.severity !== "info")
415
+ );
416
+ if (visible.length === 0) return;
417
+ console.table(
418
+ visible.map((report) => ({
419
+ asset: report.id,
420
+ stage: imageStage(report),
421
+ rendered: `${Math.round(report.renderedWidth)}\xD7${Math.round(report.renderedHeight)}`,
422
+ selected: report.selectedWidth,
423
+ ideal: report.idealWidth,
424
+ bytes: report.transferBytes,
425
+ blurMs: report.timing?.blurMs ?? null,
426
+ previewMs: report.timing?.previewMs ?? null,
427
+ finalMs: report.timing?.finalMs ?? null,
428
+ lazy: report.loading === "lazy",
429
+ lcp: report.isLcp,
430
+ status: diagnosticStatus(report),
431
+ diagnostics: report.diagnostics.map((diagnostic) => diagnostic.code).join(", ")
432
+ }))
433
+ );
434
+ }
435
+ function imageStage(report) {
436
+ if (report.timing?.finalAt !== null && report.timing?.finalAt !== void 0)
437
+ return "final";
438
+ if (report.timing?.previewAt) return "preview";
439
+ if (report.timing) return "blur";
440
+ return report.naturalWidth > 0 ? "final" : "loading";
441
+ }
442
+ function diagnosticStatus(report) {
443
+ if (report.diagnostics.some((item) => item.severity === "error"))
444
+ return "error";
445
+ if (report.diagnostics.some((item) => item.severity === "warning"))
446
+ return "warning";
447
+ if (report.diagnostics.some((item) => item.severity === "info")) return "info";
448
+ return "healthy";
449
+ }
450
+ function reportSignature(reports) {
451
+ return reports.map(
452
+ (report) => [
453
+ report.id,
454
+ imageStage(report),
455
+ report.selectedWidth,
456
+ report.transferBytes,
457
+ report.timing?.blurMs,
458
+ report.timing?.previewMs,
459
+ report.timing?.finalMs,
460
+ diagnosticStatus(report)
461
+ ].join(":")
462
+ ).join("|");
463
+ }
464
+
465
+ // packages/react/plan-image.tsx
466
+ var import_react = require("react");
467
+ var import_jsx_runtime = require("react/jsx-runtime");
468
+ function IrisPlanImage(props) {
469
+ const { plan } = props;
470
+ const key = JSON.stringify([
471
+ plan.src,
472
+ plan.final.url,
473
+ plan.preview?.url,
474
+ plan.placeholder?.url,
475
+ plan.placeholder?.blur
476
+ ]);
477
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlanImage, { ...props }, key);
478
+ }
479
+ function PlanImage({
480
+ plan,
481
+ alt,
482
+ className,
483
+ style,
484
+ onLoad,
485
+ onError,
486
+ loading = "lazy",
487
+ ...props
488
+ }) {
489
+ const [finalReady, setFinalReady] = (0, import_react.useState)(false);
490
+ const [previewReady, setPreviewReady] = (0, import_react.useState)(false);
491
+ const previewDecoding = (0, import_react.useRef)(false);
492
+ const previewShown = (0, import_react.useRef)(false);
493
+ const finalRef = (0, import_react.useCallback)((image) => {
494
+ if (image?.complete && image.naturalWidth) setFinalReady(true);
495
+ }, []);
496
+ const revealPreview = (0, import_react.useCallback)(async (image) => {
497
+ if (previewDecoding.current || previewShown.current) return;
498
+ previewDecoding.current = true;
499
+ try {
500
+ await image.decode?.();
501
+ } catch {
502
+ }
503
+ previewDecoding.current = false;
504
+ if (image.isConnected && image.naturalWidth && !previewShown.current) {
505
+ previewShown.current = true;
506
+ setPreviewReady(true);
507
+ }
508
+ }, []);
509
+ const previewRef = (0, import_react.useCallback)(
510
+ (image) => {
511
+ if (image?.complete && image.naturalWidth) void revealPreview(image);
512
+ },
513
+ [revealPreview]
514
+ );
515
+ const progressive = Boolean(plan.final.progressive);
516
+ const surface = {
517
+ position: "relative",
518
+ display: "block",
519
+ ...style
520
+ };
521
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className, "data-slot": "iris-image", style: surface, children: [
522
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
523
+ "img",
524
+ {
525
+ ...props,
526
+ ref: finalRef,
527
+ src: plan.final.url,
528
+ alt,
529
+ width: plan.final.width,
530
+ height: plan.final.height,
531
+ "data-slot": "iris-final",
532
+ "data-iris-src": plan.src,
533
+ loading,
534
+ style: {
535
+ position: "relative",
536
+ display: "block",
537
+ width: "100%",
538
+ height: "auto"
539
+ },
540
+ onLoad: (event) => {
541
+ setFinalReady(true);
542
+ onLoad?.(event);
543
+ },
544
+ onError
545
+ }
546
+ ),
547
+ !progressive && !finalReady && !previewReady && plan.placeholder && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
548
+ "span",
549
+ {
550
+ "aria-hidden": "true",
551
+ "data-slot": "iris-placeholder",
552
+ "data-stage": "blur",
553
+ style: {
554
+ position: "absolute",
555
+ inset: 0,
556
+ backgroundImage: `url(${JSON.stringify(plan.placeholder.url)})`,
557
+ backgroundSize: "100% 100%",
558
+ filter: `blur(${plan.placeholder.blur}px)`,
559
+ clipPath: "inset(0)",
560
+ pointerEvents: "none",
561
+ zIndex: 2
562
+ }
563
+ }
564
+ ),
565
+ !progressive && plan.preview && !finalReady && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
566
+ "img",
567
+ {
568
+ ref: previewRef,
569
+ src: plan.preview.url,
570
+ alt: "",
571
+ "aria-hidden": "true",
572
+ "data-slot": "iris-preview",
573
+ width: plan.final.width,
574
+ height: plan.final.height,
575
+ loading,
576
+ decoding: props.decoding,
577
+ crossOrigin: props.crossOrigin,
578
+ referrerPolicy: props.referrerPolicy,
579
+ style: {
580
+ position: "absolute",
581
+ inset: 0,
582
+ width: "100%",
583
+ height: "100%",
584
+ objectFit: "contain",
585
+ pointerEvents: "none",
586
+ zIndex: 1,
587
+ opacity: previewReady ? 1 : 0
588
+ },
589
+ onLoad: (event) => void revealPreview(event.currentTarget)
590
+ }
591
+ )
592
+ ] });
593
+ }
594
+
595
+ // packages/react/index.tsx
596
+ var import_jsx_runtime2 = require("react/jsx-runtime");
597
+ var IrisDebuggerContext = (0, import_react2.createContext)(null);
598
+ function IrisProvider({
599
+ children,
600
+ enabled = true,
601
+ manifest,
602
+ scope = "marked",
603
+ console: consoleMode = "warnings"
604
+ }) {
605
+ const [iris] = (0, import_react2.useState)(
606
+ () => typeof window === "undefined" ? null : createIrisDebugger({ manifest, scope, console: consoleMode })
607
+ );
608
+ (0, import_react2.useEffect)(() => {
609
+ if (!enabled || !iris) return;
610
+ iris.observe(document);
611
+ window.__IRIS__ = iris;
612
+ return () => {
613
+ iris.disconnect();
614
+ if (window.__IRIS__ === iris) delete window.__IRIS__;
615
+ };
616
+ }, [enabled, iris]);
617
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(IrisDebuggerContext.Provider, { value: enabled ? iris : null, children });
618
+ }
619
+ function useIrisDebugger() {
620
+ return (0, import_react2.useContext)(IrisDebuggerContext);
621
+ }
622
+ // Annotate the CommonJS export names for ESM import in node:
623
+ 0 && (module.exports = {
624
+ IrisPlanImage,
625
+ IrisProvider,
626
+ useIrisDebugger
627
+ });
628
+ //# sourceMappingURL=react.cjs.map