@plumix/plugin-media 0.1.0 → 0.3.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.
package/dist/builder.d.ts CHANGED
@@ -1,80 +1,116 @@
1
- import type { MediaListMetaBoxField, MediaMetaBoxField, MetaBoxFieldSpan } from "plumix/plugin";
1
+ import type { Label } from "plumix/i18n";
2
+ import type { FieldBuilder, MediaListMetaBoxField, MediaMetaBoxField, MetaBoxFieldSpan, MetaBoxFieldValidate, MetaFieldCondition, MetaFieldConditionRule } from "plumix/plugin";
3
+ import type { MediaFieldScope, MediaReference } from "./lookup.js";
4
+ /** One read element: the hydrated `MediaReference`, or the bare id after `.returns("id")`. */
5
+ type MediaReadItem<Returns extends "id" | "hydrated"> = Returns extends "id" ? string : MediaReference;
2
6
  /**
3
- * Per-field options for the `media()` builder.
4
- *
5
- * `accept` filters the picker grid client-side (UX hint) AND the
6
- * `mediaLookupAdapter`'s `list({ ids })` path on the server (security
7
- * boundary — re-validated at write time). Format: a single MIME prefix
8
- * string (`"image/"` matches every `image/*` mime) OR a readonly array
9
- * of exact MIME matches (`["image/png", "application/pdf"]`).
10
- *
11
- * `default` is a `MediaValue` shape (`{ id, ... }`) — the meta pipeline
12
- * normalizes the cached fields on every write, so a new entry with a
13
- * `default: { id: "42" }` ships with the canonical `{ id, mime,
14
- * filename }` after the first save.
7
+ * The phantom read type. A single reference is always optional (a
8
+ * target can be deleted after the id is written); a multi reference
9
+ * reads a dense array, present once `.required()` guarantees a write.
15
10
  */
