@pro-laico/payload-dev-tools 0.1.0 → 0.2.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 (45) hide show
  1. package/dist/components/DevToolbar.d.ts.map +1 -1
  2. package/dist/components/DevToolbar.js +7 -3
  3. package/dist/components/DevToolbar.js.map +1 -1
  4. package/dist/components/DevToolbarClient.d.ts.map +1 -1
  5. package/dist/components/DevToolbarClient.js +91 -1
  6. package/dist/components/DevToolbarClient.js.map +1 -1
  7. package/dist/components/styles.d.ts +1 -1
  8. package/dist/components/styles.d.ts.map +1 -1
  9. package/dist/components/styles.js +13 -0
  10. package/dist/components/styles.js.map +1 -1
  11. package/dist/endpoints/activateIconSet.d.ts.map +1 -1
  12. package/dist/endpoints/activateIconSet.js +2 -0
  13. package/dist/endpoints/activateIconSet.js.map +1 -1
  14. package/dist/endpoints/draft.d.ts +12 -0
  15. package/dist/endpoints/draft.d.ts.map +1 -0
  16. package/dist/endpoints/draft.js +66 -0
  17. package/dist/endpoints/draft.js.map +1 -0
  18. package/dist/lib/snapshot.d.ts +15 -0
  19. package/dist/lib/snapshot.d.ts.map +1 -1
  20. package/dist/lib/snapshot.js +16 -1
  21. package/dist/lib/snapshot.js.map +1 -1
  22. package/dist/next/client.d.ts +5 -0
  23. package/dist/next/client.d.ts.map +1 -1
  24. package/dist/next/client.js +80 -0
  25. package/dist/next/client.js.map +1 -1
  26. package/dist/next/createDevPage.d.ts.map +1 -1
  27. package/dist/next/createDevPage.js +10 -1
  28. package/dist/next/createDevPage.js.map +1 -1
  29. package/dist/next/pageStyles.d.ts +1 -1
  30. package/dist/next/pageStyles.d.ts.map +1 -1
  31. package/dist/next/pageStyles.js +17 -0
  32. package/dist/next/pageStyles.js.map +1 -1
  33. package/dist/next/revalidatePanel.d.ts +81 -0
  34. package/dist/next/revalidatePanel.d.ts.map +1 -0
  35. package/dist/next/revalidatePanel.js +1094 -0
  36. package/dist/next/revalidatePanel.js.map +1 -0
  37. package/dist/next/views.d.ts +6 -0
  38. package/dist/next/views.d.ts.map +1 -1
  39. package/dist/next/views.js +16 -0
  40. package/dist/next/views.js.map +1 -1
  41. package/dist/plugin.d.ts +1 -0
  42. package/dist/plugin.d.ts.map +1 -1
  43. package/dist/plugin.js +3 -0
  44. package/dist/plugin.js.map +1 -1
  45. package/package.json +5 -1
