@valbuild/react 0.150.0 → 0.150.2

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 (43) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/analytics/dist/valbuild-react-analytics.browser.esm.js +2 -3
  3. package/analytics/dist/valbuild-react-analytics.cjs.dev.js +5 -6
  4. package/analytics/dist/valbuild-react-analytics.cjs.prod.js +5 -6
  5. package/analytics/dist/valbuild-react-analytics.esm.js +2 -3
  6. package/analytics/dist/valbuild-react-analytics.worker.esm.js +2 -3
  7. package/dist/{createForOfIteratorHelper-c0be3691.browser.esm.js → createForOfIteratorHelper-02fbe55e.worker.esm.js} +18 -2
  8. package/dist/{createForOfIteratorHelper-73fe9a1a.worker.esm.js → createForOfIteratorHelper-36020cf7.browser.esm.js} +18 -2
  9. package/dist/{createForOfIteratorHelper-8a871dc3.cjs.dev.js → createForOfIteratorHelper-522d616d.cjs.dev.js} +17 -0
  10. package/dist/{createForOfIteratorHelper-6f9dc6fa.cjs.prod.js → createForOfIteratorHelper-598a2f85.cjs.prod.js} +17 -0
  11. package/dist/{createForOfIteratorHelper-69c4a419.esm.js → createForOfIteratorHelper-8a8caaf6.esm.js} +18 -2
  12. package/dist/declarations/src/stega/index.d.ts +1 -0
  13. package/dist/declarations/src/stega/stegaLeaks.d.ts +40 -0
  14. package/dist/{raw-4870e29f.cjs.prod.js → raw-1617ec66.cjs.prod.js} +1 -1
  15. package/dist/{raw-7d65198e.worker.esm.js → raw-4d8c9599.worker.esm.js} +2 -2
  16. package/dist/{raw-400c4eba.cjs.dev.js → raw-a62618f3.cjs.dev.js} +1 -1
  17. package/dist/{raw-f1b35392.esm.js → raw-c9cc9df6.esm.js} +2 -2
  18. package/dist/{raw-27e587b6.browser.esm.js → raw-d34abe21.browser.esm.js} +2 -2
  19. package/dist/{slicedToArray-3d4c6640.esm.js → slicedToArray-106ca5f3.worker.esm.js} +1 -1
  20. package/dist/{slicedToArray-8f9b0d84.browser.esm.js → slicedToArray-70ecb9da.esm.js} +1 -1
  21. package/dist/{slicedToArray-8b1a55e5.worker.esm.js → slicedToArray-b66ff800.browser.esm.js} +1 -1
  22. package/internal/dist/valbuild-react-internal.browser.esm.js +3 -4
  23. package/internal/dist/valbuild-react-internal.cjs.dev.js +3 -4
  24. package/internal/dist/valbuild-react-internal.cjs.prod.js +3 -4
  25. package/internal/dist/valbuild-react-internal.esm.js +3 -4
  26. package/internal/dist/valbuild-react-internal.worker.esm.js +3 -4
  27. package/jsx-dev-runtime/dist/valbuild-react-jsx-dev-runtime.browser.esm.js +1 -1
  28. package/jsx-dev-runtime/dist/valbuild-react-jsx-dev-runtime.esm.js +1 -1
  29. package/jsx-dev-runtime/dist/valbuild-react-jsx-dev-runtime.worker.esm.js +1 -1
  30. package/jsx-runtime/dist/valbuild-react-jsx-runtime.browser.esm.js +1 -1
  31. package/jsx-runtime/dist/valbuild-react-jsx-runtime.esm.js +1 -1
  32. package/jsx-runtime/dist/valbuild-react-jsx-runtime.worker.esm.js +1 -1
  33. package/package.json +4 -4
  34. package/stega/dist/valbuild-react-stega.browser.esm.js +299 -22
  35. package/stega/dist/valbuild-react-stega.cjs.dev.js +300 -19
  36. package/stega/dist/valbuild-react-stega.cjs.prod.js +300 -19
  37. package/stega/dist/valbuild-react-stega.esm.js +299 -22
  38. package/stega/dist/valbuild-react-stega.worker.esm.js +299 -22
  39. package/dist/toConsumableArray-203e636a.cjs.prod.js +0 -21
  40. package/dist/toConsumableArray-5335b4b0.worker.esm.js +0 -19
  41. package/dist/toConsumableArray-991f680f.browser.esm.js +0 -19
  42. package/dist/toConsumableArray-c950e394.esm.js +0 -19
  43. package/dist/toConsumableArray-d7cf1601.cjs.dev.js +0 -21
