@coffer-org/sdk 1.9.0 → 2.1.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/fields.d.ts +1 -1
- package/dist/fields.js +28 -8
- package/dist/shelf.d.ts +8 -2
- package/dist/shelf.js +25 -6
- package/dist/users-shelf.js +1 -1
- package/package.json +1 -1
package/dist/fields.d.ts
CHANGED
package/dist/fields.js
CHANGED
|
@@ -466,7 +466,7 @@ export function relation(raw) {
|
|
|
466
466
|
prim: 'relation',
|
|
467
467
|
column: multi ? 'text' : 'integer',
|
|
468
468
|
hints: { multi, displayKey: o.displayKey ?? 'name' },
|
|
469
|
-
relation: { library: raw.options.library,
|
|
469
|
+
relation: { library: raw.options.library, shelf: raw.options.shelf },
|
|
470
470
|
...(multi && { json: true }),
|
|
471
471
|
zod: optionalize(s, required),
|
|
472
472
|
};
|
|
@@ -789,15 +789,34 @@ export function period(raw) {
|
|
|
789
789
|
};
|
|
790
790
|
return wrapKey(o, meta);
|
|
791
791
|
}
|
|
792
|
+
const FILE_KEYS = new Set(['name', 'mime', 'size']);
|
|
793
|
+
const FILE_URL_KEYS = ['url', 'src', 'href', 'link'];
|
|
794
|
+
function fileEntryIssue(it) {
|
|
795
|
+
if (typeof it !== 'object' || it === null || Array.isArray(it))
|
|
796
|
+
return 'file_structure';
|
|
797
|
+
const rec = it;
|
|
798
|
+
if (FILE_URL_KEYS.some((k) => k in rec))
|
|
799
|
+
return 'file_remote_url';
|
|
800
|
+
const name = rec['name'];
|
|
801
|
+
if (typeof name !== 'string' || name === '')
|
|
802
|
+
return 'file_structure';
|
|
803
|
+
if (name.includes('://') || name.startsWith('//'))
|
|
804
|
+
return 'file_remote_url';
|
|
805
|
+
if (/[\\/]/.test(name) || name.includes('..'))
|
|
806
|
+
return 'file_name';
|
|
807
|
+
for (const k of Object.keys(rec))
|
|
808
|
+
if (!FILE_KEYS.has(k))
|
|
809
|
+
return 'file_unknown_key';
|
|
810
|
+
if (rec['mime'] !== undefined && typeof rec['mime'] !== 'string')
|
|
811
|
+
return 'file_structure';
|
|
812
|
+
if (rec['size'] !== undefined && typeof rec['size'] !== 'number')
|
|
813
|
+
return 'file_structure';
|
|
814
|
+
return null;
|
|
815
|
+
}
|
|
792
816
|
function makeFile(kind, raw) {
|
|
793
817
|
const o = normalizeOpts(raw);
|
|
794
818
|
const required = o.required ?? false;
|
|
795
819
|
const multiple = o.multiple ?? false;
|
|
796
|
-
const rowSchema = z.object({
|
|
797
|
-
name: z.string().min(1),
|
|
798
|
-
mime: z.string().optional(),
|
|
799
|
-
size: z.number().optional(),
|
|
800
|
-
});
|
|
801
820
|
const s = z.unknown().superRefine((raw, ctx) => {
|
|
802
821
|
if (typeof raw === 'string') {
|
|
803
822
|
try {
|
|
@@ -811,8 +830,9 @@ function makeFile(kind, raw) {
|
|
|
811
830
|
const parsed = jsonValue(raw);
|
|
812
831
|
const items = Array.isArray(parsed) ? parsed : [parsed];
|
|
813
832
|
for (const it of items) {
|
|
814
|
-
|
|
815
|
-
|
|
833
|
+
const code = fileEntryIssue(it);
|
|
834
|
+
if (code) {
|
|
835
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: vmsg(code) });
|
|
816
836
|
return;
|
|
817
837
|
}
|
|
818
838
|
}
|
package/dist/shelf.d.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import type { FieldMeta, LayoutEl, ColumnType } from './fields.ts';
|
|
3
3
|
import type { FieldClient, GroupEl, PseudoEl } from './fields.ts';
|
|
4
|
+
export interface ShelfListView {
|
|
5
|
+
kind?: string;
|
|
6
|
+
fields: string[];
|
|
7
|
+
}
|
|
4
8
|
export interface ShelfViews {
|
|
5
9
|
title?: string;
|
|
6
10
|
inline?: string[];
|
|
7
|
-
|
|
11
|
+
list?: ShelfListView;
|
|
8
12
|
groupBy?: string[];
|
|
9
13
|
defaultGroupBy?: string;
|
|
10
14
|
select?: {
|
|
@@ -25,6 +29,7 @@ export interface ShelfDef {
|
|
|
25
29
|
}
|
|
26
30
|
export declare function fieldEntries(items: LayoutEl[]): [string, FieldMeta][];
|
|
27
31
|
export declare function storageColumns(key: string, field: FieldMeta): [string, ColumnType][];
|
|
32
|
+
export declare function cellValue(field: FieldMeta, key: string, row: Record<string, unknown>): unknown;
|
|
28
33
|
export interface CollectionEntry {
|
|
29
34
|
key: string;
|
|
30
35
|
group: GroupEl;
|
|
@@ -40,7 +45,8 @@ export declare function textSearchKeys(m: ShelfDef): string[];
|
|
|
40
45
|
export declare function inlineKeys(m: ShelfDef): string[];
|
|
41
46
|
export declare function inlineImageKey(m: ShelfDef): string | undefined;
|
|
42
47
|
export declare function inlineText(m: ShelfDef, row: Record<string, unknown>): string;
|
|
43
|
-
export declare function
|
|
48
|
+
export declare function viewKeys(m: ShelfDef): string[];
|
|
49
|
+
export declare function listKind(m: ShelfDef): string;
|
|
44
50
|
export declare function groupableFields(m: ShelfDef): string[];
|
|
45
51
|
export declare function defaultGroupField(m: ShelfDef): string | undefined;
|
|
46
52
|
export declare function coverKey(m: ShelfDef): string | undefined;
|
package/dist/shelf.js
CHANGED
|
@@ -24,6 +24,22 @@ export function storageColumns(key, field) {
|
|
|
24
24
|
return Object.entries(field.columns).map(([sub, col]) => [`${key}__${sub}`, col]);
|
|
25
25
|
return [[key, field.column]];
|
|
26
26
|
}
|
|
27
|
+
export function cellValue(field, key, row) {
|
|
28
|
+
if (!field.columns)
|
|
29
|
+
return row[key];
|
|
30
|
+
if (row[key] !== undefined)
|
|
31
|
+
return row[key];
|
|
32
|
+
const out = {};
|
|
33
|
+
let filled = false;
|
|
34
|
+
for (const sub of Object.keys(field.columns)) {
|
|
35
|
+
const v = row[`${key}__${sub}`];
|
|
36
|
+
if (v == null || v === '')
|
|
37
|
+
continue;
|
|
38
|
+
out[sub] = v;
|
|
39
|
+
filled = true;
|
|
40
|
+
}
|
|
41
|
+
return filled ? out : undefined;
|
|
42
|
+
}
|
|
27
43
|
export function collectionGroups(items) {
|
|
28
44
|
const out = [];
|
|
29
45
|
for (const it of items) {
|
|
@@ -52,8 +68,8 @@ export function defineShelf(m) {
|
|
|
52
68
|
const dup = keys.find((k, i) => keys.indexOf(k) !== i);
|
|
53
69
|
if (dup)
|
|
54
70
|
throw new Error(`[shelf] ${m.library}/${m.shelf}: duplicate key '${dup}'`);
|
|
55
|
-
if (m.standalone !== false && !m.views?.
|
|
56
|
-
console.warn(`[shelf] ${m.library}/${m.shelf}: standalone shelf without an explicit views.
|
|
71
|
+
if (m.standalone !== false && !m.views?.list?.fields?.length)
|
|
72
|
+
console.warn(`[shelf] ${m.library}/${m.shelf}: standalone shelf without an explicit views.list`);
|
|
57
73
|
return m;
|
|
58
74
|
}
|
|
59
75
|
export const SYSTEM_FIELDS = ['id', 'createdAt', 'updatedAt'];
|
|
@@ -108,13 +124,16 @@ export function inlineText(m, row) {
|
|
|
108
124
|
}, []);
|
|
109
125
|
return parts.join(' ').trim() || recordTitle(m, row);
|
|
110
126
|
}
|
|
111
|
-
export function
|
|
127
|
+
export function viewKeys(m) {
|
|
112
128
|
const tk = titleKey(m);
|
|
113
|
-
const rest = m.views?.
|
|
114
|
-
? realKeys(m, m.views.
|
|
129
|
+
const rest = m.views?.list?.fields?.length
|
|
130
|
+
? realKeys(m, m.views.list.fields.filter((k) => k !== tk))
|
|
115
131
|
: [];
|
|
116
132
|
return [tk, ...rest];
|
|
117
133
|
}
|
|
134
|
+
export function listKind(m) {
|
|
135
|
+
return m.views?.list?.kind ?? 'table';
|
|
136
|
+
}
|
|
118
137
|
const GROUPABLE_PRIMS = new Set(['select', 'checkbox', 'relation']);
|
|
119
138
|
export function groupableFields(m) {
|
|
120
139
|
if (m.views?.groupBy?.length)
|
|
@@ -146,7 +165,7 @@ export function listKeys(m) {
|
|
|
146
165
|
out.push(k);
|
|
147
166
|
};
|
|
148
167
|
add(titleKey(m));
|
|
149
|
-
|
|
168
|
+
viewKeys(m).forEach(add);
|
|
150
169
|
groupableFields(m).forEach(add);
|
|
151
170
|
inlineKeys(m).forEach(add);
|
|
152
171
|
add(inlineImageKey(m));
|
package/dist/users-shelf.js
CHANGED
|
@@ -5,7 +5,7 @@ export const USERS_SHELF = defineShelf({
|
|
|
5
5
|
library: '',
|
|
6
6
|
label: 'auth.usersTitle',
|
|
7
7
|
icon: 'lucide:users',
|
|
8
|
-
views: {
|
|
8
|
+
views: { list: { fields: ['login', 'displayName', 'role', 'disabled'] } },
|
|
9
9
|
fields: [
|
|
10
10
|
field.string({ key: 'login', label: 'auth.fieldLogin' }),
|
|
11
11
|
field.string({ key: 'displayName', label: 'auth.fieldDisplayName' }),
|