@valbuild/react 0.150.1 → 0.150.3

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