@@ -1,18 +1,82 @@
1
- import { _ as _slicedToArray } from '../../dist/slicedToArray-8f9b0d84.browser.esm.js';
1
+ import { _ as _slicedToArray } from '../../dist/slicedToArray-b66ff800.browser.esm.js';
2
+ import { _ as _createForOfIteratorHelper, a as _toConsumableArray } from '../../dist/createForOfIteratorHelper-36020cf7.browser.esm.js';
2
3
  import { _ as _typeof } from '../../dist/typeof-9915e135.browser.esm.js';
3
- import { _ as _createForOfIteratorHelper } from '../../dist/createForOfIteratorHelper-c0be3691.browser.esm.js';
4
4
  import { Internal } from '@valbuild/core';
5
5
  import ReactJSXRuntime__default from 'react/jsx-runtime';
6
6
  import jsxRuntimeDev__default from 'react/jsx-dev-runtime';
7
7
  import React from 'react';
8
8
  import { VERCEL_STEGA_REGEX, vercelStegaSplit } from '@vercel/stega';
9
- import { s as stegaDecodeStrings } from '../../dist/raw-27e587b6.browser.esm.js';
10
- export { c as attrs, g as getModuleIds, r as raw, b as stegaClean, s as stegaDecodeStrings, a as stegaEncode } from '../../dist/raw-27e587b6.browser.esm.js';
9
+ import { s as stegaDecodeStrings } from '../../dist/raw-d34abe21.browser.esm.js';
10
+ export { c as attrs, g as getModuleIds, r as raw, b as stegaClean, s as stegaDecodeStrings, a as stegaEncode } from '../../dist/raw-d34abe21.browser.esm.js';
11
11
 
12
12
  var isIntrinsicElement = function isIntrinsicElement(type) {
13
13
  // TODO: think this is not correct, but good enough for now?
14
14
  return typeof type === "string";
15
15
  };
