@tryghost/metafield-types 0.1.0 → 0.1.1
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/CHANGELOG.md +7 -0
- package/package.json +2 -2
- package/build/csv.d.ts +0 -39
- package/build/csv.d.ts.map +0 -1
- package/build/csv.js +0 -119
- package/build/csv.js.map +0 -1
package/CHANGELOG.md
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tryghost/metafield-types",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Ghost member custom field types: which types exist, what each is made of, and how a value of each is validated",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ghost Foundation",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"zod": "4.
|
|
37
|
+
"zod": "4.6.5"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"build": "tsc",
|
package/build/csv.d.ts
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { type FieldType } from './index.ts';
|
|
2
|
-
import type { FieldIdentityString } from './identity.ts';
|
|
3
|
-
/** A field definition reduced to what CSV needs to know about it. */
|
|
4
|
-
export interface CsvField {
|
|
5
|
-
namespace: string;
|
|
6
|
-
key: string;
|
|
7
|
-
type: FieldType;
|
|
8
|
-
}
|
|
9
|
-
/** A column a field occupies, and which part of the field's value it holds. */
|
|
10
|
-
export interface CsvFieldColumn {
|
|
11
|
-
column: string;
|
|
12
|
-
/** The part this column holds, or null where the field's whole value is one column. */
|
|
13
|
-
subField: string | null;
|
|
14
|
-
}
|
|
15
|
-
export declare function csvColumnsForField(field: CsvField): CsvFieldColumn[];
|
|
16
|
-
export declare function isMetafieldColumn(column: string): boolean;
|
|
17
|
-
export declare function csvCellsForFields(fields: readonly CsvField[], values: Record<string, Record<string, unknown> | undefined>): Record<string, string>;
|
|
18
|
-
/**
|
|
19
|
-
* The inverse of `csvCellsForFields`: read a row's metafield cells into the values a
|
|
20
|
-
* member write takes. Only the passed fields are read, so a column naming no active
|
|
21
|
-
* field is dropped rather than erroring.
|
|
22
|
-
*
|
|
23
|
-
* `decodeCell` turns a raw cell into its value; it defaults to identity. The caller owns
|
|
24
|
-
* any de-serialization the specific file needs — the members importer passes one that
|
|
25
|
-
* strips the export's formula guard — so this stays pure vocabulary and holds no knowledge
|
|
26
|
-
* of how any particular CSV escapes its cells.
|
|
27
|
-
*
|
|
28
|
-
* A field is written only from a non-blank cell; a blank or absent cell leaves the
|
|
29
|
-
* existing value untouched, not cleared — matching how the importer keeps a blank name or
|
|
30
|
-
* note, so re-importing a partly-filled export can't wipe values a publisher didn't
|
|
31
|
-
* touch. A composite whose every cell is blank is omitted too, because the export writes
|
|
32
|
-
* "no address" and an all-blank address identically; one with any filled cell is read
|
|
33
|
-
* from its non-blank cells and validated whole (a malformed sub-field fails the row).
|
|
34
|
-
* No sub-field is required, so a row carrying only some of an address reads as only
|
|
35
|
-
* those sub-fields. A blank sub-field cell reads as no data for that sub-field, the
|
|
36
|
-
* same way a blank cell reads as no data for a whole field.
|
|
37
|
-
*/
|
|
38
|
-
export declare function fieldValuesFromCsvRow(fields: readonly CsvField[], row: Record<string, unknown>, decodeCell?: (cell: string) => string): Record<FieldIdentityString, unknown>;
|
|
39
|
-
//# sourceMappingURL=csv.d.ts.map
|
package/build/csv.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"csv.d.ts","sourceRoot":"","sources":["../src/csv.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAEzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAqBzD,qEAAqE;AACrE,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,SAAS,CAAC;CACjB;AAcD,+EAA+E;AAC/E,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,uFAAuF;IACvF,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAID,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,QAAQ,GAAG,cAAc,EAAE,CAMpE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEzD;AAeD,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,QAAQ,EAAE,EAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,GAC1D,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAoBxB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,SAAS,QAAQ,EAAE,EAC3B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,UAAU,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAuB,GACpD,MAAM,CAAC,mBAAmB,EAAE,OAAO,CAAC,CAqCtC"}
|
package/build/csv.js
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import { subFieldsOf } from './index.js';
|
|
2
|
-
import { QUALIFIER, SEPARATOR, formatIdentity } from './identity.js';
|
|
3
|
-
/**
|
|
4
|
-
* How a field's value maps onto CSV columns.
|
|
5
|
-
*
|
|
6
|
-
* CSV is flat and a value need not be, so a composite occupies one column per
|
|
7
|
-
* sub-field. That shape is derived from the type's own value schema rather than
|
|
8
|
-
* declared alongside it: the two cannot drift, and it stays correct as sub-fields
|
|
9
|
-
* are added, removed, or made optional — an optional sub-field is still a column,
|
|
10
|
-
* it just holds an empty cell more often.
|
|
11
|
-
*
|
|
12
|
-
* The same column names are the vocabulary the importer maps onto, so an exported
|
|
13
|
-
* file re-imports without the publisher remapping anything by hand.
|
|
14
|
-
*/
|
|
15
|
-
const PREFIX = `${QUALIFIER}${SEPARATOR}`;
|
|
16
|
-
function columnIdentity(field) {
|
|
17
|
-
return formatIdentity({ namespace: field.namespace, key: field.key, partPath: null });
|
|
18
|
-
}
|
|
19
|
-
function toCell(value) {
|
|
20
|
-
return value === undefined || value === null ? '' : String(value);
|
|
21
|
-
}
|
|
22
|
-
// A cell holding nothing but whitespace carries no more data than an empty one, so both
|
|
23
|
-
// read as blank. Without this a stray space is a value: it would be stored as one on a
|
|
24
|
-
// scalar, and on a composite it would make an otherwise untouched address fail its "at
|
|
25
|
-
// least one part filled in" rule and take the whole member row down with it.
|
|
26
|
-
function isBlank(cell) {
|
|
27
|
-
return cell.trim() === '';
|
|
28
|
-
}
|
|
29
|
-
// Shares csvCellsForFields' column derivation, so a field is written, read, and offered
|
|
30
|
-
// as a mapping target under one set of column names.
|
|
31
|
-
export function csvColumnsForField(field) {
|
|
32
|
-
const column = `${PREFIX}${columnIdentity(field)}`;
|
|
33
|
-
const subFields = subFieldsOf(field.type);
|
|
34
|
-
return subFields
|
|
35
|
-
? subFields.map((sub) => ({ column: `${column}${SEPARATOR}${sub}`, subField: sub }))
|
|
36
|
-
: [{ column, subField: null }];
|
|
37
|
-
}
|
|
38
|
-
export function isMetafieldColumn(column) {
|
|
39
|
-
return column === QUALIFIER || column.startsWith(PREFIX);
|
|
40
|
-
}
|
|
41
|
-
// A missing column and a column present but blank mean different things here, so
|
|
42
|
-
// presence has to be detectable. The CSV parser only ever puts strings in these
|
|
43
|
-
// cells, which makes "not a string" a reliable stand-in for "no such column".
|
|
44
|
-
function cellFor(row, field, tail) {
|
|
45
|
-
const suffix = tail === null ? '' : `${SEPARATOR}${tail}`;
|
|
46
|
-
const cell = row[`${PREFIX}${columnIdentity(field)}${suffix}`];
|
|
47
|
-
return typeof cell === 'string' ? cell : undefined;
|
|
48
|
-
}
|
|
49
|
-
export function csvCellsForFields(fields, values) {
|
|
50
|
-
const cells = {};
|
|
51
|
-
for (const field of fields) {
|
|
52
|
-
const value = values[field.namespace]?.[field.key];
|
|
53
|
-
const subFields = subFieldsOf(field.type);
|
|
54
|
-
const column = `${PREFIX}${columnIdentity(field)}`;
|
|
55
|
-
if (!subFields) {
|
|
56
|
-
cells[column] = toCell(value);
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
const composite = (value ?? {});
|
|
60
|
-
for (const sub of subFields) {
|
|
61
|
-
cells[`${column}${SEPARATOR}${sub}`] = toCell(composite[sub]);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return cells;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* The inverse of `csvCellsForFields`: read a row's metafield cells into the values a
|
|
68
|
-
* member write takes. Only the passed fields are read, so a column naming no active
|
|
69
|
-
* field is dropped rather than erroring.
|
|
70
|
-
*
|
|
71
|
-
* `decodeCell` turns a raw cell into its value; it defaults to identity. The caller owns
|
|
72
|
-
* any de-serialization the specific file needs — the members importer passes one that
|
|
73
|
-
* strips the export's formula guard — so this stays pure vocabulary and holds no knowledge
|
|
74
|
-
* of how any particular CSV escapes its cells.
|
|
75
|
-
*
|
|
76
|
-
* A field is written only from a non-blank cell; a blank or absent cell leaves the
|
|
77
|
-
* existing value untouched, not cleared — matching how the importer keeps a blank name or
|
|
78
|
-
* note, so re-importing a partly-filled export can't wipe values a publisher didn't
|
|
79
|
-
* touch. A composite whose every cell is blank is omitted too, because the export writes
|
|
80
|
-
* "no address" and an all-blank address identically; one with any filled cell is read
|
|
81
|
-
* from its non-blank cells and validated whole (a malformed sub-field fails the row).
|
|
82
|
-
* No sub-field is required, so a row carrying only some of an address reads as only
|
|
83
|
-
* those sub-fields. A blank sub-field cell reads as no data for that sub-field, the
|
|
84
|
-
* same way a blank cell reads as no data for a whole field.
|
|
85
|
-
*/
|
|
86
|
-
export function fieldValuesFromCsvRow(fields, row, decodeCell = (cell) => cell) {
|
|
87
|
-
const values = {};
|
|
88
|
-
for (const field of fields) {
|
|
89
|
-
const subFields = subFieldsOf(field.type);
|
|
90
|
-
if (!subFields) {
|
|
91
|
-
const cell = cellFor(row, field, null);
|
|
92
|
-
if (cell !== undefined) {
|
|
93
|
-
const decoded = decodeCell(cell);
|
|
94
|
-
if (!isBlank(decoded)) {
|
|
95
|
-
values[columnIdentity(field)] = decoded;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
let anyColumnPresent = false;
|
|
101
|
-
const composite = {};
|
|
102
|
-
for (const sub of subFields) {
|
|
103
|
-
const cell = cellFor(row, field, sub);
|
|
104
|
-
if (cell === undefined) {
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
anyColumnPresent = true;
|
|
108
|
-
const decoded = decodeCell(cell);
|
|
109
|
-
if (!isBlank(decoded)) {
|
|
110
|
-
composite[sub] = decoded;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
if (anyColumnPresent && Object.keys(composite).length > 0) {
|
|
114
|
-
values[columnIdentity(field)] = composite;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
return values;
|
|
118
|
-
}
|
|
119
|
-
//# sourceMappingURL=csv.js.map
|
package/build/csv.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"csv.js","sourceRoot":"","sources":["../src/csv.ts"],"names":[],"mappings":"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"}
|