@coffer-org/sdk 1.3.1 → 1.5.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/extend.d.ts CHANGED
@@ -2,8 +2,8 @@ import type { LayoutEl } from './fields.ts';
2
2
  import type { Condition } from './condition.ts';
3
3
  import { z } from 'zod';
4
4
  export interface AttachRule {
5
- vault: string;
6
- module?: string;
5
+ library: string;
6
+ shelf?: string;
7
7
  }
8
8
  export interface ExtendDef {
9
9
  id: string;
@@ -15,7 +15,7 @@ export interface ExtendDef {
15
15
  claude?: string;
16
16
  }
17
17
  export declare function defineExtend(e: ExtendDef): ExtendDef;
18
- export declare function extendMatches(e: ExtendDef, vault: string, module: string): boolean;
18
+ export declare function extendMatches(e: ExtendDef, library: string, shelf: string): boolean;
19
19
  export declare function buildExtendZodPartial(e: ExtendDef): z.ZodObject<z.ZodRawShape>;
20
20
  export declare function extendToClient(e: ExtendDef): {
21
21
  id: string;
@@ -23,5 +23,5 @@ export declare function extendToClient(e: ExtendDef): {
23
23
  icon: string | undefined;
24
24
  attachTo: AttachRule[];
25
25
  showWhen: Condition | undefined;
26
- fields: import("./module.ts").LayoutElClient[];
26
+ fields: import("./shelf.ts").LayoutElClient[];
27
27
  };
package/dist/extend.js CHANGED
@@ -1,4 +1,4 @@
1
- import { layoutToClient, fieldEntries, buildShape } from "./module.js";
1
+ import { layoutToClient, fieldEntries, buildShape } from "./shelf.js";
2
2
  import { z } from 'zod';
3
3
  export function defineExtend(e) {
4
4
  const keys = fieldEntries(e.fields).map(([k]) => k);
@@ -7,11 +7,11 @@ export function defineExtend(e) {
7
7
  throw new Error(`[extend] ${e.id}: duplicate key '${dup}'`);
8
8
  return e;
9
9
  }
10
- export function extendMatches(e, vault, module) {
10
+ export function extendMatches(e, library, shelf) {
11
11
  return e.attachTo.some((rule) => {
12
- if (rule.vault !== vault)
12
+ if (rule.library !== library)
13
13
  return false;
14
- if (rule.module && rule.module !== module)
14
+ if (rule.shelf && rule.shelf !== shelf)
15
15
  return false;
16
16
  return true;
17
17
  });
@@ -55,8 +55,8 @@ export type NormalizedOpts = {
55
55
  pick?: string[];
56
56
  option?: unknown;
57
57
  relation?: {
58
- vault: string;
59
- module: string;
58
+ library: string;
59
+ shelf: string;
60
60
  };
61
61
  fields?: unknown;
62
62
  config?: FieldConfig;
package/dist/fields.d.ts CHANGED
@@ -40,8 +40,8 @@ export interface ComputeCtx {
40
40
  settings: Record<string, unknown>;
41
41
  global: Record<string, unknown>;
42
42
  meta: {
43
- vault: string;
44
- module: string;
43
+ library: string;
44
+ shelf: string;
45
45
  };
46
46
  }
47
47
  export type ComputeFn = (ctx: ComputeCtx) => unknown;
@@ -93,7 +93,7 @@ export interface FieldClient {
93
93
  hints: Record<string, unknown>;
94
94
  options?: OptionItem[];
95
95
  relation?: {
96
- vault: string;
96
+ library: string;
97
97
  type: string;
98
98
  };
99
99
  virtual?: boolean;
@@ -185,6 +185,10 @@ export interface FieldCoreOpts {
185
185
  default?: string | number | boolean | null;
186
186
  required?: boolean | Condition;
187
187
  multiple?: boolean;
188
+ emphasis?: 'hero' | 'muted';
189
+ noLabel?: boolean;
190
+ role?: 'cover' | 'avatar';
191
+ pinned?: boolean;
188
192
  }
189
193
  export declare function wrapKey(opts: {
190
194
  key?: string;
@@ -192,6 +196,10 @@ export declare function wrapKey(opts: {
192
196
  noEditControl?: boolean;
193
197
  default?: string | number | boolean | null;
194
198
  hidden?: boolean | Condition;
199
+ emphasis?: 'hero' | 'muted';
200
+ noLabel?: boolean;
201
+ role?: 'cover' | 'avatar';
202
+ pinned?: boolean;
195
203
  }, meta: FieldMeta): FieldItem | StaticEl | FieldMeta;
196
204
  export interface FieldMeta extends FieldClient {
197
205
  column: ColumnType;
@@ -371,8 +379,8 @@ export declare function select(o: SelectOpts & {
371
379
  }): FieldMeta;
372
380
  export interface RelationOpts extends FieldCoreOpts {
373
381
  options: {
374
- vault: string;
375
- module: string;
382
+ library: string;
383
+ shelf: string;
376
384
  };
377
385
  }
378
386
  export declare function relation(o: RelationOpts & {
package/dist/fields.js CHANGED
@@ -116,6 +116,14 @@ export function wrapKey(opts, meta) {
116
116
  let m = meta;
117
117
  if (opts.noEditControl)
118
118
  m = { ...m, hints: { ...m.hints, noEditControl: true } };
119
+ if (opts.emphasis)
120
+ m = { ...m, hints: { ...m.hints, emphasis: opts.emphasis } };
121
+ if (opts.noLabel)
122
+ m = { ...m, hints: { ...m.hints, noLabel: true } };
123
+ if (opts.role)
124
+ m = { ...m, hints: { ...m.hints, role: opts.role } };
125
+ if (opts.pinned)
126
+ m = { ...m, hints: { ...m.hints, pinned: true } };
119
127
  if (opts.default !== undefined)
120
128
  m = { ...m, default: opts.default };
121
129
  if (opts.hidden !== undefined)
@@ -456,7 +464,7 @@ export function relation(raw) {
456
464
  prim: 'relation',
457
465
  column: multi ? 'text' : 'integer',
458
466
  hints: { multi, displayKey: o.displayKey ?? 'name' },
459
- relation: { vault: raw.options.vault, type: raw.options.module },
467
+ relation: { library: raw.options.library, type: raw.options.shelf },
460
468
  ...(multi && { json: true }),
461
469
  zod: optionalize(s, required),
462
470
  };
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  export * from './field-helpers.ts';
2
2
  export * from './fields.ts';
3
3
  export * from './field-presets.ts';
4
- export * from './module.ts';
5
- export * from './vault.ts';
4
+ export * from './shelf.ts';
5
+ export * from './library.ts';
6
6
  export * from './extend.ts';
7
7
  export * from './package.ts';
8
8
  export * from './plugin.ts';
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  export * from "./field-helpers.js";
2
2
  export * from "./fields.js";
3
3
  export * from "./field-presets.js";
4
- export * from "./module.js";
5
- export * from "./vault.js";
4
+ export * from "./shelf.js";
5
+ export * from "./library.js";
6
6
  export * from "./extend.js";
7
7
  export * from "./package.js";
8
8
  export * from "./plugin.js";
@@ -0,0 +1,14 @@
1
+ import type { ShelfDef } from './shelf.ts';
2
+ export interface LibraryDef {
3
+ id: string;
4
+ label: string;
5
+ icon?: string;
6
+ agent?: string;
7
+ }
8
+ export declare function defineLibrary(v: LibraryDef): LibraryDef;
9
+ export declare const DEFAULT_LIBRARY_ICON = "lucide:folder";
10
+ export declare function fallbackLibrary(id: string): LibraryDef;
11
+ export interface LibraryBundle {
12
+ meta: LibraryDef;
13
+ shelves: ShelfDef[];
14
+ }
@@ -0,0 +1,7 @@
1
+ export function defineLibrary(v) {
2
+ return v;
3
+ }
4
+ export const DEFAULT_LIBRARY_ICON = 'lucide:folder';
5
+ export function fallbackLibrary(id) {
6
+ return { id, label: id, icon: DEFAULT_LIBRARY_ICON };
7
+ }
@@ -0,0 +1,18 @@
1
+ export interface Logger {
2
+ debug(msg: string, extra?: Record<string, unknown>): void;
3
+ info(msg: string, extra?: Record<string, unknown>): void;
4
+ warn(msg: string, extra?: unknown): void;
5
+ error(msg: string, extra?: unknown): void;
6
+ }
7
+ export interface RootLogger {
8
+ child(bindings: Record<string, unknown>): RootLogger;
9
+ debug(obj: unknown, msg?: string): void;
10
+ info(obj: unknown, msg?: string): void;
11
+ warn(obj: unknown, msg?: string): void;
12
+ error(obj: unknown, msg?: string): void;
13
+ }
14
+ export declare const LOG_LEVEL: string;
15
+ export declare function parseTags(raw: string | undefined): Set<string>;
16
+ export declare function tagEnabled(tag: string, tags: Set<string>): boolean;
17
+ export declare function setRootLogger(l: RootLogger | null): void;
18
+ export declare function getLogger(tag: string): Logger;
package/dist/logger.js ADDED
@@ -0,0 +1,57 @@
1
+ const LEVEL_NUM = { trace: 10, debug: 20, info: 30, warn: 40, error: 50 };
2
+ export const LOG_LEVEL = process.env.LOG_LEVEL ?? 'info';
3
+ export function parseTags(raw) {
4
+ return new Set((raw ?? '')
5
+ .split(',')
6
+ .map((t) => t.trim())
7
+ .filter(Boolean));
8
+ }
9
+ export function tagEnabled(tag, tags) {
10
+ return tags.size === 0 || tags.has(tag);
11
+ }
12
+ const TAGS = parseTags(process.env.LOG_TAGS);
13
+ const CONSOLE_THRESHOLD = LEVEL_NUM[LOG_LEVEL] ?? LEVEL_NUM.info;
14
+ let root = null;
15
+ const children = new Map();
16
+ export function setRootLogger(l) {
17
+ root = l;
18
+ children.clear();
19
+ }
20
+ export function getLogger(tag) {
21
+ const emit = (level, msg, extra) => {
22
+ if (!tagEnabled(tag, TAGS))
23
+ return;
24
+ if (root) {
25
+ let child = children.get(tag);
26
+ if (!child) {
27
+ child = root.child({ tag });
28
+ children.set(tag, child);
29
+ }
30
+ if (extra === undefined)
31
+ child[level](msg);
32
+ else if (extra instanceof Error)
33
+ child[level]({ err: extra }, msg);
34
+ else if (typeof extra === 'object')
35
+ child[level](extra, msg);
36
+ else
37
+ child[level]({ err: extra }, msg);
38
+ return;
39
+ }
40
+ if (LEVEL_NUM[level] < CONSOLE_THRESHOLD)
41
+ return;
42
+ const line = `[${tag}] ${msg}`;
43
+ const rest = extra === undefined ? [] : [extra];
44
+ if (level === 'error')
45
+ console.error(line, ...rest);
46
+ else if (level === 'warn')
47
+ console.warn(line, ...rest);
48
+ else
49
+ console.log(line, ...rest);
50
+ };
51
+ return {
52
+ debug: (msg, extra) => emit('debug', msg, extra),
53
+ info: (msg, extra) => emit('info', msg, extra),
54
+ warn: (msg, extra) => emit('warn', msg, extra),
55
+ error: (msg, extra) => emit('error', msg, extra),
56
+ };
57
+ }
package/dist/package.d.ts CHANGED
@@ -1,13 +1,13 @@
1
- import type { VaultBundle } from './vault.ts';
2
- import type { ModuleDef } from './module.ts';
1
+ import type { LibraryBundle } from './library.ts';
2
+ import type { ShelfDef } from './shelf.ts';
3
3
  import type { ExtendDef } from './extend.ts';
4
4
  export interface ModulePackage {
5
- vaults?: VaultBundle[];
6
- vaultModules?: ModuleDef[];
5
+ libraries?: LibraryBundle[];
6
+ libraryShelves?: ShelfDef[];
7
7
  extends_?: ExtendDef[];
8
8
  }
9
9
  export declare function assemblePackages(packages: ModulePackage[]): {
10
- vaults: VaultBundle[];
11
- modules: ModuleDef[];
10
+ libraries: LibraryBundle[];
11
+ shelves: ShelfDef[];
12
12
  extends_: ExtendDef[];
13
13
  };
package/dist/package.js CHANGED
@@ -1,26 +1,26 @@
1
- import { fallbackVault } from "./vault.js";
1
+ import { fallbackLibrary } from "./library.js";
2
2
  export function assemblePackages(packages) {
3
3
  const metaMap = new Map();
4
4
  for (const pkg of packages) {
5
- for (const bundle of pkg.vaults ?? []) {
5
+ for (const bundle of pkg.libraries ?? []) {
6
6
  if (!metaMap.has(bundle.meta.id))
7
7
  metaMap.set(bundle.meta.id, { ...bundle.meta });
8
8
  }
9
9
  }
10
- const modules = [];
10
+ const shelves = [];
11
11
  for (const pkg of packages) {
12
- for (const bundle of pkg.vaults ?? [])
13
- modules.push(...bundle.modules);
14
- modules.push(...(pkg.vaultModules ?? []));
12
+ for (const bundle of pkg.libraries ?? [])
13
+ shelves.push(...bundle.shelves);
14
+ shelves.push(...(pkg.libraryShelves ?? []));
15
15
  }
16
- const ids = new Set([...metaMap.keys(), ...modules.map((m) => m.vault)]);
17
- const vaults = [...ids].map((id) => ({
18
- meta: metaMap.get(id) ?? fallbackVault(id),
19
- modules: modules.filter((m) => m.vault === id),
16
+ const ids = new Set([...metaMap.keys(), ...shelves.map((m) => m.library)]);
17
+ const libraries = [...ids].map((id) => ({
18
+ meta: metaMap.get(id) ?? fallbackLibrary(id),
19
+ shelves: shelves.filter((m) => m.library === id),
20
20
  }));
21
21
  return {
22
- vaults,
23
- modules,
22
+ libraries,
23
+ shelves,
24
24
  extends_: packages.flatMap((pkg) => pkg.extends_ ?? []),
25
25
  };
26
26
  }
package/dist/plugin.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- import type { VaultBundle } from './vault.ts';
2
- import type { ModuleDef } from './module.ts';
1
+ import type { LibraryBundle } from './library.ts';
2
+ import type { ShelfDef } from './shelf.ts';
3
3
  import type { ExtendDef } from './extend.ts';
4
4
  import type { OptionItem } from './fields.ts';
5
5
  import type { SettingsDef } from './settings.ts';
6
6
  export interface FieldContrib {
7
- vault: string;
8
- module: string;
7
+ library: string;
8
+ shelf: string;
9
9
  field: string;
10
10
  options?: OptionItem[];
11
11
  suggestions?: string[];
@@ -18,8 +18,8 @@ export interface PluginManifest {
18
18
  id: string;
19
19
  version: string;
20
20
  dependsOn: string[];
21
- vaults?: VaultBundle[];
22
- vaultModules?: ModuleDef[];
21
+ libraries?: LibraryBundle[];
22
+ libraryShelves?: ShelfDef[];
23
23
  extends_?: ExtendDef[];
24
24
  fieldContribs?: FieldContrib[];
25
25
  optionLists?: NamedOptionList[];
@@ -30,8 +30,8 @@ export declare function definePlugin(p: PluginManifest): PluginManifest;
30
30
  export declare function topoSortPlugins(plugins: PluginManifest[]): PluginManifest[];
31
31
  export interface AssembledPlugins {
32
32
  order: PluginManifest[];
33
- vaults: VaultBundle[];
34
- modules: ModuleDef[];
33
+ libraries: LibraryBundle[];
34
+ shelves: ShelfDef[];
35
35
  extends_: ExtendDef[];
36
36
  }
37
37
  export declare function assemblePlugins(plugins: PluginManifest[]): AssembledPlugins;
package/dist/plugin.js CHANGED
@@ -38,6 +38,6 @@ export function topoSortPlugins(plugins) {
38
38
  }
39
39
  export function assemblePlugins(plugins) {
40
40
  const order = topoSortPlugins(plugins);
41
- const { vaults, modules, extends_ } = assemblePackages(order);
42
- return { order, vaults, modules, extends_ };
41
+ const { libraries, shelves, extends_ } = assemblePackages(order);
42
+ return { order, libraries, shelves, extends_ };
43
43
  }
@@ -0,0 +1,74 @@
1
+ import { z } from 'zod';
2
+ import type { FieldMeta, LayoutEl, ColumnType } from './fields.ts';
3
+ import type { FieldClient, GroupEl, PseudoEl } from './fields.ts';
4
+ export interface ShelfViews {
5
+ title?: string;
6
+ inline?: string[];
7
+ table?: string[];
8
+ groupBy?: string[];
9
+ defaultGroupBy?: string;
10
+ select?: {
11
+ icon?: string;
12
+ title: string | string[];
13
+ subtitle?: string;
14
+ };
15
+ }
16
+ export interface ShelfDef {
17
+ shelf: string;
18
+ library: string;
19
+ label: string;
20
+ icon?: string;
21
+ claude?: string;
22
+ standalone?: boolean;
23
+ views?: ShelfViews;
24
+ fields: LayoutEl[];
25
+ }
26
+ export declare function fieldEntries(items: LayoutEl[]): [string, FieldMeta][];
27
+ export declare function storageColumns(key: string, field: FieldMeta): [string, ColumnType][];
28
+ export interface CollectionEntry {
29
+ key: string;
30
+ group: GroupEl;
31
+ }
32
+ export declare function collectionGroups(items: LayoutEl[]): CollectionEntry[];
33
+ export declare function fieldMap(items: LayoutEl[]): Record<string, FieldMeta>;
34
+ export declare function defineShelf(m: ShelfDef): ShelfDef;
35
+ export declare const SYSTEM_FIELDS: readonly ["id", "createdAt", "updatedAt"];
36
+ export declare function titleKey(m: ShelfDef): string;
37
+ export declare function recordTitle(m: ShelfDef, record: Record<string, unknown>): string;
38
+ export declare const isImageKind: (kind: string) => boolean;
39
+ export declare function textSearchKeys(m: ShelfDef): string[];
40
+ export declare function inlineKeys(m: ShelfDef): string[];
41
+ export declare function inlineImageKey(m: ShelfDef): string | undefined;
42
+ export declare function inlineText(m: ShelfDef, row: Record<string, unknown>): string;
43
+ export declare function tableKeys(m: ShelfDef): string[];
44
+ export declare function groupableFields(m: ShelfDef): string[];
45
+ export declare function defaultGroupField(m: ShelfDef): string | undefined;
46
+ export declare function buildShape(fields: LayoutEl[], partial: boolean): z.ZodRawShape;
47
+ export declare function buildZodObject(m: ShelfDef): z.ZodTypeAny;
48
+ export declare function buildZodObjectPartial(m: ShelfDef): z.ZodTypeAny;
49
+ export interface StaticElClient {
50
+ el: 'static';
51
+ type: FieldClient;
52
+ value: unknown;
53
+ computed?: boolean;
54
+ }
55
+ export type GroupElClient = Omit<GroupEl, 'fields'> & {
56
+ fields: ({
57
+ key: string;
58
+ type: FieldClient;
59
+ } | StaticElClient | GroupElClient | PseudoEl)[];
60
+ };
61
+ export type LayoutElClient = {
62
+ key: string;
63
+ type: FieldClient;
64
+ } | GroupElClient | StaticElClient | PseudoEl;
65
+ export declare function layoutToClient(items: LayoutEl[]): LayoutElClient[];
66
+ export declare function shelfToClient(m: ShelfDef): {
67
+ shelf: string;
68
+ library: string;
69
+ label: string;
70
+ icon: string | undefined;
71
+ views: ShelfViews | undefined;
72
+ standalone: boolean | undefined;
73
+ fields: LayoutElClient[];
74
+ };
package/dist/shelf.js ADDED
@@ -0,0 +1,212 @@
1
+ import { z } from 'zod';
2
+ import { toClient, isField, isGroup, isStatic, isCollectionGroup, isEmbeddedGroup } from "./fields.js";
3
+ import { resolveFlag } from "./condition.js";
4
+ import { parseUrl } from "./fields/url.js";
5
+ export function fieldEntries(items) {
6
+ const out = [];
7
+ for (const it of items) {
8
+ if (isField(it)) {
9
+ out.push([it.key, it.type]);
10
+ }
11
+ else if (isGroup(it)) {
12
+ if (isCollectionGroup(it))
13
+ continue;
14
+ const prefix = it.key ? `${it.key}__` : '';
15
+ for (const [sub, fm] of fieldEntries(it.fields)) {
16
+ out.push([prefix + sub, fm]);
17
+ }
18
+ }
19
+ }
20
+ return out;
21
+ }
22
+ export function storageColumns(key, field) {
23
+ if (field.columns)
24
+ return Object.entries(field.columns).map(([sub, col]) => [`${key}__${sub}`, col]);
25
+ return [[key, field.column]];
26
+ }
27
+ export function collectionGroups(items) {
28
+ const out = [];
29
+ for (const it of items) {
30
+ if (!isGroup(it))
31
+ continue;
32
+ if (isCollectionGroup(it)) {
33
+ out.push({ key: it.key, group: it });
34
+ }
35
+ else {
36
+ out.push(...collectionGroups(it.fields));
37
+ }
38
+ }
39
+ return out;
40
+ }
41
+ const mapCache = new WeakMap();
42
+ export function fieldMap(items) {
43
+ let m = mapCache.get(items);
44
+ if (!m) {
45
+ m = Object.fromEntries(fieldEntries(items));
46
+ mapCache.set(items, m);
47
+ }
48
+ return m;
49
+ }
50
+ export function defineShelf(m) {
51
+ const keys = fieldEntries(m.fields).map(([k]) => k);
52
+ const dup = keys.find((k, i) => keys.indexOf(k) !== i);
53
+ if (dup)
54
+ 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`);
57
+ return m;
58
+ }
59
+ export const SYSTEM_FIELDS = ['id', 'createdAt', 'updatedAt'];
60
+ export function titleKey(m) {
61
+ if (m.views?.title)
62
+ return m.views.title;
63
+ const fm = fieldMap(m.fields);
64
+ const inlineNonImage = m.views?.inline?.find((k) => fm[k] && !isImageKind(fm[k].kind));
65
+ if (inlineNonImage)
66
+ return inlineNonImage;
67
+ if (fm['name'])
68
+ return 'name';
69
+ const firstText = fieldEntries(m.fields).find(([, f]) => !f.virtual && isTextPrim(f.prim));
70
+ if (firstText)
71
+ return firstText[0];
72
+ return 'id';
73
+ }
74
+ export function recordTitle(m, record) {
75
+ return String(record[titleKey(m)] ?? record['id'] ?? '');
76
+ }
77
+ function realKeys(m, keys) {
78
+ const fm = fieldMap(m.fields);
79
+ return keys.filter((k) => fm[k] && !fm[k].virtual);
80
+ }
81
+ const IMAGE_KINDS = new Set(['image', 'media', 'avatar', 'cover']);
82
+ export const isImageKind = (kind) => IMAGE_KINDS.has(kind);
83
+ const isTextPrim = (p) => p === 'string' || p === 'text';
84
+ export function textSearchKeys(m) {
85
+ return fieldEntries(m.fields)
86
+ .filter(([, f]) => !f.virtual && f.kind !== 'password' && isTextPrim(f.prim))
87
+ .map(([k]) => k);
88
+ }
89
+ export function inlineKeys(m) {
90
+ const ks = m.views?.inline?.length ? realKeys(m, m.views.inline) : [];
91
+ if (ks.length)
92
+ return ks;
93
+ return [titleKey(m)];
94
+ }
95
+ export function inlineImageKey(m) {
96
+ const fm = fieldMap(m.fields);
97
+ return inlineKeys(m).find((k) => fm[k] && isImageKind(fm[k].kind));
98
+ }
99
+ export function inlineText(m, row) {
100
+ const fm = fieldMap(m.fields);
101
+ const parts = inlineKeys(m).reduce((acc, k) => {
102
+ if (!fm[k] || !isImageKind(fm[k].kind)) {
103
+ const v = row[k];
104
+ if (v != null && v !== '')
105
+ acc.push(String(v));
106
+ }
107
+ return acc;
108
+ }, []);
109
+ return parts.join(' ').trim() || recordTitle(m, row);
110
+ }
111
+ export function tableKeys(m) {
112
+ const tk = titleKey(m);
113
+ const rest = m.views?.table?.length
114
+ ? realKeys(m, m.views.table.filter((k) => k !== tk))
115
+ : [];
116
+ return [tk, ...rest];
117
+ }
118
+ const GROUPABLE_PRIMS = new Set(['select', 'checkbox', 'relation']);
119
+ export function groupableFields(m) {
120
+ if (m.views?.groupBy?.length)
121
+ return realKeys(m, m.views.groupBy);
122
+ return fieldEntries(m.fields)
123
+ .filter(([, f]) => !f.virtual && GROUPABLE_PRIMS.has(f.prim))
124
+ .map(([k]) => k);
125
+ }
126
+ export function defaultGroupField(m) {
127
+ const dg = m.views?.defaultGroupBy;
128
+ if (dg) {
129
+ const fm = fieldMap(m.fields);
130
+ if (fm[dg] && !fm[dg].virtual)
131
+ return dg;
132
+ }
133
+ return groupableFields(m)[0];
134
+ }
135
+ export function buildShape(fields, partial) {
136
+ const shape = {};
137
+ for (const it of fields) {
138
+ if (isField(it)) {
139
+ if (it.type.virtual)
140
+ continue;
141
+ shape[it.key] = partial ? it.type.zod.optional() : it.type.zod;
142
+ }
143
+ else if (isGroup(it)) {
144
+ if (isCollectionGroup(it)) {
145
+ const inner = z.object(buildShape(it.fields, partial)).passthrough();
146
+ const arr = z.array(inner);
147
+ shape[it.key] = partial ? arr.optional() : it.required ? arr : arr.optional();
148
+ }
149
+ else if (isEmbeddedGroup(it)) {
150
+ let inner = z.object(buildShape(it.fields, partial)).passthrough();
151
+ if (it.kind === 'url') {
152
+ inner = z.preprocess((v) => (typeof v === 'string' ? (parseUrl(v) ?? {}) : v), inner);
153
+ }
154
+ shape[it.key] = partial ? inner.optional() : it.required ? inner : inner.optional();
155
+ }
156
+ else {
157
+ Object.assign(shape, buildShape(it.fields, partial));
158
+ }
159
+ }
160
+ }
161
+ return shape;
162
+ }
163
+ const isEmpty = (v) => v == null || v === '' || (Array.isArray(v) && v.length === 0);
164
+ function withConditionalRequired(base, m, partial) {
165
+ return base.superRefine((data, ctx) => {
166
+ for (const [key, f] of fieldEntries(m.fields)) {
167
+ if (f.virtual)
168
+ continue;
169
+ if (key.includes('__'))
170
+ continue;
171
+ if (partial && !(key in data))
172
+ continue;
173
+ if (f.required === true)
174
+ continue;
175
+ if (!resolveFlag(f.required, data))
176
+ continue;
177
+ if (isEmpty(data[key]))
178
+ ctx.addIssue({ code: 'custom', path: [key], message: JSON.stringify({ code: 'required' }) });
179
+ }
180
+ });
181
+ }
182
+ export function buildZodObject(m) {
183
+ return withConditionalRequired(z.object(buildShape(m.fields, false)), m, false);
184
+ }
185
+ export function buildZodObjectPartial(m) {
186
+ return withConditionalRequired(z.object(buildShape(m.fields, true)), m, true);
187
+ }
188
+ function elToClient(it) {
189
+ if (isField(it))
190
+ return { key: it.key, type: toClient(it.type) };
191
+ if (isStatic(it)) {
192
+ const fn = typeof it.value === 'function';
193
+ return { el: 'static', type: toClient(it.type), value: fn ? null : it.value, computed: fn || undefined };
194
+ }
195
+ if (isGroup(it))
196
+ return { ...it, fields: it.fields.map(elToClient) };
197
+ return it;
198
+ }
199
+ export function layoutToClient(items) {
200
+ return items.map(elToClient);
201
+ }
202
+ export function shelfToClient(m) {
203
+ return {
204
+ shelf: m.shelf,
205
+ library: m.library,
206
+ label: m.label,
207
+ icon: m.icon,
208
+ views: m.views,
209
+ standalone: m.standalone,
210
+ fields: layoutToClient(m.fields),
211
+ };
212
+ }
@@ -0,0 +1 @@
1
+ export declare const USERS_SHELF: import("./shelf.ts").ShelfDef;
@@ -0,0 +1,22 @@
1
+ import { defineShelf } from "./shelf.js";
2
+ import { field } from "./fields.js";
3
+ export const USERS_SHELF = defineShelf({
4
+ shelf: 'users',
5
+ library: '',
6
+ label: 'auth.usersTitle',
7
+ icon: 'lucide:users',
8
+ views: { table: ['login', 'displayName', 'role', 'disabled'] },
9
+ fields: [
10
+ field.string({ key: 'login', label: 'auth.fieldLogin' }),
11
+ field.string({ key: 'displayName', label: 'auth.fieldDisplayName' }),
12
+ field.select({
13
+ key: 'role',
14
+ label: 'auth.fieldRole',
15
+ options: [
16
+ { value: 'admin', title: 'auth.roleAdmin' },
17
+ { value: 'member', title: 'auth.roleMember' },
18
+ ],
19
+ }),
20
+ field.boolean({ key: 'disabled', label: 'auth.fieldDisabled' }),
21
+ ],
22
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coffer-org/sdk",
3
- "version": "1.3.1",
3
+ "version": "1.5.0",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=24"