16
+ var FRAGMENT = Symbol["for"]("react.fragment");
17
+
18
+ /**
19
+ * The paths a fragment's text named, by the fragment element. A fragment has
20
+ * no DOM element to carry `data-val-path`, so its text is cleaned and its
21
+ * paths go to the element it is a child of: `<li>{a} <>— {b}</></li>` tags
22
+ * the `<li>` with both. Without this its text kept its edit tag, on the page.
23
+ */
24
+ var fragmentPaths = new WeakMap();
25
+
26
+ /** The paths of the fragments among `children`, when it holds any. */
27
+ function pathsOfFragmentChildren(children) {
28
+ var found = [];
29
+ // `.flat`: a list of keyed fragments from `.map()` is an array among them.
30
+ var _iterator = _createForOfIteratorHelper([children].flat(Infinity)),
31
+ _step;
32
+ try {
33
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
34
+ var child = _step.value;
35
+ if (_typeof(child) === "object" && child !== null) {
36
+ var paths = fragmentPaths.get(child);
37
+ if (paths) {
38
+ found.push.apply(found, _toConsumableArray(paths));
39
+ }
40
+ }
41
+ }
42
+ } catch (err) {
43
+ _iterator.e(err);
44
+ } finally {
45
+ _iterator.f();
46
+ }
47
+ return found;
48
+ }
49
+
50
+ /**
51
+ * Takes the edit tags out of a fragment's text, and returns the paths they
52
+ * named -- its own and its fragment children's -- for its parent to carry.
53
+ */
54
+ function cleanFragment(props) {
55
+ if (!props || _typeof(props) !== "object") {
56
+ return [];
57
+ }
58
+ var paths = pathsOfFragmentChildren(props.children);
59
+ var clean = function clean(value) {
60
+ if (typeof value === "string" && value.match(VERCEL_STEGA_REGEX)) {
61
+ var found = stegaDecodeStrings(value);
62
+ if (found) {
63
+ paths.push.apply(paths, _toConsumableArray(found));
64
+ return vercelStegaSplit(value).cleaned;
65
+ }
66
+ }
67
+ return value;
68
+ };
69
+ if (Array.isArray(props.children)) {
70
+ if (!Object.isFrozen(props.children)) {
71
+ props.children.forEach(function (child, index) {
72
+ props.children[index] = clean(child);
73
+ });
74
+ }
75
+ } else if (!Object.isFrozen(props)) {
76
+ props.children = clean(props.children);
77
+ }
78
+ return paths;
79
+ }
16
80
  var addValPathIfFound = function addValPathIfFound(type, props) {
17
81
  // TODO: increases performance. Unsure about the implications right now - seems to work. Remember to look at RSC and client (and pages/?).
18
82
  if (!IS_AUTO_TAG_JSX_ENABLED()) {
@@ -20,9 +84,9 @@ var addValPathIfFound = function addValPathIfFound(type, props) {
20
84
  }
21
85
  var valSources = new Set();
22
86
 
23
- // skip auto-tagging fragments since we can't add attributes to them
24
- if (type === Symbol["for"]("react.fragment")) {
25
- return;
87
+ // A fragment cannot carry attributes: what it held goes to its parent.
88
+ if (type === FRAGMENT) {
89
+ return cleanFragment(props);
26
90
  }
27
91
  function updateValSources(key, value, props, container) {
28
92
  if (!key) {
@@ -40,17 +104,17 @@ var addValPathIfFound = function addValPathIfFound(type, props) {
40
104
  // if this is an intrinsic element (a, div, etc.), add data attrs will be on the DOM element
41
105
  // always add to sources (intrinsic or not)
42
106
  // if this is not an intrinsic element, we pass the stega encoded value downwards until we hit an intrinsic element
43
- var _iterator = _createForOfIteratorHelper(valPaths),
44
- _step;
107
+ var _iterator2 = _createForOfIteratorHelper(valPaths),
108
+ _step2;
45
109
  try {
46
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
47
- var _valPath = _step.value;
110
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
111
+ var _valPath = _step2.value;
48
112
  valSources.add(_valPath);
49
113
  }
50
114
  } catch (err) {
51
- _iterator.e(err);
115
+ _iterator2.e(err);
52
116
  } finally {
53
- _iterator.f();
117
+ _iterator2.f();
54
118
  }
55
119
  if (isIntrinsicElement(type) && !Object.isFrozen(container)) {
56
120
  // clean values before adding them to the props
@@ -71,17 +135,17 @@ var addValPathIfFound = function addValPathIfFound(type, props) {
71
135
  var _cleanValue = vercelStegaSplit(value).cleaned;
72
136
  if (typeof key === "string" && !Array.isArray(container)) {
73
137
  container[key] = _cleanValue;
74
- var _iterator2 = _createForOfIteratorHelper(valPaths),
75
- _step2;
138
+ var _iterator3 = _createForOfIteratorHelper(valPaths),
139
+ _step3;
76
140
  try {
77
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
78
- var valPath = _step2.value;
141
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
142
+ var valPath = _step3.value;
79
143
  valSources.add(valPath);
80
144
  }
81
145
  } catch (err) {
82
- _iterator2.e(err);
146
+ _iterator3.e(err);
83
147
  } finally {
84
- _iterator2.f();
148
+ _iterator3.f();
85
149
  }
86
150
  }
87
151
  }
@@ -89,6 +153,20 @@ var addValPathIfFound = function addValPathIfFound(type, props) {
89
153
  }
90
154
  }
91
155
  if (props && _typeof(props) === "object") {
156
+ if (isIntrinsicElement(type)) {
157
+ var _iterator4 = _createForOfIteratorHelper(pathsOfFragmentChildren(props.children)),
158
+ _step4;
159
+ try {
160
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
161
+ var path = _step4.value;
162
+ valSources.add(path);
163
+ }
164
+ } catch (err) {
165
+ _iterator4.e(err);
166
+ } finally {
167
+ _iterator4.f();
168
+ }
169
+ }
92
170
  for (var _i = 0, _Object$entries = Object.entries(props); _i < _Object$entries.length; _i++) {
93
171
  var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
94
172
  key = _Object$entries$_i[0],
@@ -128,11 +206,15 @@ var addValPathIfFound = function addValPathIfFound(type, props) {
128
206
  function WrapJsx(jsx) {
129
207
  if (typeof jsx !== "function") return jsx;
130
208
  return function (type, props) {
131
- addValPathIfFound(type, props);
209
+ var fragmentPathsFound = addValPathIfFound(type, props);
132
210
  for (var _len = arguments.length, rest = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
133
211
  rest[_key - 2] = arguments[_key];
134
212
  }
135
- return jsx.call.apply(jsx, [jsx, type, props].concat(rest));
213
+ var element = jsx.call.apply(jsx, [jsx, type, props].concat(rest));
214
+ if (fragmentPathsFound && fragmentPathsFound.length > 0 && _typeof(element) === "object" && element !== null) {
215
+ fragmentPaths.set(element, fragmentPathsFound);
216
+ }
217
+ return element;
136
218
  };
137
219
  }
138
220
  function autoTagJSX() {
@@ -161,6 +243,201 @@ function autoTagJSX() {
161
243
  JsxPro.jsxDEV && (/**/JsxPro.jsxDEV = WrapJsx(JsxPro.jsxDEV));
162
244
  }
163
245
 
246
+ /**
247
+ * Edit tags that left React: content whose invisible characters reached the
248
+ * page somewhere `autoTagJSX` could not take them out -- JSON-LD written with
249
+ * `dangerouslySetInnerHTML`, a `<title>` or `<meta>` set from a string,
250
+ * `encodeURIComponent(title)` in a share link. Every visitor of a site with
251
+ * content analytics on gets them after hydration, and so does any crawler
252
+ * that runs the page. See `docs/plans/analytics.md`, "The leak warning".
253
+ *
254
+ * Anything that went through `autoTagJSX` is clean -- its tags were moved to
255
+ * `data-val-path` -- so a tag found anywhere in the document is a leak.
256
+ */
257
+
258
+ /** The attributes that tell an element apart from its siblings, in the order tried. */
259
+ var NAMING_ATTRIBUTES = ["type", "name", "property", "rel", "id"];
260
+ function describeElement(element) {
261
+ var tag = element.tagName.toLowerCase();
262
+ var _iterator = _createForOfIteratorHelper(NAMING_ATTRIBUTES),
263
+ _step;
264
+ try {
265
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
266
+ var name = _step.value;
267
+ var value = element.getAttribute(name);
268
+ if (value !== null && value !== "" && !hasTag(value)) {
269
+ return "<".concat(tag, " ").concat(name, "=\"").concat(value, "\">");
270
+ }
271
+ }
272
+ } catch (err) {
273
+ _iterator.e(err);
274
+ } finally {
275
+ _iterator.f();
276
+ }
277
+ return "<".concat(tag, ">");
278
+ }
279
+ function hasTag(value) {
280
+ return value.match(VERCEL_STEGA_REGEX) !== null;
281
+ }
282
+
283
+ /**
284
+ * The paths tagged in `value`. A URL can carry a tag percent-encoded --
285
+ * `encodeURIComponent` keeps the invisible characters, as `%E2%80%8B` -- so
286
+ * that is decoded and looked in as well.
287
+ */
288
+ function taggedPaths(value) {
289
+ if (hasTag(value)) {
290
+ var _stegaDecodeStrings;
291
+ return (_stegaDecodeStrings = stegaDecodeStrings(value)) !== null && _stegaDecodeStrings !== void 0 ? _stegaDecodeStrings : [];
292
+ }
293
+ if (value.includes("%")) {
294
+ var decoded;
295
+ try {
296
+ decoded = decodeURIComponent(value);
297
+ } catch (_unused) {
298
+ return [];
299
+ }
300
+ if (decoded !== value && hasTag(decoded)) {
301
+ var _stegaDecodeStrings2;
302
+ return (_stegaDecodeStrings2 = stegaDecodeStrings(decoded)) !== null && _stegaDecodeStrings2 !== void 0 ? _stegaDecodeStrings2 : [];
303
+ }
304
+ }
305
+ return [];
306
+ }
307
+
308
+ /** Every edit tag in `root`'s document, in document order, each path and place once. */
309
+ function findStegaLeaks(root) {
310
+ var leaks = [];
311
+ var seen = new Set();
312
+ var add = function add(paths, where) {
313
+ var _iterator2 = _createForOfIteratorHelper(paths),
314
+ _step2;
315
+ try {
316
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
317
+ var path = _step2.value;
318
+ var key = "".concat(path, "\0").concat(where);
319
+ if (!seen.has(key)) {
320
+ seen.add(key);
321
+ leaks.push({
322
+ path: path,
323
+ where: where
324
+ });
325
+ }
326
+ }
327
+ } catch (err) {
328
+ _iterator2.e(err);
329
+ } finally {
330
+ _iterator2.f();
331
+ }
332
+ };
333
+ var top = root.documentElement;
334
+ if (!top) {
335
+ return leaks;
336
+ }
337
+ var walker = root.createTreeWalker(top, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT);
338
+ for (var node = walker.currentNode; node;) {
339
+ if (isElement(node)) {
340
+ for (var _i = 0, _Array$from = Array.from(node.attributes); _i < _Array$from.length; _i++) {
341
+ var attribute = _Array$from[_i];
342
+ if (attribute.name === "data-val-path") continue;
343
+ add(taggedPaths(attribute.value), "the ".concat(attribute.name, " attribute of ").concat(describeElement(node)));
344
+ }
345
+ } else if (node.nodeType === Node.TEXT_NODE) {
346
+ var parent = node.parentElement;
347
+ var text = node.nodeValue;
348
+ if (parent && text) {
349
+ add(taggedPaths(text), isOwnText(parent) ? describeElement(parent) : "text in ".concat(describeElement(parent)));
350
+ }
351
+ }
352
+ node = walker.nextNode();
353
+ }
354
+ return leaks;
355
+ }
356
+ function isElement(node) {
357
+ return node.nodeType === Node.ELEMENT_NODE;
358
+ }
359
+
360
+ /** Elements whose text is a value of its own, rather than text on the page. */
361
+ function isOwnText(element) {
362
+ var tag = element.tagName.toLowerCase();
363
+ return tag === "script" || tag === "style" || tag === "title";
364
+ }
365
+
366
+ /**
367
+ * Whether the page runs React's development build: the one signal of
368
+ * "development" that holds in every bundler. `process.env.NODE_ENV` does not
369
+ * -- TanStack Start's dev server hands the browser a `process.env` without
370
+ * it -- but the bundler resolved React's own copy of that check to pick a
371
+ * build, and the production build's `react/jsx-dev-runtime` has no `jsxDEV`.
372
+ */
373
+ function isReactDevelopmentBuild() {
374
+ var runtime = jsxRuntimeDev__default;
375
+ return typeof runtime.jsxDEV === "function";
376
+ }
377
+ function stegaLeakMessage(leak) {
378
+ return "Content from ".concat(leak.path, " reached ").concat(leak.where, " with its edit tag still in it. Wrap the value in val.raw() where it leaves React.");
379
+ }
380
+
381
+ /**
382
+ * Looks for leaks once the page has settled after a change -- after load,
383
+ * after the render that adds edit tags, after every route change -- and
384
+ * reports each path and place the first time it is found. For development:
385
+ * walking the whole document is not something to do to a visitor.
386
+ *
387
+ * Returns what stops it.
388
+ */
389
+ function watchStegaLeaks(report) {
390
+ var _options$root, _options$settleMs;
391
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
392
+ var root = (_options$root = options.root) !== null && _options$root !== void 0 ? _options$root : document;
393
+ var settleMs = (_options$settleMs = options.settleMs) !== null && _options$settleMs !== void 0 ? _options$settleMs : 1000;
394
+ var reported = new Set();
395
+ var timer = null;
396
+ var scan = function scan() {
397
+ timer = null;
398
+ var _iterator3 = _createForOfIteratorHelper(findStegaLeaks(root)),
399
+ _step3;
400
+ try {
401
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
402
+ var leak = _step3.value;
403
+ var key = "".concat(leak.path, "\0").concat(leak.where);
404
+ if (!reported.has(key)) {
405
+ reported.add(key);
406
+ report(leak);
407
+ }
408
+ }
409
+ } catch (err) {
410
+ _iterator3.e(err);
411
+ } finally {
412
+ _iterator3.f();
413
+ }
414
+ };
415
+ var schedule = function schedule() {
416
+ // At most one scan per `settleMs`, however busy the page: the first
417
+ // change starts the wait, later ones ride along with it.
418
+ if (timer === null) {
419
+ timer = setTimeout(scan, settleMs);
420
+ }
421
+ };
422
+ var observer = new MutationObserver(schedule);
423
+ if (root.documentElement) {
424
+ observer.observe(root.documentElement, {
425
+ subtree: true,
426
+ childList: true,
427
+ attributes: true,
428
+ characterData: true
429
+ });
430
+ }
431
+ schedule();
432
+ return function () {
433
+ observer.disconnect();
434
+ if (timer !== null) {
435
+ clearTimeout(timer);
436
+ timer = null;
437
+ }
438
+ };
439
+ }
440
+
164
441
  // NOTE: the exports of this file needs to be kept in sync with the stubs in loadValModules
165
442
  var isRSC = false;
166
443
  var autoTagJSXEnabled = false;
@@ -177,4 +454,4 @@ function SET_RSC(enabled) {
177
454
  isRSC = enabled;
178
455
  }
179
456
 
180
- export { IS_AUTO_TAG_JSX_ENABLED, IS_RSC, SET_AUTO_TAG_JSX_ENABLED, SET_RSC, autoTagJSX };
457
+ export { IS_AUTO_TAG_JSX_ENABLED, IS_RSC, SET_AUTO_TAG_JSX_ENABLED, SET_RSC, autoTagJSX, findStegaLeaks, isReactDevelopmentBuild, stegaLeakMessage, watchStegaLeaks };