@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 CHANGED
@@ -94,7 +94,7 @@ export interface FieldClient {
94
94
  options?: OptionItem[];
95
95
  relation?: {
96
96
  library: string;
97
- type: string;
97
+ shelf: string;
98
98
  };
99
99
  virtual?: boolean;
100
100
  default?: string | number | boolean | null;
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, type: raw.options.shelf },
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
- if (!rowSchema.safeParse(it).success) {
815
- ctx.addIssue({ code: z.ZodIssueCode.custom, message: vmsg('file_structure') });
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
- table?: string[];
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 tableKeys(m: ShelfDef): string[];
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?.table?.length)
56
- console.warn(`[shelf] ${m.library}/${m.shelf}: standalone shelf without an explicit views.table`);
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 tableKeys(m) {
127
+ export function viewKeys(m) {
112
128
  const tk = titleKey(m);
113
- const rest = m.views?.table?.length
114
- ? realKeys(m, m.views.table.filter((k) => k !== tk))
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
- tableKeys(m).forEach(add);
168
+ viewKeys(m).forEach(add);
150
169
  groupableFields(m).forEach(add);
151
170
  inlineKeys(m).forEach(add);
152
171
  add(inlineImageKey(m));
@@ -5,7 +5,7 @@ export const USERS_SHELF = defineShelf({
5
5
  library: '',
6
6
  label: 'auth.usersTitle',
7
7
  icon: 'lucide:users',
8
- views: { table: ['login', 'displayName', 'role', 'disabled'] },
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' }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coffer-org/sdk",
3
- "version": "1.9.0",
3
+ "version": "2.1.0",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=24"