@coffer-org/sdk 3.0.0 → 3.2.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/condition.d.ts +9 -0
- package/dist/condition.js +44 -0
- package/dist/field-presets.js +1 -1
- package/dist/fields.d.ts +3 -0
- package/dist/fields.js +15 -1
- package/dist/plugin.d.ts +2 -0
- package/dist/showcase.d.ts +15 -0
- package/dist/showcase.js +39 -0
- package/package.json +5 -1
package/dist/condition.d.ts
CHANGED
|
@@ -17,6 +17,15 @@ export interface Condition {
|
|
|
17
17
|
$or?: Condition[];
|
|
18
18
|
}
|
|
19
19
|
export type Values = Record<string, unknown>;
|
|
20
|
+
export declare const COMPILABLE_SCALAR_OPS: Set<string>;
|
|
21
|
+
export declare const COMPILABLE_ARRAY_OPS: Set<string>;
|
|
22
|
+
export declare const COMPILABLE_MEMBER_OPS: Set<string>;
|
|
20
23
|
export declare function evalCondition(cond: Condition | undefined, values: Values): boolean;
|
|
21
24
|
export declare function resolveFlag(v: boolean | Condition | undefined, values: Values): boolean;
|
|
25
|
+
export declare function conditionKeys(cond: Condition | undefined): string[];
|
|
26
|
+
export declare function conditionOps(cond: Condition | undefined, field: string): string[];
|
|
27
|
+
export declare function visibleOptions<T extends {
|
|
28
|
+
value?: string;
|
|
29
|
+
when?: Condition;
|
|
30
|
+
}>(options: T[], values: Values, keep?: string | string[]): T[];
|
|
22
31
|
export declare function describeCondition(cond: Condition | undefined, labelOf: (field: string) => string, valueLabelOf?: (field: string, value: Scalar) => string): string[];
|
package/dist/condition.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import sift from 'sift';
|
|
2
|
+
export const COMPILABLE_SCALAR_OPS = new Set(['$eq', '$ne', '$gt', '$gte', '$lt', '$lte']);
|
|
3
|
+
export const COMPILABLE_ARRAY_OPS = new Set(['$in', '$nin']);
|
|
4
|
+
export const COMPILABLE_MEMBER_OPS = new Set(['$contains']);
|
|
2
5
|
const createEqualsOperation = sift
|
|
3
6
|
.createEqualsOperation;
|
|
4
7
|
const isScalar = (v) => typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean';
|
|
@@ -62,6 +65,47 @@ function describePredicate(label, pred, vl) {
|
|
|
62
65
|
}
|
|
63
66
|
return out;
|
|
64
67
|
}
|
|
68
|
+
export function conditionKeys(cond) {
|
|
69
|
+
if (!cond)
|
|
70
|
+
return [];
|
|
71
|
+
const out = [];
|
|
72
|
+
for (const [key, spec] of Object.entries(cond)) {
|
|
73
|
+
if (spec === undefined)
|
|
74
|
+
continue;
|
|
75
|
+
if (key === '$and' || key === '$or') {
|
|
76
|
+
for (const c of spec)
|
|
77
|
+
out.push(...conditionKeys(c));
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
out.push(key);
|
|
81
|
+
}
|
|
82
|
+
return [...new Set(out)];
|
|
83
|
+
}
|
|
84
|
+
export function conditionOps(cond, field) {
|
|
85
|
+
if (!cond)
|
|
86
|
+
return [];
|
|
87
|
+
const out = [];
|
|
88
|
+
for (const [key, spec] of Object.entries(cond)) {
|
|
89
|
+
if (spec === undefined)
|
|
90
|
+
continue;
|
|
91
|
+
if (key === '$and' || key === '$or') {
|
|
92
|
+
for (const c of spec)
|
|
93
|
+
out.push(...conditionOps(c, field));
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (key !== field)
|
|
97
|
+
continue;
|
|
98
|
+
if (typeof spec === 'object' && spec !== null && !Array.isArray(spec))
|
|
99
|
+
out.push(...Object.keys(spec));
|
|
100
|
+
else
|
|
101
|
+
out.push('$eq');
|
|
102
|
+
}
|
|
103
|
+
return [...new Set(out)];
|
|
104
|
+
}
|
|
105
|
+
export function visibleOptions(options, values, keep) {
|
|
106
|
+
const kept = keep === undefined ? undefined : new Set(Array.isArray(keep) ? keep : [keep]);
|
|
107
|
+
return options.filter((o) => (kept !== undefined && o.value !== undefined && kept.has(o.value)) || (o.when ? evalCondition(o.when, values) : true));
|
|
108
|
+
}
|
|
65
109
|
export function describeCondition(cond, labelOf, valueLabelOf) {
|
|
66
110
|
if (!cond)
|
|
67
111
|
return [];
|
package/dist/field-presets.js
CHANGED
|
@@ -375,7 +375,7 @@ export function source(raw) {
|
|
|
375
375
|
required,
|
|
376
376
|
prim: 'text',
|
|
377
377
|
column: 'text',
|
|
378
|
-
hints: { ext, maxBytes, noEditControl: true },
|
|
378
|
+
hints: { ext, maxBytes, noEditControl: true, ...(o.key ? { fileName: o.key } : {}) },
|
|
379
379
|
zod: optionalize(s, required),
|
|
380
380
|
};
|
|
381
381
|
return wrapKey(o, meta);
|
package/dist/fields.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ export interface OptionItem {
|
|
|
24
24
|
title: string;
|
|
25
25
|
icon?: OptionIcon;
|
|
26
26
|
subtitle?: string;
|
|
27
|
+
when?: Condition;
|
|
27
28
|
}
|
|
28
29
|
declare const __fieldItemBrand: unique symbol;
|
|
29
30
|
export interface FieldItem {
|
|
@@ -96,6 +97,7 @@ export interface FieldClient {
|
|
|
96
97
|
relation?: {
|
|
97
98
|
library: string;
|
|
98
99
|
shelf: string;
|
|
100
|
+
filter?: Condition;
|
|
99
101
|
};
|
|
100
102
|
virtual?: boolean;
|
|
101
103
|
default?: string | number | boolean | null;
|
|
@@ -377,6 +379,7 @@ export interface RelationOpts extends FieldCoreOpts {
|
|
|
377
379
|
options: {
|
|
378
380
|
library: string;
|
|
379
381
|
shelf: string;
|
|
382
|
+
filter?: Condition;
|
|
380
383
|
};
|
|
381
384
|
}
|
|
382
385
|
export declare function relation(o: RelationOpts & {
|
package/dist/fields.js
CHANGED
|
@@ -453,10 +453,20 @@ export function select(raw) {
|
|
|
453
453
|
const meta = applyMultiple(base, o.multiple ?? false);
|
|
454
454
|
return wrapKey(o, meta);
|
|
455
455
|
}
|
|
456
|
+
function assertFlatEqualityFilter(filter) {
|
|
457
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
458
|
+
if (key.startsWith('$'))
|
|
459
|
+
throw new Error(`[relation] filter key '${key}' is an operator — only flat equality is allowed here`);
|
|
460
|
+
if (value === undefined || (value !== null && typeof value === 'object'))
|
|
461
|
+
throw new Error(`[relation] filter key '${key}' has a non-scalar value — only flat equality is allowed here`);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
456
464
|
export function relation(raw) {
|
|
457
465
|
const o = normalizeOpts(raw);
|
|
458
466
|
const required = o.required ?? false;
|
|
459
467
|
const multi = o.multiple ?? false;
|
|
468
|
+
if (raw.options.filter)
|
|
469
|
+
assertFlatEqualityFilter(raw.options.filter);
|
|
460
470
|
let s;
|
|
461
471
|
if (multi) {
|
|
462
472
|
s = z.unknown().superRefine((raw, ctx) => {
|
|
@@ -478,7 +488,11 @@ export function relation(raw) {
|
|
|
478
488
|
prim: 'relation',
|
|
479
489
|
column: multi ? 'text' : 'integer',
|
|
480
490
|
hints: { multi, displayKey: o.displayKey ?? 'name' },
|
|
481
|
-
relation: {
|
|
491
|
+
relation: {
|
|
492
|
+
library: raw.options.library,
|
|
493
|
+
shelf: raw.options.shelf,
|
|
494
|
+
...(raw.options.filter && { filter: raw.options.filter }),
|
|
495
|
+
},
|
|
482
496
|
...(multi && { json: true }),
|
|
483
497
|
zod: optionalize(s, required),
|
|
484
498
|
};
|
package/dist/plugin.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ 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
|
+
import type { ShowcaseDef } from './showcase.ts';
|
|
6
7
|
export interface FieldContrib {
|
|
7
8
|
library: string;
|
|
8
9
|
shelf: string;
|
|
@@ -24,6 +25,7 @@ export interface PluginManifest {
|
|
|
24
25
|
fieldContribs?: FieldContrib[];
|
|
25
26
|
optionLists?: NamedOptionList[];
|
|
26
27
|
settings?: SettingsDef;
|
|
28
|
+
showcases?: ShowcaseDef[];
|
|
27
29
|
}
|
|
28
30
|
export declare const BASE_PLUGIN = "core";
|
|
29
31
|
export declare function definePlugin(p: PluginManifest): PluginManifest;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Condition } from './condition.ts';
|
|
2
|
+
export interface ShowcaseDef {
|
|
3
|
+
id: string;
|
|
4
|
+
library: string;
|
|
5
|
+
label: string;
|
|
6
|
+
icon: string;
|
|
7
|
+
source: {
|
|
8
|
+
library: string;
|
|
9
|
+
shelf: string;
|
|
10
|
+
};
|
|
11
|
+
filter: Condition;
|
|
12
|
+
columns: string[];
|
|
13
|
+
createDefaults?: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
export declare function defineShowcase(s: ShowcaseDef): ShowcaseDef;
|
package/dist/showcase.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { COMPILABLE_SCALAR_OPS, COMPILABLE_ARRAY_OPS, COMPILABLE_MEMBER_OPS } from "./condition.js";
|
|
2
|
+
const COMPILABLE_OPS = new Set([
|
|
3
|
+
...COMPILABLE_SCALAR_OPS,
|
|
4
|
+
...COMPILABLE_ARRAY_OPS,
|
|
5
|
+
...COMPILABLE_MEMBER_OPS,
|
|
6
|
+
]);
|
|
7
|
+
function assertCompilableOps(cond, id) {
|
|
8
|
+
for (const [key, spec] of Object.entries(cond)) {
|
|
9
|
+
if (spec === undefined)
|
|
10
|
+
continue;
|
|
11
|
+
if (key === '$and' || key === '$or') {
|
|
12
|
+
for (const c of spec)
|
|
13
|
+
assertCompilableOps(c, id);
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
if (typeof spec === 'object' && spec !== null) {
|
|
17
|
+
for (const op of Object.keys(spec)) {
|
|
18
|
+
if (!COMPILABLE_OPS.has(op)) {
|
|
19
|
+
throw new Error(`[showcase] ${id}: operator '${op}' on '${key}' does not compile to SQL`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function defineShowcase(s) {
|
|
26
|
+
if (!s.id)
|
|
27
|
+
throw new Error('[showcase] missing id');
|
|
28
|
+
if (!s.library)
|
|
29
|
+
throw new Error(`[showcase] ${s.id}: missing library`);
|
|
30
|
+
if (!s.source?.library || !s.source?.shelf)
|
|
31
|
+
throw new Error(`[showcase] ${s.id}: missing source library/shelf`);
|
|
32
|
+
if (!s.columns?.length)
|
|
33
|
+
throw new Error(`[showcase] ${s.id}: columns must not be empty`);
|
|
34
|
+
if (!s.filter || Object.keys(s.filter).length === 0) {
|
|
35
|
+
throw new Error(`[showcase] ${s.id}: filter must not be empty — an unfiltered showcase is the shelf itself`);
|
|
36
|
+
}
|
|
37
|
+
assertCompilableOps(s.filter, s.id);
|
|
38
|
+
return s;
|
|
39
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coffer-org/sdk",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=24"
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
"types": "./dist/derive.d.ts",
|
|
18
18
|
"default": "./dist/derive.js"
|
|
19
19
|
},
|
|
20
|
+
"./showcase": {
|
|
21
|
+
"types": "./dist/showcase.d.ts",
|
|
22
|
+
"default": "./dist/showcase.js"
|
|
23
|
+
},
|
|
20
24
|
"./*": {
|
|
21
25
|
"types": "./dist/*.d.ts",
|
|
22
26
|
"default": "./dist/*.js"
|