@@ -0,0 +1,1094 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import "@xyflow/react/dist/style.css";
4
+ import dagre from "@dagrejs/dagre";
5
+ import { Background, Handle, Position, ReactFlow } from "@xyflow/react";
6
+ import { useMemo, useState } from "react";
7
+ import { BustTagCard } from "./client.js";
8
+ const RICH_TEXT_NODE = '*';
9
+ const clock = (iso)=>iso.slice(11, 19);
10
+ const label = (node)=>node === RICH_TEXT_NODE ? 'richText embeds' : node;
11
+ const readName = (read)=>read.label ?? `${read.kind}:${read.collection ?? read.global ?? '?'}${read.as !== undefined ? `:${read.as}` : ''}${read.list ? `:${read.list}` : ''}${read.draft ? ' (draft)' : ''}`;
12
+ /** Which node a read belongs to, and which collection a tag points at (prefix-aware). */ const readNode = (read)=>read.collection ?? (read.global ? `global:${read.global}` : null);
13
+ const tagCollection = (tag, prefix)=>{
14
+ const bare = prefix && tag.startsWith(`${prefix}:`) ? tag.slice(prefix.length + 1) : tag;
15
+ return bare.split(':')[0] ?? bare;
16
+ };
17
+ /** Observed bake-ins as `edited -> renderer` pairs — colors the schema edges by real usage. */ const bakedPairs = (reads, prefix)=>{
18
+ const pairs = new Set();
19
+ for (const read of reads){
20
+ const renderer = readNode(read);
21
+ if (!renderer) continue;
22
+ for (const embed of read.bakedIn)pairs.add(`${tagCollection(embed.tag, prefix)}->${renderer}`);
23
+ }
24
+ return pairs;
25
+ };
26
+ function GraphNode({ data }) {
27
+ return /*#__PURE__*/ _jsxs("div", {
28
+ className: `pdtp-flow-node ${data.selected ? 'pdtp-flow-node-active' : ''}`,
29
+ children: [
30
+ /*#__PURE__*/ _jsx(Handle, {
31
+ type: "target",
32
+ position: Position.Left,
33
+ className: "pdtp-flow-handle"
34
+ }),
35
+ /*#__PURE__*/ _jsx("span", {
36
+ className: "pdtp-mono",
37
+ children: data.title
38
+ }),
39
+ /*#__PURE__*/ _jsxs("span", {
40
+ className: "pdtp-flow-badges",
41
+ children: [
42
+ data.kind !== 'collection' ? /*#__PURE__*/ _jsx("em", {
43
+ children: data.kind === 'global' ? 'global' : 'per-doc'
44
+ }) : null,
45
+ data.reads ? /*#__PURE__*/ _jsxs("em", {
46
+ children: [
47
+ data.reads,
48
+ " reads"
49
+ ]
50
+ }) : null,
51
+ data.baked ? /*#__PURE__*/ _jsxs("em", {
52
+ className: "pdtp-flow-warn",
53
+ children: [
54
+ data.baked,
55
+ " baked ⚠"
56
+ ]
57
+ }) : null
58
+ ]
59
+ }),
60
+ /*#__PURE__*/ _jsx(Handle, {
61
+ type: "source",
62
+ position: Position.Right,
63
+ className: "pdtp-flow-handle"
64
+ })
65
+ ]
66
+ });
67
+ }
68
+ const nodeTypes = {
69
+ pdtp: GraphNode
70
+ };
71
+ /** Left→right layered layout: sources are the things you EDIT, targets the surfaces that
72
+ * go stale. Sized by label so dagre spaces long slugs correctly. */ const layout = (nodes, edges)=>{
73
+ const g = new dagre.graphlib.Graph();
74
+ g.setGraph({
75
+ rankdir: 'LR',
76
+ nodesep: 24,
77
+ ranksep: 120
78
+ });
79
+ g.setDefaultEdgeLabel(()=>({}));
80
+ for (const node of nodes)g.setNode(node.id, {
81
+ width: 30 + node.data.title.length * 8 + (node.data.reads ? 60 : 0),
82
+ height: 40
83
+ });
84
+ for (const edge of edges)g.setEdge(edge.source, edge.target);
85
+ dagre.layout(g);
86
+ return nodes.map((node)=>{
87
+ const pos = g.node(node.id);
88
+ return {
89
+ ...node,
90
+ position: {
91
+ x: pos.x - pos.width / 2,
92
+ y: pos.y - pos.height / 2
93
+ }
94
+ };
95
+ });
96
+ };
97
+ function ExploreTab({ data, selected, onSelect }) {
98
+ const { nodes, edges } = useMemo(()=>{
99
+ const baked = bakedPairs(data.reads, data.prefix);
100
+ const readCounts = new Map();
101
+ const bakedCounts = new Map();
102
+ for (const read of data.reads){
103
+ const node = readNode(read);
104
+ if (!node) continue;
105
+ readCounts.set(node, (readCounts.get(node) ?? 0) + 1);
106
+ if (read.bakedIn.length) bakedCounts.set(node, (bakedCounts.get(node) ?? 0) + 1);
107
+ }
108
+ const ids = [
109
+ ...data.graph.collections,
110
+ ...data.graph.globals.map((g)=>`global:${g}`)
111
+ ];
112
+ const hasRichText = data.graph.edges.some((e)=>e.to === RICH_TEXT_NODE);
113
+ const allIds = hasRichText ? [
114
+ ...ids,
115
+ RICH_TEXT_NODE
116
+ ] : ids;
117
+ const flowNodes = allIds.map((id)=>({
118
+ id,
119
+ type: 'pdtp',
120
+ position: {
121
+ x: 0,
122
+ y: 0
123
+ },
124
+ data: {
125
+ title: label(id),
126
+ kind: id === RICH_TEXT_NODE ? 'richText' : id.startsWith('global:') ? 'global' : 'collection',
127
+ reads: readCounts.get(id) ?? 0,
128
+ baked: bakedCounts.get(id) ?? 0,
129
+ selected: selected === id
130
+ }
131
+ }));
132
+ // Direction of staleness: editing `edge.to` makes `edge.from` stale — source is the
133
+ // edited side so the graph reads left (cause) → right (effect).
134
+ const flowEdges = data.graph.edges.map((edge)=>{
135
+ const isBaked = baked.has(`${edge.to}->${edge.from}`);
136
+ const active = selected !== null && (edge.to === selected || edge.from === selected);
137
+ const dimmed = selected !== null && !active;
138
+ return {
139
+ id: `${edge.from}|${edge.via}|${edge.to}`,
140
+ source: edge.to,
141
+ target: edge.from,
142
+ animated: isBaked && !dimmed,
143
+ label: active ? `${edge.via} (${edge.kind})` : undefined,
144
+ style: {
145
+ stroke: isBaked ? 'var(--pdtp-warn)' : 'oklch(1 0 0 / 30%)',
146
+ strokeWidth: active ? 2 : 1.25,
147
+ strokeDasharray: edge.kind === 'richText' || edge.to === RICH_TEXT_NODE ? '5 5' : undefined,
148
+ opacity: dimmed ? 0.15 : 1
149
+ },
150
+ labelStyle: {
151
+ fill: 'var(--pdtp-fg)',
152
+ fontSize: 11
153
+ },
154
+ labelBgStyle: {
155
+ fill: 'var(--pdtp-card)'
156
+ }
157
+ };
158
+ });
159
+ return {
160
+ nodes: layout(flowNodes, flowEdges),
161
+ edges: flowEdges
162
+ };
163
+ }, [
164
+ data,
165
+ selected
166
+ ]);
167
+ return /*#__PURE__*/ _jsxs(_Fragment, {
168
+ children: [
169
+ /*#__PURE__*/ _jsx("div", {
170
+ className: "pdtp-flow",
171
+ children: /*#__PURE__*/ _jsx(ReactFlow, {
172
+ nodes: nodes,
173
+ edges: edges,
174
+ nodeTypes: nodeTypes,
175
+ colorMode: "dark",
176
+ fitView: true,
177
+ minZoom: 0.3,
178
+ nodesDraggable: true,
179
+ nodesConnectable: false,
180
+ proOptions: {
181
+ hideAttribution: true
182
+ },
183
+ onNodeClick: (_, node)=>onSelect(selected === node.id ? null : node.id),
184
+ onPaneClick: ()=>onSelect(null),
185
+ children: /*#__PURE__*/ _jsx(Background, {
186
+ gap: 24
187
+ })
188
+ })
189
+ }),
190
+ /*#__PURE__*/ _jsxs("p", {
191
+ className: "pdtp-note",
192
+ style: {
193
+ marginTop: 8
194
+ },
195
+ children: [
196
+ "Left → right: editing a node makes its right-hand neighbors stale. ",
197
+ /*#__PURE__*/ _jsx("span", {
198
+ className: "pdtp-warn",
199
+ children: "Amber animated"
200
+ }),
201
+ " edges are OBSERVED bake-ins (populated content — the anti-pattern); grey edges are id-references or not yet observed; dashed = richText (per-doc). Click a node for its full story."
202
+ ]
203
+ }),
204
+ selected ? /*#__PURE__*/ _jsx(NodeDetail, {
205
+ node: selected,
206
+ data: data
207
+ }) : null
208
+ ]
209
+ });
210
+ }
211
+ /**
212
+ * The focused, evidence-based answer for one node — three layers, never conflated:
213
+ *
214
+ * 1. **What a write here busts** — tags, by event kind. Facts from the decision table.
215
+ * 2. **What observably purges right now** — the cached entries that actually carry those
216
+ * tags. This is the truth of "what happens if I edit/create a doc here today".
217
+ * 3. **Potential coupling** — schema edges. A reference alone never propagates a purge
218
+ * (ids are stable); an edge only becomes real when a getter is OBSERVED baking the
219
+ * content in. Each edge is labeled accordingly.
220
+ */ function NodeDetail({ node, data }) {
221
+ const { graph, rules, reads, prefix } = data;
222
+ const p = prefix ? `${prefix}:` : '';
223
+ const isGlobal = node.startsWith('global:');
224
+ const slug = isGlobal ? node.slice('global:'.length) : node;
225
+ const settings = data.settings[slug];
226
+ const baked = bakedPairs(reads, prefix);
227
+ const makesStale = graph.edges.filter((e)=>e.to === node);
228
+ const staleWhen = graph.edges.filter((e)=>e.from === node);
229
+ const ownRules = rules.filter((r)=>r.on === slug);
230
+ const scopes = Object.entries(settings?.lists ?? {});
231
+ // Observed: entries carrying this collection's DOC tags (own entries + bake-ins)…
232
+ const docTagCarriers = new Map();
233
+ // …and entries carrying its LIST tags (what membership events actually purge).
234
+ const listCarriers = reads.filter((read)=>read.kind === 'ids' && read.collection === slug);
235
+ if (!isGlobal && node !== RICH_TEXT_NODE) {
236
+ for (const read of reads){
237
+ for (const tag of [
238
+ ...read.staticTags,
239
+ ...read.depTags
240
+ ]){
241
+ if (!tag.startsWith(`${p}${slug}:`) || tag.startsWith(`${p}${slug}:list:`) || tag.endsWith(':draft')) continue;
242
+ const isBaked = read.bakedIn.some((b)=>b.tag === tag);
243
+ docTagCarriers.set(tag, [
244
+ ...docTagCarriers.get(tag) ?? [],
245
+ {
246
+ name: readName(read),
247
+ baked: isBaked
248
+ }
249
+ ]);
250
+ }
251
+ }
252
+ }
253
+ if (node === RICH_TEXT_NODE) {
254
+ return /*#__PURE__*/ _jsxs("div", {
255
+ className: "pdtp-card",
256
+ style: {
257
+ marginTop: 16
258
+ },
259
+ children: [
260
+ /*#__PURE__*/ _jsxs("h2", {
261
+ children: [
262
+ "richText embeds ",
263
+ /*#__PURE__*/ _jsx("span", {
264
+ className: "pdtp-kind",
265
+ children: "per-doc"
266
+ })
267
+ ]
268
+ }),
269
+ /*#__PURE__*/ _jsx("p", {
270
+ className: "pdtp-note",
271
+ style: {
272
+ margin: 0
273
+ },
274
+ children: "Upload/relationship nodes inside rich text resolve per document at read time. Populated nodes bake content in and tag the entry (shown as bake-ins on the owning read); id-only nodes are references — render them through id-keyed getters."
275
+ })
276
+ ]
277
+ });
278
+ }
279
+ // The getters the CODE declares for this node (live source scan), matched against
280
+ // runtime observation so each shows whether it has materialized yet.
281
+ const helperKind = {
282
+ cacheDoc: 'doc',
283
+ cacheIds: 'ids',
284
+ cacheGlobal: 'global'
285
+ };
286
+ const nodeGetters = data.getters.filter((g)=>g.slug === slug && (isGlobal ? g.helper === 'cacheGlobal' : g.helper !== 'cacheGlobal')).map((g)=>{
287
+ const observed = reads.find((read)=>(read.collection ?? read.global) === slug && read.kind === helperKind[g.helper] && (read.list ?? undefined) === (g.list ?? undefined) && (g.label === undefined || read.label === g.label));
288
+ return {
289
+ ...g,
290
+ observed
291
+ };
292
+ });
293
+ const vocabulary = isGlobal ? [
294
+ `${p}global:${slug}`,
295
+ `${p}global:${slug}:draft`
296
+ ] : [
297
+ `${p}${slug}:{id}`,
298
+ ...settings?.idField ? [
299
+ `${p}${slug}:{${settings.idField}}`
300
+ ] : [],
301
+ `${p}${slug}`,
302
+ ...scopes.map(([scope])=>`${p}${slug}:list:${scope}`),
303
+ '+ :draft variants'
304
+ ];
305
+ return /*#__PURE__*/ _jsxs("div", {
306
+ className: "pdtp-card",
307
+ style: {
308
+ marginTop: 16
309
+ },
310
+ children: [
311
+ /*#__PURE__*/ _jsxs("h2", {
312
+ children: [
313
+ label(node),
314
+ " ",
315
+ /*#__PURE__*/ _jsx("span", {
316
+ className: "pdtp-kind",
317
+ children: isGlobal ? 'global' : 'collection'
318
+ })
319
+ ]
320
+ }),
321
+ /*#__PURE__*/ _jsxs("p", {
322
+ className: "pdtp-note",
323
+ style: {
324
+ margin: '0 0 12px'
325
+ },
326
+ children: [
327
+ "Tags:",
328
+ ' ',
329
+ vocabulary.map((tag)=>/*#__PURE__*/ _jsx("span", {
330
+ className: "pdtp-code",
331
+ style: {
332
+ marginRight: 6
333
+ },
334
+ children: tag
335
+ }, tag))
336
+ ]
337
+ }),
338
+ /*#__PURE__*/ _jsx("h3", {
339
+ className: "pdtp-rev-subhead",
340
+ children: "A write here busts…"
341
+ }),
342
+ /*#__PURE__*/ _jsx("ul", {
343
+ className: "pdtp-rev-list",
344
+ children: isGlobal ? /*#__PURE__*/ _jsxs("li", {
345
+ children: [
346
+ "any save → ",
347
+ /*#__PURE__*/ _jsx("span", {
348
+ className: "pdtp-code",
349
+ children: `${p}global:${slug}`
350
+ })
351
+ ]
352
+ }) : /*#__PURE__*/ _jsxs(_Fragment, {
353
+ children: [
354
+ /*#__PURE__*/ _jsxs("li", {
355
+ children: [
356
+ "editing a doc → ",
357
+ /*#__PURE__*/ _jsx("span", {
358
+ className: "pdtp-code",
359
+ children: `${p}${slug}:{id}`
360
+ }),
361
+ settings?.idField ? /*#__PURE__*/ _jsxs(_Fragment, {
362
+ children: [
363
+ ' ',
364
+ "+ ",
365
+ /*#__PURE__*/ _jsx("span", {
366
+ className: "pdtp-code",
367
+ children: `${p}${slug}:{${settings.idField}}`
368
+ })
369
+ ]
370
+ }) : null,
371
+ ' ',
372
+ /*#__PURE__*/ _jsx("span", {
373
+ className: "pdtp-muted",
374
+ children: "— that one doc, nothing else"
375
+ })
376
+ ]
377
+ }),
378
+ /*#__PURE__*/ _jsxs("li", {
379
+ children: [
380
+ "create / delete / publish / unpublish → also ",
381
+ /*#__PURE__*/ _jsx("span", {
382
+ className: "pdtp-code",
383
+ children: `${p}${slug}`
384
+ }),
385
+ scopes.map(([scope])=>/*#__PURE__*/ _jsxs("span", {
386
+ children: [
387
+ ' ',
388
+ "+ ",
389
+ /*#__PURE__*/ _jsx("span", {
390
+ className: "pdtp-code",
391
+ children: `${p}${slug}:list:${scope}`
392
+ })
393
+ ]
394
+ }, scope)),
395
+ ' ',
396
+ /*#__PURE__*/ _jsx("span", {
397
+ className: "pdtp-muted",
398
+ children: "— membership changed"
399
+ })
400
+ ]
401
+ }),
402
+ scopes.map(([scope, fields])=>/*#__PURE__*/ _jsxs("li", {
403
+ children: [
404
+ "editing ",
405
+ /*#__PURE__*/ _jsx("span", {
406
+ className: "pdtp-mono",
407
+ children: fields.join(' / ')
408
+ }),
409
+ " → also",
410
+ ' ',
411
+ /*#__PURE__*/ _jsx("span", {
412
+ className: "pdtp-code",
413
+ children: `${p}${slug}:list:${scope}`
414
+ }),
415
+ " ",
416
+ /*#__PURE__*/ _jsx("span", {
417
+ className: "pdtp-muted",
418
+ children: "— order/filter changed"
419
+ })
420
+ ]
421
+ }, scope)),
422
+ settings?.extraTags.length ? /*#__PURE__*/ _jsxs("li", {
423
+ children: [
424
+ "every published write → also ",
425
+ /*#__PURE__*/ _jsx("span", {
426
+ className: "pdtp-mono",
427
+ children: settings.extraTags.join(', ')
428
+ })
429
+ ]
430
+ }) : null,
431
+ ownRules.length ? /*#__PURE__*/ _jsxs("li", {
432
+ children: [
433
+ "rules → also ",
434
+ /*#__PURE__*/ _jsx("span", {
435
+ className: "pdtp-mono",
436
+ children: ownRules.flatMap((r)=>r.bust).join(', ')
437
+ })
438
+ ]
439
+ }) : null
440
+ ]
441
+ })
442
+ }),
443
+ /*#__PURE__*/ _jsx("h3", {
444
+ className: "pdtp-rev-subhead",
445
+ style: {
446
+ marginTop: 14
447
+ },
448
+ children: "…which currently purges (observed)"
449
+ }),
450
+ docTagCarriers.size ? /*#__PURE__*/ _jsx("ul", {
451
+ className: "pdtp-rev-list",
452
+ children: [
453
+ ...docTagCarriers.entries()
454
+ ].slice(0, 10).map(([tag, carriers])=>/*#__PURE__*/ _jsxs("li", {
455
+ children: [
456
+ /*#__PURE__*/ _jsx("span", {
457
+ className: "pdtp-code",
458
+ children: tag
459
+ }),
460
+ " →",
461
+ ' ',
462
+ carriers.map((c, i)=>/*#__PURE__*/ _jsxs("span", {
463
+ children: [
464
+ i > 0 ? ', ' : '',
465
+ c.name,
466
+ c.baked ? /*#__PURE__*/ _jsx("span", {
467
+ className: "pdtp-warn",
468
+ children: " (baked-in ⚠)"
469
+ }) : /*#__PURE__*/ _jsx("span", {
470
+ className: "pdtp-muted",
471
+ children: " (own entry ✓)"
472
+ })
473
+ ]
474
+ }, c.name))
475
+ ]
476
+ }, tag))
477
+ }) : !isGlobal ? /*#__PURE__*/ _jsxs("p", {
478
+ className: "pdtp-muted",
479
+ style: {
480
+ margin: 0
481
+ },
482
+ children: [
483
+ "No cached entries carry ",
484
+ slug,
485
+ " doc tags yet — editing a ",
486
+ slug,
487
+ " doc right now purges nothing (its entry materializes, tagged, on first read)."
488
+ ]
489
+ }) : null,
490
+ !isGlobal ? /*#__PURE__*/ _jsx("p", {
491
+ className: "pdtp-note",
492
+ children: listCarriers.length ? /*#__PURE__*/ _jsxs(_Fragment, {
493
+ children: [
494
+ "Membership events purge: ",
495
+ listCarriers.map((read)=>readName(read)).join(', '),
496
+ ' — ',
497
+ "the id-lists re-query; every unchanged card entry survives."
498
+ ]
499
+ }) : /*#__PURE__*/ _jsxs(_Fragment, {
500
+ children: [
501
+ "Membership events currently purge ",
502
+ /*#__PURE__*/ _jsx("strong", {
503
+ children: "nothing"
504
+ }),
505
+ " — no id-list read carries",
506
+ ' ',
507
+ /*#__PURE__*/ _jsx("span", {
508
+ className: "pdtp-code",
509
+ children: `${p}${slug}`
510
+ }),
511
+ " or a scope yet. Creating or deleting a ",
512
+ slug,
513
+ " doc is invisible to the cache until a `cacheIds` read exists."
514
+ ]
515
+ })
516
+ }) : null,
517
+ nodeGetters.length ? /*#__PURE__*/ _jsxs("div", {
518
+ style: {
519
+ marginTop: 14
520
+ },
521
+ children: [
522
+ /*#__PURE__*/ _jsx("h3", {
523
+ className: "pdtp-rev-subhead",
524
+ children: "Getters in your code"
525
+ }),
526
+ /*#__PURE__*/ _jsx("ul", {
527
+ className: "pdtp-rev-list",
528
+ children: nodeGetters.map((g)=>/*#__PURE__*/ _jsxs("li", {
529
+ children: [
530
+ /*#__PURE__*/ _jsx("strong", {
531
+ children: g.getter ? `${g.getter}()` : g.label ?? g.helper
532
+ }),
533
+ ' ',
534
+ /*#__PURE__*/ _jsxs("span", {
535
+ className: "pdtp-mono pdtp-muted",
536
+ children: [
537
+ g.helper,
538
+ g.list ? ` · list:${g.list}` : '',
539
+ " · ",
540
+ g.file,
541
+ ":",
542
+ g.line
543
+ ]
544
+ }),
545
+ ' ',
546
+ g.observed ? /*#__PURE__*/ _jsxs("span", {
547
+ className: "pdtp-muted",
548
+ children: [
549
+ "materialized ×",
550
+ g.observed.count,
551
+ g.observed.bakedIn.length ? /*#__PURE__*/ _jsxs("span", {
552
+ className: "pdtp-warn",
553
+ children: [
554
+ " · ",
555
+ g.observed.bakedIn.length,
556
+ " baked-in ⚠"
557
+ ]
558
+ }) : null,
559
+ g.observed.undeclared ? /*#__PURE__*/ _jsx("span", {
560
+ className: "pdtp-warn",
561
+ children: " · undeclared scope!"
562
+ }) : null
563
+ ]
564
+ }) : /*#__PURE__*/ _jsx("span", {
565
+ className: "pdtp-muted",
566
+ children: "not yet observed"
567
+ })
568
+ ]
569
+ }, `${g.file}:${g.line}`))
570
+ }),
571
+ /*#__PURE__*/ _jsx("p", {
572
+ className: "pdtp-note",
573
+ children: "Found by scanning your source for cache-helper calls (literal slugs only) — the payload-icons pattern."
574
+ })
575
+ ]
576
+ }) : null,
577
+ /*#__PURE__*/ _jsxs("div", {
578
+ className: "pdtp-rev-cols",
579
+ style: {
580
+ marginTop: 14
581
+ },
582
+ children: [
583
+ /*#__PURE__*/ _jsxs("div", {
584
+ children: [
585
+ /*#__PURE__*/ _jsx("h3", {
586
+ className: "pdtp-rev-subhead",
587
+ children: "Can be referenced by (schema)"
588
+ }),
589
+ makesStale.length ? /*#__PURE__*/ _jsx("ul", {
590
+ className: "pdtp-rev-list",
591
+ children: makesStale.map((edge)=>{
592
+ const observedBaked = baked.has(`${node}->${edge.from}`);
593
+ return /*#__PURE__*/ _jsxs("li", {
594
+ children: [
595
+ /*#__PURE__*/ _jsx("strong", {
596
+ children: edge.from
597
+ }),
598
+ ' ',
599
+ /*#__PURE__*/ _jsxs("span", {
600
+ className: "pdtp-mono pdtp-muted",
601
+ children: [
602
+ "via ",
603
+ edge.via,
604
+ " (",
605
+ edge.kind,
606
+ ")"
607
+ ]
608
+ }),
609
+ ' ',
610
+ observedBaked ? /*#__PURE__*/ _jsx("span", {
611
+ className: "pdtp-warn",
612
+ children: "baked-in ⚠ — edits here DO purge those entries"
613
+ }) : /*#__PURE__*/ _jsx("span", {
614
+ className: "pdtp-muted",
615
+ children: "reference only — no purge propagation"
616
+ })
617
+ ]
618
+ }, `${edge.from}-${edge.via}`);
619
+ })
620
+ }) : /*#__PURE__*/ _jsx("p", {
621
+ className: "pdtp-muted",
622
+ style: {
623
+ margin: 0
624
+ },
625
+ children: "No schema field references it."
626
+ })
627
+ ]
628
+ }),
629
+ /*#__PURE__*/ _jsxs("div", {
630
+ children: [
631
+ /*#__PURE__*/ _jsx("h3", {
632
+ className: "pdtp-rev-subhead",
633
+ children: "References (schema)"
634
+ }),
635
+ staleWhen.length ? /*#__PURE__*/ _jsx("ul", {
636
+ className: "pdtp-rev-list",
637
+ children: staleWhen.map((edge)=>{
638
+ const observedBaked = baked.has(`${edge.to}->${node}`);
639
+ return /*#__PURE__*/ _jsxs("li", {
640
+ children: [
641
+ /*#__PURE__*/ _jsx("strong", {
642
+ children: label(edge.to)
643
+ }),
644
+ ' ',
645
+ /*#__PURE__*/ _jsxs("span", {
646
+ className: "pdtp-mono pdtp-muted",
647
+ children: [
648
+ "via ",
649
+ edge.via,
650
+ " (",
651
+ edge.kind,
652
+ ")"
653
+ ]
654
+ }),
655
+ ' ',
656
+ observedBaked ? /*#__PURE__*/ _jsxs("span", {
657
+ className: "pdtp-warn",
658
+ children: [
659
+ "baked-in ⚠ — its edits purge ",
660
+ slug,
661
+ " entries"
662
+ ]
663
+ }) : /*#__PURE__*/ _jsxs("span", {
664
+ className: "pdtp-muted",
665
+ children: [
666
+ "id only — its edits never touch ",
667
+ slug,
668
+ " entries"
669
+ ]
670
+ })
671
+ ]
672
+ }, `${edge.to}-${edge.via}`);
673
+ })
674
+ }) : /*#__PURE__*/ _jsx("p", {
675
+ className: "pdtp-muted",
676
+ style: {
677
+ margin: 0
678
+ },
679
+ children: "References nothing."
680
+ })
681
+ ]
682
+ })
683
+ ]
684
+ }),
685
+ /*#__PURE__*/ _jsxs("p", {
686
+ className: "pdtp-note",
687
+ children: [
688
+ "A reference (id) never couples two entries — the id is stable, and the referenced doc's freshness lives in its own id-keyed entry. Only",
689
+ ' ',
690
+ /*#__PURE__*/ _jsx("span", {
691
+ className: "pdtp-warn",
692
+ children: "baked-in ⚠"
693
+ }),
694
+ " rows propagate purges; refactor those getters to shrink the blast radius."
695
+ ]
696
+ })
697
+ ]
698
+ });
699
+ }
700
+ // ---------------------------------------------------------------------------
701
+ // Fields tab — the per-field blast-radius table
702
+ // ---------------------------------------------------------------------------
703
+ function FieldsTab({ data }) {
704
+ const entries = Object.entries(data.settings);
705
+ if (!entries.length) return /*#__PURE__*/ _jsx("p", {
706
+ className: "pdtp-muted",
707
+ children: "No collections registered."
708
+ });
709
+ return /*#__PURE__*/ _jsxs(_Fragment, {
710
+ children: [
711
+ entries.map(([slug, s])=>/*#__PURE__*/ _jsxs("div", {
712
+ className: "pdtp-card",
713
+ style: {
714
+ marginBottom: 14
715
+ },
716
+ children: [
717
+ /*#__PURE__*/ _jsxs("h2", {
718
+ children: [
719
+ slug,
720
+ ' ',
721
+ /*#__PURE__*/ _jsx("span", {
722
+ className: "pdtp-kind",
723
+ children: Object.keys(s.lists).length ? `lists: ${Object.keys(s.lists).join(', ')}` : 'no declared lists'
724
+ })
725
+ ]
726
+ }),
727
+ /*#__PURE__*/ _jsxs("table", {
728
+ className: "pdtp-table",
729
+ children: [
730
+ /*#__PURE__*/ _jsx("thead", {
731
+ children: /*#__PURE__*/ _jsxs("tr", {
732
+ children: [
733
+ /*#__PURE__*/ _jsx("th", {
734
+ children: "editing field…"
735
+ }),
736
+ /*#__PURE__*/ _jsx("th", {
737
+ children: "busts"
738
+ })
739
+ ]
740
+ })
741
+ }),
742
+ /*#__PURE__*/ _jsx("tbody", {
743
+ children: s.fields.map((field)=>{
744
+ const scopes = Object.entries(s.lists).filter(([, fields])=>fields.includes(field)).map(([scope])=>scope);
745
+ return /*#__PURE__*/ _jsxs("tr", {
746
+ children: [
747
+ /*#__PURE__*/ _jsx("td", {
748
+ className: "pdtp-mono",
749
+ children: field
750
+ }),
751
+ /*#__PURE__*/ _jsxs("td", {
752
+ className: "pdtp-mono",
753
+ children: [
754
+ "doc",
755
+ field === s.idField ? ' + alias (old & new)' : '',
756
+ scopes.length ? ` + ${scopes.map((scope)=>`list:${scope}`).join(' + ')}` : ''
757
+ ]
758
+ })
759
+ ]
760
+ }, field);
761
+ })
762
+ })
763
+ ]
764
+ }),
765
+ /*#__PURE__*/ _jsxs("p", {
766
+ className: "pdtp-note",
767
+ children: [
768
+ "Create / delete / publish / unpublish additionally bust ",
769
+ /*#__PURE__*/ _jsx("span", {
770
+ className: "pdtp-code",
771
+ children: slug
772
+ }),
773
+ Object.keys(s.lists).map((scope)=>/*#__PURE__*/ _jsxs("span", {
774
+ children: [
775
+ ' ',
776
+ "+ ",
777
+ /*#__PURE__*/ _jsx("span", {
778
+ className: "pdtp-code",
779
+ children: `${slug}:list:${scope}`
780
+ })
781
+ ]
782
+ }, scope)),
783
+ s.extraTags.length ? /*#__PURE__*/ _jsxs(_Fragment, {
784
+ children: [
785
+ " · every published write also busts ",
786
+ s.extraTags.join(', ')
787
+ ]
788
+ }) : null
789
+ ]
790
+ })
791
+ ]
792
+ }, slug)),
793
+ data.rules.length ? /*#__PURE__*/ _jsxs("div", {
794
+ className: "pdtp-section",
795
+ children: [
796
+ /*#__PURE__*/ _jsxs("h2", {
797
+ children: [
798
+ "Manual rules ",
799
+ /*#__PURE__*/ _jsx("span", {
800
+ className: "pdtp-kind",
801
+ children: data.rules.length
802
+ })
803
+ ]
804
+ }),
805
+ /*#__PURE__*/ _jsx("table", {
806
+ className: "pdtp-table",
807
+ children: /*#__PURE__*/ _jsx("tbody", {
808
+ children: data.rules.map((rule)=>/*#__PURE__*/ _jsxs("tr", {
809
+ children: [
810
+ /*#__PURE__*/ _jsx("td", {
811
+ className: "pdtp-code",
812
+ children: rule.on
813
+ }),
814
+ /*#__PURE__*/ _jsxs("td", {
815
+ className: "pdtp-mono",
816
+ children: [
817
+ "busts ",
818
+ rule.bust.join(', ')
819
+ ]
820
+ }),
821
+ /*#__PURE__*/ _jsx("td", {
822
+ className: "pdtp-muted",
823
+ children: rule.whenFields ? `when ${rule.whenFields.join(', ')} change` : 'always'
824
+ })
825
+ ]
826
+ }, `${rule.on}-${rule.bust.join(',')}`))
827
+ })
828
+ })
829
+ ]
830
+ }) : null
831
+ ]
832
+ });
833
+ }
834
+ // ---------------------------------------------------------------------------
835
+ // Reads + Events tabs
836
+ // ---------------------------------------------------------------------------
837
+ function ReadsTab({ data }) {
838
+ return data.reads.length ? /*#__PURE__*/ _jsxs(_Fragment, {
839
+ children: [
840
+ /*#__PURE__*/ _jsxs("table", {
841
+ className: "pdtp-table",
842
+ children: [
843
+ /*#__PURE__*/ _jsx("thead", {
844
+ children: /*#__PURE__*/ _jsxs("tr", {
845
+ children: [
846
+ /*#__PURE__*/ _jsx("th", {
847
+ children: "read"
848
+ }),
849
+ /*#__PURE__*/ _jsx("th", {
850
+ children: "tags"
851
+ }),
852
+ /*#__PURE__*/ _jsx("th", {
853
+ children: "health"
854
+ }),
855
+ /*#__PURE__*/ _jsx("th", {
856
+ children: "seen"
857
+ }),
858
+ /*#__PURE__*/ _jsx("th", {
859
+ children: "last"
860
+ })
861
+ ]
862
+ })
863
+ }),
864
+ /*#__PURE__*/ _jsx("tbody", {
865
+ children: data.reads.map((read)=>{
866
+ const all = [
867
+ ...read.staticTags,
868
+ ...read.depTags
869
+ ];
870
+ return /*#__PURE__*/ _jsxs("tr", {
871
+ children: [
872
+ /*#__PURE__*/ _jsx("td", {
873
+ className: "pdtp-code",
874
+ children: readName(read)
875
+ }),
876
+ /*#__PURE__*/ _jsx("td", {
877
+ className: "pdtp-mono",
878
+ title: all.join('\n'),
879
+ children: all.length
880
+ }),
881
+ /*#__PURE__*/ _jsxs("td", {
882
+ children: [
883
+ read.undeclared ? /*#__PURE__*/ _jsx("span", {
884
+ className: "pdtp-warn",
885
+ children: "undeclared scope! "
886
+ }) : null,
887
+ read.bakedIn.length ? /*#__PURE__*/ _jsxs("span", {
888
+ className: "pdtp-warn",
889
+ title: read.bakedIn.map((b)=>`${b.via} → ${b.tag}`).join('\n'),
890
+ children: [
891
+ read.bakedIn.length,
892
+ " baked-in ⚠"
893
+ ]
894
+ }) : null,
895
+ read.capped ? /*#__PURE__*/ _jsx("span", {
896
+ className: "pdtp-warn",
897
+ children: " capped!"
898
+ }) : null,
899
+ !read.undeclared && !read.bakedIn.length && !read.capped ? /*#__PURE__*/ _jsx("span", {
900
+ className: "pdtp-muted",
901
+ children: "atomic ✓"
902
+ }) : null
903
+ ]
904
+ }),
905
+ /*#__PURE__*/ _jsxs("td", {
906
+ className: "pdtp-mono",
907
+ children: [
908
+ "×",
909
+ read.count
910
+ ]
911
+ }),
912
+ /*#__PURE__*/ _jsx("td", {
913
+ className: "pdtp-mono",
914
+ children: clock(read.lastAt)
915
+ })
916
+ ]
917
+ }, readName(read));
918
+ })
919
+ })
920
+ ]
921
+ }),
922
+ /*#__PURE__*/ _jsx("p", {
923
+ className: "pdtp-note",
924
+ children: "Hover a tag count for the full list, a baked-in badge for the field paths. Reads record on cache miss (a hit never re-executes the getter)."
925
+ })
926
+ ]
927
+ }) : /*#__PURE__*/ _jsx("p", {
928
+ className: "pdtp-muted",
929
+ children: "Nothing materialized yet — reads appear here the first time a cacheDoc/cacheIds/cacheGlobal getter runs."
930
+ });
931
+ }
932
+ function EventsTab({ data, endpointPath }) {
933
+ return /*#__PURE__*/ _jsxs(_Fragment, {
934
+ children: [
935
+ data.events.length ? /*#__PURE__*/ _jsxs("table", {
936
+ className: "pdtp-table",
937
+ children: [
938
+ /*#__PURE__*/ _jsx("thead", {
939
+ children: /*#__PURE__*/ _jsxs("tr", {
940
+ children: [
941
+ /*#__PURE__*/ _jsx("th", {
942
+ children: "at"
943
+ }),
944
+ /*#__PURE__*/ _jsx("th", {
945
+ children: "trigger"
946
+ }),
947
+ /*#__PURE__*/ _jsx("th", {
948
+ children: "busted"
949
+ })
950
+ ]
951
+ })
952
+ }),
953
+ /*#__PURE__*/ _jsx("tbody", {
954
+ children: data.events.map((event)=>/*#__PURE__*/ _jsxs("tr", {
955
+ children: [
956
+ /*#__PURE__*/ _jsx("td", {
957
+ className: "pdtp-mono",
958
+ children: clock(event.at)
959
+ }),
960
+ /*#__PURE__*/ _jsxs("td", {
961
+ children: [
962
+ /*#__PURE__*/ _jsxs("span", {
963
+ className: "pdtp-code",
964
+ children: [
965
+ event.trigger.operation,
966
+ " ",
967
+ event.trigger.slug,
968
+ event.trigger.id !== undefined ? `:${event.trigger.id}` : ''
969
+ ]
970
+ }),
971
+ event.trigger.lane === 'draft' ? /*#__PURE__*/ _jsx("span", {
972
+ className: "pdtp-muted",
973
+ children: " draft"
974
+ }) : null
975
+ ]
976
+ }),
977
+ /*#__PURE__*/ _jsxs("td", {
978
+ className: "pdtp-mono",
979
+ title: event.busted.map((b)=>`${b.tag} (${b.reason})`).join('\n'),
980
+ children: [
981
+ event.busted.slice(0, 4).map((b)=>b.tag).join(', '),
982
+ event.busted.length > 4 ? ` +${event.busted.length - 4}` : ''
983
+ ]
984
+ })
985
+ ]
986
+ }, `${event.at}-${event.trigger.slug}-${event.trigger.id ?? ''}`))
987
+ })
988
+ ]
989
+ }) : /*#__PURE__*/ _jsx("p", {
990
+ className: "pdtp-muted",
991
+ children: "No busts recorded yet — edit something in the admin panel."
992
+ }),
993
+ endpointPath ? /*#__PURE__*/ _jsx(BustTagCard, {
994
+ endpointPath: endpointPath
995
+ }) : null
996
+ ]
997
+ });
998
+ }
999
+ // ---------------------------------------------------------------------------
1000
+ // The tabbed panel
1001
+ // ---------------------------------------------------------------------------
1002
+ const TABS = [
1003
+ {
1004
+ key: 'explore',
1005
+ title: 'Explore'
1006
+ },
1007
+ {
1008
+ key: 'fields',
1009
+ title: 'Fields'
1010
+ },
1011
+ {
1012
+ key: 'reads',
1013
+ title: 'Reads'
1014
+ },
1015
+ {
1016
+ key: 'events',
1017
+ title: 'Events'
1018
+ }
1019
+ ];
1020
+ /** The `/dev/revalidate` panel — tabbed so a grown project stays navigable: Explore (the
1021
+ * interactive dependency graph + per-node lookup), Fields (the per-field blast-radius
1022
+ * tables), Reads (observed cache entries with atomic-health badges), Events (the bust
1023
+ * log + manual bust box). */ export function RevalidatePanel({ data, endpointPath }) {
1024
+ const [tab, setTab] = useState('explore');
1025
+ const [selected, setSelected] = useState(null);
1026
+ return /*#__PURE__*/ _jsxs(_Fragment, {
1027
+ children: [
1028
+ !data.observing ? /*#__PURE__*/ _jsx("p", {
1029
+ className: "pdtp-warn",
1030
+ style: {
1031
+ marginTop: 0
1032
+ },
1033
+ children: "Observation is off (`observe: false`) — the graph is static config analysis only; reads and events aren't recorded."
1034
+ }) : null,
1035
+ /*#__PURE__*/ _jsx("div", {
1036
+ className: "pdtp-seg",
1037
+ style: {
1038
+ marginBottom: 20
1039
+ },
1040
+ children: TABS.map(({ key, title })=>/*#__PURE__*/ _jsxs("button", {
1041
+ type: "button",
1042
+ className: tab === key ? 'pdtp-active' : '',
1043
+ onClick: ()=>setTab(key),
1044
+ children: [
1045
+ title,
1046
+ key === 'reads' && data.reads.length ? ` · ${data.reads.length}` : '',
1047
+ key === 'events' && data.events.length ? ` · ${data.events.length}` : ''
1048
+ ]
1049
+ }, key))
1050
+ }),
1051
+ tab === 'explore' ? /*#__PURE__*/ _jsx(ExploreTab, {
1052
+ data: data,
1053
+ selected: selected,
1054
+ onSelect: setSelected
1055
+ }) : null,
1056
+ tab === 'fields' ? /*#__PURE__*/ _jsx(FieldsTab, {
1057
+ data: data
1058
+ }) : null,
1059
+ tab === 'reads' ? /*#__PURE__*/ _jsx(ReadsTab, {
1060
+ data: data
1061
+ }) : null,
1062
+ tab === 'events' ? /*#__PURE__*/ _jsx(EventsTab, {
1063
+ data: data,
1064
+ endpointPath: endpointPath
1065
+ }) : null,
1066
+ /*#__PURE__*/ _jsxs("p", {
1067
+ className: "pdtp-note",
1068
+ children: [
1069
+ "Machine-readable: ",
1070
+ /*#__PURE__*/ _jsxs("span", {
1071
+ className: "pdtp-code",
1072
+ children: [
1073
+ "GET ",
1074
+ endpointPath ?? '/api/revalidate-map'
1075
+ ]
1076
+ }),
1077
+ " serves all of this as JSON",
1078
+ data.prefix ? /*#__PURE__*/ _jsxs(_Fragment, {
1079
+ children: [
1080
+ ' ',
1081
+ "· tag prefix ",
1082
+ /*#__PURE__*/ _jsx("span", {
1083
+ className: "pdtp-code",
1084
+ children: data.prefix
1085
+ })
1086
+ ]
1087
+ }) : null
1088
+ ]
1089
+ })
1090
+ ]
1091
+ });
1092
+ }
1093
+
1094
+ //# sourceMappingURL=revalidatePanel.js.map