@pipelex/mthds-form 0.5.0 → 0.7.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 (38) hide show
  1. package/dist/chunk-2JWRG53S.js +33 -0
  2. package/dist/chunk-2JWRG53S.js.map +1 -0
  3. package/dist/chunk-45C2MKK6.js +94 -0
  4. package/dist/chunk-45C2MKK6.js.map +1 -0
  5. package/dist/chunk-4BSBBG2T.js +80 -0
  6. package/dist/chunk-4BSBBG2T.js.map +1 -0
  7. package/dist/chunk-IIU3YK4O.js +16 -0
  8. package/dist/chunk-IIU3YK4O.js.map +1 -0
  9. package/dist/{chunk-IIJRJGZQ.js → chunk-J4L32LD6.js} +14 -5
  10. package/dist/chunk-J4L32LD6.js.map +1 -0
  11. package/dist/{chunk-IM2AHWBR.js → chunk-R7B7ID25.js} +3 -3
  12. package/dist/{chunk-IM2AHWBR.js.map → chunk-R7B7ID25.js.map} +1 -1
  13. package/dist/{chunk-5Q2W6HBA.js → chunk-T4NSAGOS.js} +3 -3
  14. package/dist/{chunk-5Q2W6HBA.js.map → chunk-T4NSAGOS.js.map} +1 -1
  15. package/dist/core/derive.d.ts +35 -2
  16. package/dist/core/derive.js +2 -1
  17. package/dist/core/file-formats.d.ts +102 -0
  18. package/dist/core/file-formats.js +3 -0
  19. package/dist/core/file-formats.js.map +1 -0
  20. package/dist/core/gate.js +2 -2
  21. package/dist/core/index.d.ts +6 -2
  22. package/dist/core/index.js +8 -4
  23. package/dist/core/native-content.d.ts +219 -0
  24. package/dist/core/native-content.js +4 -0
  25. package/dist/core/native-content.js.map +1 -0
  26. package/dist/core/output-form.d.ts +45 -0
  27. package/dist/core/output-form.js +5 -0
  28. package/dist/core/output-form.js.map +1 -0
  29. package/dist/core/stuff-files.d.ts +52 -0
  30. package/dist/core/stuff-files.js +5 -0
  31. package/dist/core/stuff-files.js.map +1 -0
  32. package/dist/core/values.js +3 -2
  33. package/dist/react/index.d.ts +278 -1
  34. package/dist/react/index.js +1358 -12
  35. package/dist/react/index.js.map +1 -1
  36. package/dist/styles.css +1 -1
  37. package/package.json +17 -5
  38. package/dist/chunk-IIJRJGZQ.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"output-form.js"}
@@ -0,0 +1,52 @@
1
+ import { RunField } from './descriptor.js';
2
+ import './contracts.js';
3
+ import 'mthds/protocol';
4
+
5
+ /**
6
+ * One file a stuff carries: what to fetch, and what to call it once saved.
7
+ */
8
+ interface StuffFile {
9
+ /**
10
+ * The durable reference or URL the value states, for a file held BY
11
+ * REFERENCE. Absent for inline content, which needs no fetch.
12
+ */
13
+ url?: string;
14
+ /**
15
+ * The content itself, for a file the stuff carries INLINE — markup, today.
16
+ * Exactly one of `url` and `text` is set.
17
+ */
18
+ text?: string;
19
+ /** The extension to save inline content under, without the dot. */
20
+ extension?: string;
21
+ /**
22
+ * The URL that works outside this session, when the value carries one. A host
23
+ * whose display URL is session-scoped (an owner-checked proxy route) needs
24
+ * this for anything that leaves the browser.
25
+ */
26
+ publicUrl?: string;
27
+ /** The file's own name when it states one — otherwise the caller derives it. */
28
+ filename?: string;
29
+ mimeType?: string;
30
+ /** Where it sat, dotted: `report.attachments.2.scan`. Names a saved file. */
31
+ path: string;
32
+ kind: 'image' | 'document' | 'markup';
33
+ }
34
+ /**
35
+ * Every file inside a stuff, found by walking the DESCRIPTOR — never the value.
36
+ *
37
+ * The distinction is the whole point. A payload walk would have to decide what
38
+ * a file is by looking at it: an object with a `url`, a string that ends in
39
+ * `.pdf`. That is the guessing this package exists to remove, and it is wrong in
40
+ * both directions — a method whose output legitimately has a `url` field of its
41
+ * own is not a document, and a stored image whose reference carries no extension
42
+ * is. The descriptor already states `kind: "image"` or `kind: "document"`, so
43
+ * the walk reads the declaration and consults the value only for the members
44
+ * that declaration promises.
45
+ *
46
+ * Ordering is the authored one, depth-first, which is the order the rendered
47
+ * view puts them in — so a download of three images numbers them the way the
48
+ * reader saw them.
49
+ */
50
+ declare function collectStuffFiles(field: RunField, value: unknown): StuffFile[];
51
+
52
+ export { type StuffFile, collectStuffFiles };
@@ -0,0 +1,5 @@
1
+ export { collectStuffFiles } from '../chunk-4BSBBG2T.js';
2
+ import '../chunk-45C2MKK6.js';
3
+ import '../chunk-V3OV2XQT.js';
4
+ //# sourceMappingURL=stuff-files.js.map
5
+ //# sourceMappingURL=stuff-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"stuff-files.js"}
@@ -1,5 +1,6 @@
1
- export { apiInputsFromRunValues, fieldsForContract, inputDataFromWorkingMemory, outputsFromPipeOutput, rjsfDataFromRunValues, runValuesFromStore, setValueAtPath, storeInputDataFromRunValues } from '../chunk-5Q2W6HBA.js';
2
- import '../chunk-IIJRJGZQ.js';
1
+ export { apiInputsFromRunValues, fieldsForContract, inputDataFromWorkingMemory, outputsFromPipeOutput, rjsfDataFromRunValues, runValuesFromStore, setValueAtPath, storeInputDataFromRunValues } from '../chunk-T4NSAGOS.js';
2
+ import '../chunk-J4L32LD6.js';
3
+ import '../chunk-2JWRG53S.js';
3
4
  import '../chunk-GCXV76WU.js';