16
- export interface MediaFieldOptions {
17
- readonly key: string;
18
- readonly label: string;
19
- readonly required?: boolean;
20
- readonly description?: string;
21
- readonly default?: MediaValue;
11
+ type MediaReadValue<Multiple extends boolean, Required extends boolean, Returns extends "id" | "hydrated"> = Multiple extends true ? Required extends true ? readonly MediaReadItem<Returns>[] : readonly MediaReadItem<Returns>[] | undefined : MediaReadItem<Returns> | undefined;
12
+ /** The phantom stored shape — bare ids; `.required()` narrows optionality. */
13
+ type MediaStoredValue<Multiple extends boolean, Required extends boolean> = Multiple extends true ? Required extends true ? readonly string[] : readonly string[] | undefined : Required extends true ? string : string | undefined;
14
+ interface MediaFieldState {
15
+ readonly visibleWhen?: MetaFieldCondition;
16
+ readonly label?: Label;
17
+ readonly description?: Label;
18
+ readonly default?: string | readonly string[];
19
+ readonly required?: true;
20
+ readonly multiple?: true;
21
+ readonly returns?: "id";
22
+ readonly max?: number;
22
23
  readonly span?: MetaBoxFieldSpan;
23
- readonly accept?: string | readonly string[];
24
- }
25
- /**
26
- * Storage shape for a single `media` field. The meta pipeline writes
27
- * `{ id, ...adapterCached }` on every save (mime + filename today;
28
- * width/height/etc once the upload pipeline captures them). Renders
29
- * read straight from this object — no `lookup.resolve` per field.
30
- */
31
- export interface MediaValue {
32
- readonly id: string;
33
- readonly mime?: string;
34
- readonly filename?: string;
24
+ readonly capability?: string;
25
+ readonly showInApi?: true;
26
+ readonly sanitize?: (value: unknown) => unknown;
27
+ readonly validate?: MetaBoxFieldValidate;
35
28
  }
36
29
  /**
37
- * Build a typed `media` reference field. Stored as a `MediaValue`
38
- * object admin thumbnails render without a resolve round-trip, the
39
- * meta pipeline rewrites the cached fields on every write so the
40
- * snapshot stays close to the asset.
30
+ * Fluent chain for the `media` reference field
31
+ * `media("hero").accept("image/")`. Mirrors the core reference
32
+ * builders (`entry` / `term` / `user`): `.accept()` refines the picker
33
+ * MIME filter, `.multiple()` flips to an id array, `.returns("id")`
34
+ * opts out of read-time hydration. Immutable — every call returns a
35
+ * fresh instance.
41
36
  *
42
- * The picker opens the existing Media Library in modal/picker mode,
43
- * filters the grid to MIME `accept` if set, and emits the bare id +
44
- * cached fields back to the form on selection.
45
- *
46
- * Single-value only — `mediaList()` covers the multi case (slice
47
- * #132).
48
- */
49
- export declare function media(options: MediaFieldOptions): MediaMetaBoxField;
50
- /**
51
- * Per-field options for the `mediaList()` builder. Multi-value
52
- * counterpart to `media()` — same `accept` semantics, plus a `max`
53
- * length cap. Storage is `MediaValue[]`.
37
+ * Storage is the bare media id (an id array under `.multiple()`); reads
38
+ * hydrate to the {@link MediaReference} summary by default so themes
39
+ * render a media field (URL included) without a manual fetch.
54
40
  */
55
- export interface MediaListFieldOptions {
56
- readonly key: string;
57
- readonly label: string;
58
- readonly required?: boolean;
59
- readonly description?: string;
60
- readonly default?: readonly MediaValue[];
61
- readonly span?: MetaBoxFieldSpan;
62
- readonly accept?: string | readonly string[];
63
- /** Max items allowed in the array. Omitted = unbounded. */
64
- readonly max?: number;
41
+ export declare class MediaFieldBuilder<K extends string = string, Multiple extends boolean = false, Required extends boolean = false, Returns extends "id" | "hydrated" = "hydrated"> implements FieldBuilder {
42
+ #private;
43
+ /** Phantom literal key of the field — type-level only, never assigned. */
44
+ readonly _key: K;
45
+ /** Phantom read type — hydrated `MediaReference` by default, id after `.returns("id")`. */
46
+ readonly _value: MediaReadValue<Multiple, Required, Returns>;
47
+ /** Phantom stored shape — bare ids; `.required()` narrows optionality. */
48
+ readonly _stored: MediaStoredValue<Multiple, Required>;
49
+ /** Phantom cardinality marker backing the compile-time gate on `.multiple()` / `.max()`. */
50
+ readonly _multiple: Multiple;
51
+ constructor(key: string, scope?: MediaFieldScope, state?: MediaFieldState);
52
+ /**
53
+ * Filter the picker (and re-validate on write) by MIME. A single
54
+ * prefix string (`"image/"` matches every `image/*`) or a readonly
55
+ * array of exact MIME matches (`["image/png", "application/pdf"]`).
56
+ */
57
+ accept(accept: string | readonly string[]): MediaFieldBuilder<K, Multiple, Required, Returns>;
58
+ /**
59
+ * Store an array of ids instead of a single one — reads become a
60
+ * dense array. Declare cardinality before `.required()` narrows the
61
+ * shapes; the `this`-type gate blocks a post-narrowing `.multiple()`.
62
+ */
63
+ multiple(this: MediaFieldBuilder<K, false, false, Returns>): MediaFieldBuilder<K, true, false, Returns>;
64
+ /** Cap the array length — multi-value fields only. */
65
+ max(this: MediaFieldBuilder<K, true, Required, Returns>, max: number): MediaFieldBuilder<K, true, Required, Returns>;
66
+ /** Read the bare stored id(s) instead of the hydrated summary. */
67
+ returns(shape: "id"): MediaFieldBuilder<K, Multiple, Required, "id">;
68
+ /** Mark the field required — enforced at write time by the constraint walker. */
69
+ required(): MediaFieldBuilder<K, Multiple, true, Returns>;
70
+ /** Override the derived (humanized-key) label. */
71
+ label(label: Label): MediaFieldBuilder<K, Multiple, Required, Returns>;
72
+ /** Help text rendered under the label. */
73
+ description(description: Label): MediaFieldBuilder<K, Multiple, Required, Returns>;
74
+ /** Prefill for absent keys — a stored id (or id array for multi fields). */
75
+ default(value: Multiple extends true ? readonly string[] : string): MediaFieldBuilder<K, Multiple, Required, Returns>;
76
+ /** Column span within the box's 12-column grid — a universal layout hint. */
77
+ span(span: MetaBoxFieldSpan): MediaFieldBuilder<K, Multiple, Required, Returns>;
78
+ /** Capability gate for this field — see `MetaBoxFieldBase.capability`. */
79
+ capability(capability: string): MediaFieldBuilder<K, Multiple, Required, Returns>;
80
+ /** Opt this field's value into public REST responses (default-deny). */
81
+ showInApi(): MediaFieldBuilder<K, Multiple, Required, Returns>;
82
+ /** Rule factory: this field's stored id equals `value`. */
83
+ is(value: string): MetaFieldConditionRule;
84
+ /** Rule factory: this field's stored id differs from `value`. */
85
+ isNot(value: string): MetaFieldConditionRule;
86
+ /** Rule factory: this field has no value (unset or cleared). */
87
+ isEmpty(): MetaFieldConditionRule;
88
+ /** Rule factory: this field has a value. */
89
+ isNotEmpty(): MetaFieldConditionRule;
90
+ /** Rule factory: the selection includes `id` — multi-value only. */
91
+ contains(this: MediaFieldBuilder<K, true, Required, Returns>, id: string): MetaFieldConditionRule;
92
+ /** Rule factory: the selection does not include `id` — multi-value only. */
93
+ notContains(this: MediaFieldBuilder<K, true, Required, Returns>, id: string): MetaFieldConditionRule;
94
+ /**
95
+ * Show this field only when every rule passes (one AND group).
96
+ * Replaces any previously declared condition; `.orVisibleWhen()`
97
+ * adds alternatives.
98
+ */
99
+ visibleWhen(...rules: MetaFieldConditionRule[]): MediaFieldBuilder<K, Multiple, Required, Returns>;
100
+ /** Add an OR alternative — one more AND group of rules. */
101
+ orVisibleWhen(...rules: MetaFieldConditionRule[]): MediaFieldBuilder<K, Multiple, Required, Returns>;
102
+ /** Normalising transform, applied after coercion and before persistence. */
103
+ sanitize(sanitize: (value: unknown) => unknown): MediaFieldBuilder<K, Multiple, Required, Returns>;
104
+ /** Custom validation — `true` for valid, or an i18n-able failure message. */
105
+ validate(validate: MetaBoxFieldValidate): MediaFieldBuilder<K, Multiple, Required, Returns>;
106
+ /** Compile the chain into the wire/manifest field definition. */
107
+ build(): Multiple extends true ? MediaListMetaBoxField : MediaMetaBoxField;
65
108
  }
66
109
  /**
67
- * Build a typed `mediaList` reference field — the multi-value
68
- * counterpart to `media()`. Storage is `MediaValue[]` —
69
- * `[{ id, mime?, filename? }, ...]`. The meta pipeline rewrites
70
- * each entry's cached fields on every write so reads can render
71
- * thumbnails without a per-item resolve round-trip.
72
- *
73
- * Picker stays open across selections (so authors can pick several
74
- * without re-opening) and auto-stops when `max` is reached.
75
- * Selected items render as a vertical list with up/down reorder
76
- * and per-item removal; drag-reorder is deferred to a follow-up
77
- * (see `MediaListPickerField`).
110
+ * Build a typed `media` reference field — `media("hero")`. The picker
111
+ * opens the Media Library in modal mode; `.accept()` filters the grid
112
+ * (and re-validates on write), `.multiple()` stores an id array.
78
113
  */
79
- export declare function mediaList(options: MediaListFieldOptions): MediaListMetaBoxField;
114
+ export declare function media<K extends string>(key: K): MediaFieldBuilder<K>;
115
+ export {};
80
116
  //# sourceMappingURL=builder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,eAAe,CAAC;AAIvB;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC;IAC9B,QAAQ,CAAC,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;CAC9C;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,iBAAiB,GAAG,iBAAiB,CAanE;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IACzC,QAAQ,CAAC,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IAC7C,2DAA2D;IAC3D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CACvB,OAAO,EAAE,qBAAqB,GAC7B,qBAAqB,CAmBvB"}
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"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"}
package/dist/builder.js CHANGED
@@ -1,61 +1,161 @@
1
+ var _a;
2
+ // "heroImage" → "Hero image". Derived default for fields authored
3
+ // without `.label()`. Kept local — the plugin can't reach core's
4
+ // private `humanizeFieldKey`, and the rule is a one-liner.
5
+ function humanizeFieldKey(key) {
6
+ const spaced = key
7
+ .replaceAll(/([a-z0-9])([A-Z])/g, "$1 $2")
8
+ .replaceAll(/[_:-]+/g, " ")
9
+ .trim()
10
+ .toLowerCase();
11
+ return spaced.charAt(0).toUpperCase() + spaced.slice(1);
12
+ }
1
13
  /**
2
- * Build a typed `media` reference field. Stored as a `MediaValue`
3
- * object admin thumbnails render without a resolve round-trip, the
4
- * meta pipeline rewrites the cached fields on every write so the
5
- * snapshot stays close to the asset.
6
- *
7
- * The picker opens the existing Media Library in modal/picker mode,
8
- * filters the grid to MIME `accept` if set, and emits the bare id +
9
- * cached fields back to the form on selection.
14
+ * Fluent chain for the `media` reference field
15
+ * `media("hero").accept("image/")`. Mirrors the core reference
16
+ * builders (`entry` / `term` / `user`): `.accept()` refines the picker
17
+ * MIME filter, `.multiple()` flips to an id array, `.returns("id")`
18
+ * opts out of read-time hydration. Immutable — every call returns a
19
+ * fresh instance.
10
20
  *
11
- * Single-value only `mediaList()` covers the multi case (slice
12
- * #132).
21
+ * Storage is the bare media id (an id array under `.multiple()`); reads
22
+ * hydrate to the {@link MediaReference} summary by default so themes
23
+ * render a media field (URL included) without a manual fetch.
13
24
  */
14
- export function media(options) {
15
- const scope = { accept: options.accept };
16
- return {
17
- key: options.key,
18
- label: options.label,
19
- type: "json",
20
- inputType: "media",
21
- referenceTarget: { kind: "media", scope, valueShape: "object" },
22
- required: options.required,
23
- description: options.description,
24
- default: options.default,
25
- span: options.span,
26
- };
25
+ export class MediaFieldBuilder {
26
+ #key;
27
+ #scope;
28
+ #state;
29
+ constructor(key, scope = {}, state = {}) {
30
+ this.#key = key;
31
+ this.#scope = scope;
32
+ this.#state = state;
33
+ }
34
+ #fork(patch, scope = this.#scope) {
35
+ return new _a(this.#key, scope, { ...this.#state, ...patch });
36
+ }
37
+ /**
38
+ * Filter the picker (and re-validate on write) by MIME. A single
39
+ * prefix string (`"image/"` matches every `image/*`) or a readonly
40
+ * array of exact MIME matches (`["image/png", "application/pdf"]`).
41
+ */
42
+ accept(accept) {
43
+ return this.#fork({}, { accept });
44
+ }
45
+ /**
46
+ * Store an array of ids instead of a single one — reads become a
47
+ * dense array. Declare cardinality before `.required()` narrows the
48
+ * shapes; the `this`-type gate blocks a post-narrowing `.multiple()`.
49
+ */
50
+ multiple() {
51
+ return this.#fork({ multiple: true });
52
+ }
53
+ /** Cap the array length — multi-value fields only. */
54
+ max(max) {
55
+ return this.#fork({ max });
56
+ }
57
+ /** Read the bare stored id(s) instead of the hydrated summary. */
58
+ returns(shape) {
59
+ return this.#fork({ returns: shape });
60
+ }
61
+ /** Mark the field required — enforced at write time by the constraint walker. */
62
+ required() {
63
+ return this.#fork({ required: true });
64
+ }
65
+ /** Override the derived (humanized-key) label. */
66
+ label(label) {
67
+ return this.#fork({ label });
68
+ }
69
+ /** Help text rendered under the label. */
70
+ description(description) {
71
+ return this.#fork({ description });
72
+ }
73
+ /** Prefill for absent keys — a stored id (or id array for multi fields). */
74
+ default(value) {
75
+ return this.#fork({ default: value });
76
+ }
77
+ /** Column span within the box's 12-column grid — a universal layout hint. */
78
+ span(span) {
79
+ return this.#fork({ span });
80
+ }
81
+ /** Capability gate for this field — see `MetaBoxFieldBase.capability`. */
82
+ capability(capability) {
83
+ return this.#fork({ capability });
84
+ }
85
+ /** Opt this field's value into public REST responses (default-deny). */
86
+ showInApi() {
87
+ return this.#fork({ showInApi: true });
88
+ }
89
+ /** Rule factory: this field's stored id equals `value`. */
90
+ is(value) {
91
+ return { key: this.#key, op: "eq", value };
92
+ }
93
+ /** Rule factory: this field's stored id differs from `value`. */
94
+ isNot(value) {
95
+ return { key: this.#key, op: "neq", value };
96
+ }
97
+ /** Rule factory: this field has no value (unset or cleared). */
98
+ isEmpty() {
99
+ return { key: this.#key, op: "empty" };
100
+ }
101
+ /** Rule factory: this field has a value. */
102
+ isNotEmpty() {
103
+ return { key: this.#key, op: "not_empty" };
104
+ }
105
+ /** Rule factory: the selection includes `id` — multi-value only. */
106
+ contains(id) {
107
+ return { key: this.#key, op: "contains", value: id };
108
+ }
109
+ /** Rule factory: the selection does not include `id` — multi-value only. */
110
+ notContains(id) {
111
+ return { key: this.#key, op: "not_contains", value: id };
112
+ }
113
+ /**
114
+ * Show this field only when every rule passes (one AND group).
115
+ * Replaces any previously declared condition; `.orVisibleWhen()`
116
+ * adds alternatives.
117
+ */
118
+ visibleWhen(...rules) {
119
+ return this.#fork({ visibleWhen: [rules] });
120
+ }
121
+ /** Add an OR alternative — one more AND group of rules. */
122
+ orVisibleWhen(...rules) {
123
+ return this.#fork({
124
+ visibleWhen: [...(this.#state.visibleWhen ?? []), rules],
125
+ });
126
+ }
127
+ /** Normalising transform, applied after coercion and before persistence. */
128
+ sanitize(sanitize) {
129
+ return this.#fork({ sanitize });
130
+ }
131
+ /** Custom validation — `true` for valid, or an i18n-able failure message. */
132
+ validate(validate) {
133
+ return this.#fork({ validate });
134
+ }
135
+ /** Compile the chain into the wire/manifest field definition. */
136
+ build() {
137
+ const { multiple, ...state } = this.#state;
138
+ const target = multiple
139
+ ? { kind: "media", scope: this.#scope, multiple: true }
140
+ : { kind: "media", scope: this.#scope };
141
+ const field = {
142
+ ...state,
143
+ key: this.#key,
144
+ label: state.label ?? humanizeFieldKey(this.#key),
145
+ type: "json",
146
+ inputType: multiple ? "mediaList" : "media",
147
+ referenceTarget: target,
148
+ };
149
+ return field;
150
+ }
27
151
  }
152
+ _a = MediaFieldBuilder;
28
153
  /**
29
- * Build a typed `mediaList` reference field — the multi-value
30
- * counterpart to `media()`. Storage is `MediaValue[]` —
31
- * `[{ id, mime?, filename? }, ...]`. The meta pipeline rewrites
32
- * each entry's cached fields on every write so reads can render
33
- * thumbnails without a per-item resolve round-trip.
34
- *
35
- * Picker stays open across selections (so authors can pick several
36
- * without re-opening) and auto-stops when `max` is reached.
37
- * Selected items render as a vertical list with up/down reorder
38
- * and per-item removal; drag-reorder is deferred to a follow-up
39
- * (see `MediaListPickerField`).
154
+ * Build a typed `media` reference field — `media("hero")`. The picker
155
+ * opens the Media Library in modal mode; `.accept()` filters the grid
156
+ * (and re-validates on write), `.multiple()` stores an id array.
40
157
  */
41
- export function mediaList(options) {
42
- const scope = { accept: options.accept };
43
- return {
44
- key: options.key,
45
- label: options.label,
46
- type: "json",
47
- inputType: "mediaList",
48
- referenceTarget: {
49
- kind: "media",
50
- scope,
51
- valueShape: "object",
52
- multiple: true,
53
- },
54
- max: options.max,
55
- required: options.required,
56
- description: options.description,
57
- default: options.default,
58
- span: options.span,
59
- };
158
+ export function media(key) {
159
+ return new MediaFieldBuilder(key);
60
160
  }
61
161
  //# sourceMappingURL=builder.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"builder.js","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AA4CA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,KAAK,CAAC,OAA0B;IAC9C,MAAM,KAAK,GAAoB,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;IAC1D,OAAO;QACL,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,IAAI,EAAE,MAAM;QACZ,SAAS,EAAE,OAAO;QAClB,eAAe,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE;QAC/D,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,IAAI,EAAE,OAAO,CAAC,IAAI;KACnB,CAAC;AACJ,CAAC;AAmBD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,SAAS,CACvB,OAA8B;IAE9B,MAAM,KAAK,GAAoB,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;IAC1D,OAAO;QACL,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,IAAI,EAAE,MAAM;QACZ,SAAS,EAAE,WAAW;QACtB,eAAe,EAAE;YACf,IAAI,EAAE,OAAO;YACb,KAAK;YACL,UAAU,EAAE,QAAQ;YACpB,QAAQ,EAAE,IAAI;SACf;QACD,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,IAAI,EAAE,OAAO,CAAC,IAAI;KACnB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"builder.js","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"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"}
package/dist/fields.d.ts CHANGED
@@ -1,3 +1,2 @@
1
- export { media, mediaList } from "./builder.js";
2
- export type { MediaFieldOptions, MediaListFieldOptions, MediaValue, } from "./builder.js";
1
+ export { media, MediaFieldBuilder } from "./builder.js";
3
2
  //# sourceMappingURL=fields.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../src/fields.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAChD,YAAY,EACV,iBAAiB,EACjB,qBAAqB,EACrB,UAAU,GACX,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../src/fields.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC"}
package/dist/fields.js CHANGED
@@ -3,5 +3,5 @@
3
3
  // with `media()` (the plugin descriptor factory) exported from the
4
4
  // package root. Mirrors the `plumix/fields` subpath convention from
5
5
  // core.
6
- export { media, mediaList } from "./builder.js";
6
+ export { media, MediaFieldBuilder } from "./builder.js";
7
7
  //# sourceMappingURL=fields.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"fields.js","sourceRoot":"","sources":["../src/fields.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,sEAAsE;AACtE,mEAAmE;AACnE,oEAAoE;AACpE,QAAQ;AAER,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"fields.js","sourceRoot":"","sources":["../src/fields.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,sEAAsE;AACtE,mEAAmE;AACnE,oEAAoE;AACpE,QAAQ;AAER,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { PluginDescriptor } from "plumix/plugin";
2
2
  import { DEFAULT_ACCEPTED_TYPES } from "./mime.js";
3
- export type { MediaFieldScope } from "./lookup.js";
3
+ export type { MediaFieldScope, MediaReference } from "./lookup.js";
4
4
  export { DEFAULT_ACCEPTED_TYPES };
5
5
  /** Default max upload size — 25 MiB. */
6
6
  export declare const DEFAULT_MAX_UPLOAD_SIZE: number;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAmB,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAOvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAKnD,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,OAAO,EAAE,sBAAsB,EAAE,CAAC;AAElC,wCAAwC;AACxC,eAAO,MAAM,uBAAuB,QAAmB,CAAC;AAgExD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,UAAU,kBAAkB;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAYD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,KAAK,CACnB,OAAO,GAAE,kBAAuB,GAC/B,gBAAgB,CAAC,SAAS,CAAC,CA8G7B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAmB,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAOvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAKnD,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAEnE,OAAO,EAAE,sBAAsB,EAAE,CAAC;AAElC,wCAAwC;AACxC,eAAO,MAAM,uBAAuB,QAAmB,CAAC;AAgExD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,UAAU,kBAAkB;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAYD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,KAAK,CACnB,OAAO,GAAE,kBAAuB,GAC/B,gBAAgB,CAAC,SAAS,CAAC,CA8G7B"}
package/dist/lookup.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { LookupAdapter } from "plumix/plugin";
1
+ import type { HydratedReference, LookupResult } from "plumix/plugin";
2
2
  /**
3
3
  * Public scope shape for the `media` reference field. Carried on the
4
4
  * field's `referenceTarget.scope`; the media `LookupAdapter` consumes
@@ -14,12 +14,32 @@ import type { LookupAdapter } from "plumix/plugin";
14
14
  export interface MediaFieldScope {
15
15
  readonly accept?: string | readonly string[];
16
16
  }
17
+ /**
18
+ * Hydrated shape of a `media` reference — the read pipeline resolves
19
+ * stored ids into this at read time, so themes can render a media meta
20
+ * field (URL included) without a manual fetch. `id` stays the stored
21
+ * string id so a hydrated value posted back through a meta write
22
+ * self-heals to the plain id.
23
+ */
24
+ export interface MediaReference extends HydratedReference {
25
+ readonly title: string;
26
+ readonly mime: string;
27
+ readonly size: number;
28
+ readonly alt: string | null;
29
+ readonly url: string;
30
+ readonly thumbnailUrl: string;
31
+ readonly width: number | null;
32
+ readonly height: number | null;
33
+ }
34
+ declare module "plumix/plugin" {
35
+ interface ReferenceHydrationShapes {
36
+ readonly media: MediaReference;
37
+ }
38
+ }
17
39
  /**
18
40
  * Server-side adapter for the `media` reference field. Storage is the
19
- * cached-object shape (`{ id, mime, filename }`) — the meta pipeline
20
- * pulls `cached` from `LookupResult` and merges it into the stored
21
- * value on every write so reads render thumbnails without an extra
22
- * resolve round-trip.
41
+ * plain media id; `hydrate` resolves ids into `MediaReference` (URL
42
+ * included) at read time through the shared meta pipeline.
23
43
  *
24
44
  * Queries:
25
45
  * - `list({ ids })`: PK lookup via `inArray(entries.id, …)` + the
@@ -39,5 +59,9 @@ export interface MediaFieldScope {
39
59
  * `media.confirm` (flips to published) — referencing one would point
40
60
  * at an asset whose bytes haven't been verified.
41
61
  */
42
- export declare const mediaLookupAdapter: LookupAdapter<MediaFieldScope>;
62
+ export declare const mediaLookupAdapter: {
63
+ list(ctx: import("@plumix/core").AppContext, options: import("@plumix/core").LookupListOptions<MediaFieldScope>): Promise<LookupResult[]>;
64
+ resolve(ctx: import("@plumix/core").AppContext, id: string, scope?: MediaFieldScope | undefined): Promise<LookupResult | null>;
65
+ hydrate(ctx: import("@plumix/core").AppContext, options: import("@plumix/core").LookupHydrateOptions<MediaFieldScope>): Promise<MediaReference[]>;
66
+ };
43
67
  //# sourceMappingURL=lookup.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"lookup.d.ts","sourceRoot":"","sources":["../src/lookup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAqB,MAAM,eAAe,CAAC;AAgBtE;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;CAC9C;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,kBAAkB,EAAE,aAAa,CAAC,eAAe,CA4D7D,CAAC"}
1
+ {"version":3,"file":"lookup.d.ts","sourceRoot":"","sources":["../src/lookup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EAEjB,YAAY,EAEb,MAAM,eAAe,CAAC;AAiBvB;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;CAC9C;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAe,SAAQ,iBAAiB;IACvD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,OAAO,QAAQ,eAAe,CAAC;IAC7B,UAAU,wBAAwB;QAChC,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;KAChC;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,eAAO,MAAM,kBAAkB;;;;CA6GW,CAAC"}
package/dist/lookup.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { and, desc, entries, eq, inArray, like, sql } from "plumix/plugin";
2
2
  import { parseMediaMeta } from "./meta.js";
3
+ import { resolveMediaUrl, thumbnailFor } from "./read-service.js";
3
4
  const MEDIA_ENTRY_TYPE = "media";
4
5
  const DEFAULT_LIST_LIMIT = 24;
5
6
  const MAX_LIST_LIMIT = 100;
@@ -10,10 +11,8 @@ const MEDIA_ROW_COLUMNS = {
10
11
  };
11
12
  /**
12
13
  * Server-side adapter for the `media` reference field. Storage is the
13
- * cached-object shape (`{ id, mime, filename }`) — the meta pipeline
14
- * pulls `cached` from `LookupResult` and merges it into the stored
15
- * value on every write so reads render thumbnails without an extra
16
- * resolve round-trip.
14
+ * plain media id; `hydrate` resolves ids into `MediaReference` (URL
15
+ * included) at read time through the shared meta pipeline.
17
16
  *
18
17
  * Queries:
19
18
  * - `list({ ids })`: PK lookup via `inArray(entries.id, …)` + the
@@ -33,6 +32,8 @@ const MEDIA_ROW_COLUMNS = {
33
32
  * `media.confirm` (flips to published) — referencing one would point
34
33
  * at an asset whose bytes haven't been verified.
35
34
  */
35
+ // `satisfies` keeps `hydrate`'s concrete `MediaReference` return type
36
+ // visible instead of widening to the contract's `HydratedReference`.
36
37
  export const mediaLookupAdapter = {
37
38
  async list(ctx, options) {
38
39
  const conditions = [
@@ -98,6 +99,49 @@ export const mediaLookupAdapter = {
98
99
  return null;
99
100
  return toLookupResult(row.id, row.title, meta.mime);
100
101
  },
102
+ async hydrate(ctx, options) {
103
+ const numericIds = options.ids
104
+ .map((id) => parseMediaId(id))
105
+ .filter((id) => id !== null);
106
+ if (numericIds.length === 0)
107
+ return [];
108
+ const conditions = [
109
+ eq(entries.type, MEDIA_ENTRY_TYPE),
110
+ eq(entries.status, "published"),
111
+ inArray(entries.id, numericIds),
112
+ ];
113
+ const acceptCondition = buildAcceptCondition(options.scope?.accept);
114
+ if (acceptCondition)
115
+ conditions.push(acceptCondition);
116
+ const rows = await ctx.db
117
+ .select(MEDIA_ROW_COLUMNS)
118
+ .from(entries)
119
+ .where(and(...conditions))
120
+ .limit(numericIds.length);
121
+ const parsed = rows.flatMap((row) => {
122
+ const meta = parseMediaMeta(row.meta);
123
+ return meta ? [{ row, meta }] : [];
124
+ });
125
+ // `storage.url()` can be a signing round-trip — resolve the batch
126
+ // concurrently. Same URL resolution as `buildMediaItem`
127
+ // (read-service) so a hydrated reference and `media.get` agree.
128
+ return Promise.all(parsed.map(async ({ row, meta }) => {
129
+ const url = ctx.storage
130
+ ? await resolveMediaUrl(ctx.storage, meta.storageKey, row.id, ctx.basePath)
131
+ : meta.storageKey;
132
+ return {
133
+ id: String(row.id),
134
+ title: row.title,
135
+ mime: meta.mime,
136
+ size: meta.size,
137
+ alt: meta.alt,
138
+ url,
139
+ thumbnailUrl: thumbnailFor(ctx, url, meta.mime),
140
+ width: meta.width,
141
+ height: meta.height,
142
+ };
143
+ }));
144
+ },
101
145
  };
102
146
  function parseMediaId(id) {
103
147
  if (!/^[1-9]\d{0,15}$/.test(id))
@@ -139,8 +183,7 @@ function buildAcceptCondition(accept) {
139
183
  function toLookupResult(id, title, mime) {
140
184
  // Mirror the entry adapter's `null` contract for empty/whitespace
141
185
  // titles so admin pickers render a localized "Untitled" descriptor
142
- // rather than an empty `<p>`. `cached.filename` keeps the raw value
143
- // (deletion-resilient consumers may still want the original token).
186
+ // rather than an empty `<p>`.
144
187
  const trimmedTitle = title.trim();
145
188
  const label = trimmedTitle !== "" ? trimmedTitle : null;
146
189
  return {
@@ -148,7 +191,6 @@ function toLookupResult(id, title, mime) {
148
191
  label,
149
192
  targetType: MEDIA_ENTRY_TYPE,
150
193
  subtitle: mime,
151
- cached: { mime, filename: title },
152
194
  };
153
195
  }
154
196
  //# sourceMappingURL=lookup.js.map