@coffer-org/sdk 1.4.0 → 1.6.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 +4 -4
- package/dist/extend.js +4 -4
- package/dist/field-presets.d.ts +5 -0
- package/dist/field-presets.js +14 -0
- package/dist/fields/normalize.d.ts +2 -2
- package/dist/fields.d.ts +13 -5
- package/dist/fields.js +9 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/library.d.ts +14 -0
- package/dist/library.js +7 -0
- package/dist/package.d.ts +6 -6
- package/dist/package.js +12 -12
- package/dist/plugin.d.ts +8 -8
- package/dist/plugin.js +2 -2
- package/dist/shelf.d.ts +74 -0
- package/dist/shelf.js +212 -0
- package/dist/users-shelf.d.ts +1 -0
- package/dist/users-shelf.js +22 -0
- package/package.json +1 -1
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
|
-
|
|
6
|
-
|
|
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,
|
|
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("./
|
|
26
|
+
fields: import("./shelf.ts").LayoutElClient[];
|
|
27
27
|
};
|
package/dist/extend.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { layoutToClient, fieldEntries, buildShape } from "./
|
|
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,
|
|
10
|
+
export function extendMatches(e, library, shelf) {
|
|
11
11
|
return e.attachTo.some((rule) => {
|
|
12
|
-
if (rule.
|
|
12
|
+
if (rule.library !== library)
|
|
13
13
|
return false;
|
|
14
|
-
if (rule.
|
|
14
|
+
if (rule.shelf && rule.shelf !== shelf)
|
|
15
15
|
return false;
|
|
16
16
|
return true;
|
|
17
17
|
});
|
package/dist/field-presets.d.ts
CHANGED
|
@@ -39,6 +39,10 @@ export declare function internalApiToken(o: {
|
|
|
39
39
|
key: string;
|
|
40
40
|
label?: string;
|
|
41
41
|
}): GroupEl;
|
|
42
|
+
export declare function internalOauthGrants(o: {
|
|
43
|
+
key: string;
|
|
44
|
+
label?: string;
|
|
45
|
+
}): GroupEl;
|
|
42
46
|
export declare function slug(o: TextPresetOpts & {
|
|
43
47
|
key: string;
|
|
44
48
|
}): FieldItem;
|
|
@@ -247,5 +251,6 @@ export declare const presets: {
|
|
|
247
251
|
dimensions: typeof dimensions;
|
|
248
252
|
country: typeof country;
|
|
249
253
|
internalApiToken: typeof internalApiToken;
|
|
254
|
+
internalOauthGrants: typeof internalOauthGrants;
|
|
250
255
|
};
|
|
251
256
|
export {};
|
package/dist/field-presets.js
CHANGED
|
@@ -65,6 +65,19 @@ export function internalApiToken(o) {
|
|
|
65
65
|
],
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
|
+
export function internalOauthGrants(o) {
|
|
69
|
+
return group({
|
|
70
|
+
key: o.key,
|
|
71
|
+
label: o.label ?? o.key,
|
|
72
|
+
multiple: true,
|
|
73
|
+
view: { kind: 'internalOauthGrants' },
|
|
74
|
+
fields: [
|
|
75
|
+
string({ key: 'clientName' }),
|
|
76
|
+
string({ key: 'createdAt' }),
|
|
77
|
+
string({ key: 'lastUsedAt' }),
|
|
78
|
+
],
|
|
79
|
+
});
|
|
80
|
+
}
|
|
68
81
|
const SLUG_RE = /^[a-z0-9-]+$/;
|
|
69
82
|
export function slug(raw) {
|
|
70
83
|
const o = normalizeOpts(raw);
|
|
@@ -324,4 +337,5 @@ export const presets = {
|
|
|
324
337
|
dimensions,
|
|
325
338
|
country,
|
|
326
339
|
internalApiToken,
|
|
340
|
+
internalOauthGrants,
|
|
327
341
|
};
|
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
|
-
|
|
44
|
-
|
|
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
|
-
|
|
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
|
-
|
|
375
|
-
|
|
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: {
|
|
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 './
|
|
5
|
-
export * from './
|
|
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 "./
|
|
5
|
-
export * from "./
|
|
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
|
+
}
|
package/dist/library.js
ADDED
package/dist/package.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
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
|
-
|
|
6
|
-
|
|
5
|
+
libraries?: LibraryBundle[];
|
|
6
|
+
libraryShelves?: ShelfDef[];
|
|
7
7
|
extends_?: ExtendDef[];
|
|
8
8
|
}
|
|
9
9
|
export declare function assemblePackages(packages: ModulePackage[]): {
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
libraries: LibraryBundle[];
|
|
11
|
+
shelves: ShelfDef[];
|
|
12
12
|
extends_: ExtendDef[];
|
|
13
13
|
};
|
package/dist/package.js
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import {
|
|
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.
|
|
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
|
|
10
|
+
const shelves = [];
|
|
11
11
|
for (const pkg of packages) {
|
|
12
|
-
for (const bundle of pkg.
|
|
13
|
-
|
|
14
|
-
|
|
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(), ...
|
|
17
|
-
const
|
|
18
|
-
meta: metaMap.get(id) ??
|
|
19
|
-
|
|
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
|
-
|
|
23
|
-
|
|
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 {
|
|
2
|
-
import type {
|
|
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
|
-
|
|
8
|
-
|
|
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
|
-
|
|
22
|
-
|
|
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
|
-
|
|
34
|
-
|
|
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 {
|
|
42
|
-
return { order,
|
|
41
|
+
const { libraries, shelves, extends_ } = assemblePackages(order);
|
|
42
|
+
return { order, libraries, shelves, extends_ };
|
|
43
43
|
}
|
package/dist/shelf.d.ts
ADDED
|
@@ -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
|
+
});
|