4
5
  import '../chunk-PM55AHRO.js';
5
6
  import '../chunk-GKV77RAS.js';
@@ -1,6 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
3
  import { RunField, BooleanRunField, DateRunField, EnumRunField, FileRunField, ListRunField, NumberRunField, ObjectRunField, ProseRunField, TextRunField, UnknownRunField, ConceptCategory } from '../core/descriptor.js';
4
+ import { HtmlContentView } from '../core/native-content.js';
4
5
  import '../core/contracts.js';
5
6
  import 'mthds/protocol';
6
7
 
@@ -204,6 +205,235 @@ interface UnknownFieldProps {
204
205
  */
205
206
  declare function UnknownField({ field, value, onChange, id, error, disabled }: UnknownFieldProps): react.JSX.Element;
206
207
 
208
+ interface ResultFieldProps {
209
+ field: RunField;
210
+ value: unknown;
211
+ /** Nesting depth, used only for indentation. */
212
+ depth?: number;
213
+ /**
214
+ * Suppress the field's own label. Set on a LIST ITEM, where the index already
215
+ * identifies the entry: the standard says an element descriptor's `name` is
216
+ * unused, and rendering it repeats the parent's label on every row.
217
+ */
218
+ hideLabel?: boolean;
219
+ }
220
+ /** The single dispatch point, mirroring `FieldRenderer` on the input side. */
221
+ declare function ResultField({ field, value, depth, hideLabel }: ResultFieldProps): react.JSX.Element | null;
222
+
223
+ /**
224
+ * A pipe's result, with the two views a result actually needs.
225
+ *
226
+ * ## Two, and why not three
227
+ *
228
+ * **Rendered** is the answer for a person: the descriptor-driven view, which
229
+ * knows a field is an enum, that a date arrived in the serializer's typed
230
+ * envelope, that fifteen records are a table. **JSON** is the answer for whoever
231
+ * is debugging the pipe: what exactly came back, verbatim, copyable. Those are
232
+ * different jobs and neither substitutes for the other.
233
+ *
234
+ * They are deliberately NOT peers, and the toggle should not read as a menu of
235
+ * equal options. One is the result; the other is the receipt. Rendered opens
236
+ * first, always.
237
+ *
238
+ * A third view is the one to resist. An engine-rendered HTML or plain-text
239
+ * presentation — the shape the runtime's own viewer offers — is a second human
240
+ * rendering of the same payload, produced by another codebase, carrying no
241
+ * descriptor (so it cannot know a kind, a plurality or a nesting) and unable to
242
+ * match a host's design system. It is a renderer that cannot be improved without
243
+ * shipping the engine. If a plain-text form is wanted, it is a COPY FORMAT and
244
+ * belongs behind a copy control, not beside the view that reads the standard.
245
+ */
246
+ type StuffViewerView = 'rendered' | 'json';
247
+ interface StuffViewerProps {
248
+ field: RunField;
249
+ value: unknown;
250
+ /**
251
+ * The STUFF's name, for the header — `report_pages`, not `output`.
252
+ *
253
+ * A result descriptor's root node is named by the engine that built it, and
254
+ * `build_output_form` calls it `output` for every pipe there has ever been.
255
+ * That is correct in the artifact: the descriptor describes a pipe's output
256
+ * slot, which has no name of its own. It is wrong on screen, where the reader
257
+ * is looking at ONE data item and every other surface — the graph node they
258
+ * clicked, the input panel beside it, the method's own code — calls that item
259
+ * by the name the author gave it.
260
+ *
261
+ * So the name comes from the caller, because only the caller knows it: the
262
+ * graph knows which node was opened, a run page knows which variable it is
263
+ * showing. Absent, the descriptor's own name stands, which keeps a host that
264
+ * has nothing better to say honest rather than blank.
265
+ *
266
+ * It also supplies `downloadBaseName`'s default, so the file a reader saves
267
+ * is named after the thing they were reading rather than `output.json`.
268
+ */
269
+ name?: string;
270
+ /** Which view opens first. Rendered, unless a host has a reason. */
271
+ defaultView?: StuffViewerView;
272
+ /**
273
+ * Names the saved files: `<baseName>.json`, and any file inside the stuff
274
+ * that carries no name of its own. Defaults to the field's name, which is
275
+ * what the header shows.
276
+ */
277
+ downloadBaseName?: string;
278
+ /**
279
+ * Hide the download control. For a host that saves results its own way, or a
280
+ * surface where saving makes no sense — a preview inside an editor, say.
281
+ */
282
+ hideDownload?: boolean;
283
+ className?: string;
284
+ }
285
+ /**
286
+ * The payload as JSON — the receipt, with the whole of it one click from the
287
+ * clipboard.
288
+ *
289
+ * Exported because it is also the honest FLOOR: a host that holds a value but
290
+ * not the artifacts describing it (an older engine, a spec restored without its
291
+ * validate report) should show the value and say why it is not laid out, rather
292
+ * than show nothing. That is a different thing from what `StuffViewer`'s JSON
293
+ * tab was — one labelled fallback, not one of three guesses offered as a
294
+ * choice.
295
+ */
296
+ declare function JsonView({ value }: {
297
+ value: unknown;
298
+ }): react.JSX.Element;
299
+ declare function StuffViewer({ field, value, name, defaultView, downloadBaseName, hideDownload, className, }: StuffViewerProps): react.JSX.Element;
300
+
301
+ /** How a host turns a stored reference into something fetchable. */
302
+ type ResolveForDownload = (url: string) => string | undefined;
303
+ /**
304
+ * Everything a stuff contains, saved.
305
+ *
306
+ * The rule the caller asked for, and the one that matches what a result IS: the
307
+ * data goes down as JSON, and any image or document inside it goes down as
308
+ * ITSELF. A JSON file holding a URL to a picture is not the picture, and a
309
+ * reader who clicks download on a report with three attachments wants the three
310
+ * attachments — the reference is what the payload had, not what they asked for.
311
+ *
312
+ * The JSON is written whenever the stuff is anything more than its files: a
313
+ * result that IS one image has nothing left over once the image is saved, so
314
+ * writing `{"url": …}` beside it would be a second file saying less than the
315
+ * first. A result with fields around its files keeps both.
316
+ *
317
+ * A file that cannot be fetched is opened in a tab instead of failing. The
318
+ * fetch is same-origin for a host that proxies its storage, but a remote URL
319
+ * with no CORS header cannot be read into a blob at all, and handing the reader
320
+ * the file in a tab is the difference between an awkward download and none.
321
+ */
322
+ declare function downloadStuff({ field, value, baseName, resolveUrl, }: {
323
+ field: RunField;
324
+ value: unknown;
325
+ /** Names the JSON file and prefixes any file that has no name of its own. */
326
+ baseName: string;
327
+ resolveUrl?: ResolveForDownload;
328
+ }): Promise<void>;
329
+
330
+ /**
331
+ * The host's seam for turning a stored reference into something a browser can
332
+ * fetch.
333
+ *
334
+ * ## Why a result view needs one at all
335
+ *
336
+ * A run's files do not come back as public URLs. They come back as
337
+ * `pipelex-storage://…` references, which are an identity in the runtime's
338
+ * store and resolve nowhere in a browser. Without a way to exchange them, every
339
+ * file arm in the result view degrades to naming the file — a PDF that cannot
340
+ * be previewed, an image gallery of blank tiles — and no amount of care in
341
+ * those arms can fix it, because the missing thing is a fact only the host has.
342
+ *
343
+ * ## Why it is SYNCHRONOUS
344
+ *
345
+ * The obvious shape is `(uri) => Promise<string | null>`, and it is the wrong
346
+ * one here. It would put a loading state, a race and an effect into every arm
347
+ * that paints a file — inside table cells and gallery tiles, which are the
348
+ * places least able to carry them — and a gallery of twenty images would make
349
+ * twenty independent round trips as it scrolled.
350
+ *
351
+ * A host that resolves by REWRITING (the common case: map the reference onto
352
+ * its own `/assets` route, which checks ownership and streams the object) has
353
+ * no round trip to make and is served exactly by a pure function. A host that
354
+ * genuinely must presign is better off resolving the run's references in one
355
+ * batch — it holds the payload before it renders it — and closing over the
356
+ * result, which is also the only shape that does not make a gallery quadratic.
357
+ *
358
+ * So the seam is a pure function, and the async case is a lookup in a map the
359
+ * host filled. That is a real constraint on a host and it is stated rather than
360
+ * hidden.
361
+ *
362
+ * ## The contract
363
+ *
364
+ * Return `undefined` for a reference this host cannot resolve, and the view
365
+ * falls back to naming the file — which is what it does with no resolver at
366
+ * all. Never return a URL that will 404: a broken image tile says less than a
367
+ * filename.
368
+ */
369
+ type ResolveUrl = (url: string) => string | undefined;
370
+ /**
371
+ * How a stored reference becomes a URL that works OUTSIDE this page — pasted
372
+ * into another tab, another app, a colleague's message.
373
+ *
374
+ * Separate from `resolveUrl`, because the two answer different questions and a
375
+ * host's answers genuinely differ. A display URL may be a path on the host's
376
+ * own origin behind its session (`/api/assets/…`), which is what lets a strict
377
+ * `img-src 'self'` policy stand and what keeps a picture from expiring while
378
+ * the tab is open. That URL is useless on the clipboard: whoever pastes it has
379
+ * no session. A share URL therefore carries its own credential — a freshly
380
+ * minted presigned URL — and is minted per click rather than held, because it
381
+ * starts expiring the moment it exists.
382
+ *
383
+ * Async for the same reason: minting one is a round trip. Omit it and the copy
384
+ * control falls back to the display URL, which is right for a host whose
385
+ * storage is public anyway.
386
+ */
387
+ type ResolveShareUrl = (url: string) => Promise<string | undefined>;
388
+ interface ResultEnv {
389
+ resolveUrl?: ResolveUrl;
390
+ resolveShareUrl?: ResolveShareUrl;
391
+ }
392
+ declare function ResultEnvProvider({ resolveUrl, resolveShareUrl, children, }: ResultEnv & {
393
+ children: ReactNode;
394
+ }): react.JSX.Element;
395
+ /**
396
+ * The resolver, already applied.
397
+ *
398
+ * Every file arm calls this rather than reading the context and remembering to
399
+ * apply it: `useResolvedUrl(url)` is the URL to actually use, and a component
400
+ * that forgets to call it is the bug this shape prevents. An unresolvable
401
+ * reference comes back as the reference, so the arms' existing
402
+ * `isViewableUrl` check still decides whether it can be linked or only named.
403
+ */
404
+ declare function useResolvedUrl(url: string): string;
405
+ /** For the non-React readers that need the same answer (previewability). */
406
+ declare function useResolveUrl(): ResolveUrl | undefined;
407
+ /** The share-URL minter, when the host supplies one. */
408
+ declare function useResolveShareUrl(): ResolveShareUrl | undefined;
409
+
410
+ interface HtmlPreviewProps {
411
+ content: HtmlContentView;
412
+ /**
413
+ * Beyond this the frame scrolls rather than growing.
414
+ *
415
+ * Generous on purpose. A `native.Html` result is usually a whole report, and
416
+ * 480px showed three paragraphs of one with a scrollbar down the side while
417
+ * the panel around it sat empty — the reader scrolls twice to read once. The
418
+ * frame still sizes itself to its CONTENT, so a short snippet stays short;
419
+ * this only says how far it may grow before scrolling instead.
420
+ */
421
+ maxHeight?: number;
422
+ /**
423
+ * The frame's `img-src`. Defaults to `data: https:` — a method's HTML result
424
+ * embeds the images that method produced. Pass `"data:"` to block remote
425
+ * images entirely, or name an origin to allow only your own storage.
426
+ */
427
+ imgSrc?: string;
428
+ }
429
+ declare function HtmlPreview({ content, maxHeight, imgSrc, }: HtmlPreviewProps): react.JSX.Element;
430
+
431
+ interface MarkdownProps {
432
+ text: string;
433
+ className?: string;
434
+ }
435
+ declare function Markdown({ text, className }: MarkdownProps): react.JSX.Element;
436
+
207
437
  interface FieldShellProps {
208
438
  name: string;
209
439
  title?: string;
@@ -281,6 +511,53 @@ interface FieldStrings {
281
511
  preview: string;
282
512
  removeFileAria: string;
283
513
  previewUnavailablePdf: string;
514
+ /**
515
+ * What a result slot the run resolved as an absence SHOWS — a dash, not a
516
+ * sentence. Beside forty values in a grid, "not provided" repeated is louder
517
+ * than the data; a dash reads as the blank it is.
518
+ */
519
+ resultAbsent: string;
520
+ /**
521
+ * What that dash SAYS, to a screen reader.
522
+ *
523
+ * The two are separate because a glyph and a sentence are answers to different
524
+ * questions. A table column of hyphens read aloud is "hyphen, hyphen, hyphen",
525
+ * which is noise rather than information — so the dash is `aria-hidden` and
526
+ * this rides beside it, visually hidden.
527
+ */
528
+ resultAbsentDescription: string;
529
+ /**
530
+ * Shown above the raw value when a result cannot be laid out because the
531
+ * artifacts describing it are absent. It names the CAUSE — the descriptor,
532
+ * not the data — because the value is right there underneath and a reader
533
+ * who is not told why will assume the view is broken.
534
+ */
535
+ resultUndescribed: string;
536
+ /** The control that opens a result table row to show the rest of the record. */
537
+ toggleRowDetails: (index: number) => string;
538
+ /** The (visually hidden) header of the column those controls sit in. */
539
+ rowDetailsColumn: string;
540
+ /** The control that puts a file's whole URL on the clipboard. */
541
+ copyUrl: string;
542
+ /** The copy control beside any text value, markdown or not. */
543
+ copyText: string;
544
+ /** The download control's label. */
545
+ download: string;
546
+ /** Announced while the files are being fetched. */
547
+ downloading: string;
548
+ /** The result panel's two views, and the control that copies the payload. */
549
+ viewRendered: string;
550
+ viewJson: string;
551
+ copyJson: string;
552
+ resultViewGroup: string;
553
+ yes: string;
554
+ no: string;
555
+ /**
556
+ * Shown when a picked file is not a format this slot accepts. Takes the
557
+ * accept label so the message names what WOULD have worked - "not accepted"
558
+ * on its own leaves the user guessing at the list they just failed.
559
+ */
560
+ unsupportedFileType: (accept: string) => string;
284
561
  /** The optional-entries disclosure ("field" inside a concept, "input" at top level). */
285
562
  hideOptionalFields: string;
286
563
  hideOptionalInputs: string;
@@ -344,4 +621,4 @@ declare function useFieldPresentation(): FieldPresentation;
344
621
  */
345
622
  declare function humanizeFieldName(name: string): string;
346
623
 
347
- export { BooleanField, ConceptPill, DEFAULT_FIELD_STRINGS, DateField, DocumentField, EnumField, FieldDomIdProvider, type FieldEnv, type FieldPresentation, FieldPresentationProvider, FieldRenderer, type FieldRendererProps, FieldShell, type FieldStrings, FieldStringsProvider, type FileValue, ImageField, ListField, NumberField, ObjectField, OptionalToggle, ProseField, TextField, UnknownField, fieldControlClass, humanizeFieldName, useFieldDomId, useFieldPresentation, useFieldStrings };
624
+ export { BooleanField, ConceptPill, DEFAULT_FIELD_STRINGS, DateField, DocumentField, EnumField, FieldDomIdProvider, type FieldEnv, type FieldPresentation, FieldPresentationProvider, FieldRenderer, type FieldRendererProps, FieldShell, type FieldStrings, FieldStringsProvider, type FileValue, HtmlPreview, type HtmlPreviewProps, ImageField, JsonView, ListField, Markdown, type MarkdownProps, NumberField, ObjectField, OptionalToggle, ProseField, type ResolveForDownload, type ResolveShareUrl, type ResolveUrl, ResultEnvProvider, ResultField, type ResultFieldProps, StuffViewer, type StuffViewerProps, type StuffViewerView, TextField, UnknownField, downloadStuff, fieldControlClass, humanizeFieldName, useFieldDomId, useFieldPresentation, useFieldStrings, useResolveShareUrl, useResolveUrl, useResolvedUrl };