@coffer-org/sdk 2.1.2 → 2.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.
@@ -152,6 +152,22 @@ export declare function snippet(o: CodeOpts & {
152
152
  key?: undefined;
153
153
  value?: undefined;
154
154
  }): FieldMeta;
155
+ export declare const SOURCE_DEFAULT_MAX_BYTES = 262144;
156
+ export declare function isBinaryText(v: string): boolean;
157
+ export interface SourceOpts extends FieldCoreOpts {
158
+ label?: string;
159
+ }
160
+ export declare function source(o: SourceOpts & {
161
+ key: string;
162
+ }): FieldItem;
163
+ export declare function source(o: SourceOpts & {
164
+ value: unknown;
165
+ key?: undefined;
166
+ }): StaticEl;
167
+ export declare function source(o: SourceOpts & {
168
+ key?: undefined;
169
+ value?: undefined;
170
+ }): FieldMeta;
155
171
  export interface RatingOpts extends FieldCoreOpts {
156
172
  label?: string;
157
173
  }
@@ -242,6 +258,7 @@ export declare const presets: {
242
258
  tags: typeof tags;
243
259
  markdown: typeof markdown;
244
260
  snippet: typeof snippet;
261
+ source: typeof source;
245
262
  rating: typeof rating;
246
263
  duration: typeof duration;
247
264
  reminder: typeof reminder;
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { real, int, text, date, select, string, group, vmsg, jsonRefined, typeErr, optionalize, applyMultiple, wrapKey, normalizeOpts, } from "./fields.js";
2
+ import { real, int, text, date, select, string, group, vmsg, jsonRefined, typeErr, reqTypeErr, optionalize, applyMultiple, wrapKey, normalizeOpts, } from "./fields.js";
3
3
  export function email(raw) {
4
4
  const o = normalizeOpts(raw);
5
5
  const required = o.required ?? false;
@@ -225,6 +225,34 @@ export function snippet(raw) {
225
225
  };
226
226
  return wrapKey(norm, meta);
227
227
  }
228
+ export const SOURCE_DEFAULT_MAX_BYTES = 262_144;
229
+ export function isBinaryText(v) {
230
+ return v.includes('\x00') || v.includes('�');
231
+ }
232
+ export function source(raw) {
233
+ const o = normalizeOpts(raw);
234
+ const required = o.required ?? false;
235
+ const ext = o.ext ?? 'txt';
236
+ const maxBytes = o.maxBytes ?? SOURCE_DEFAULT_MAX_BYTES;
237
+ const s = z.string(reqTypeErr()).superRefine((v, ctx) => {
238
+ if (isBinaryText(v)) {
239
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message: vmsg('source_binary') });
240
+ return;
241
+ }
242
+ if (new TextEncoder().encode(v).length > maxBytes)
243
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message: vmsg('source_too_large', { maxBytes }) });
244
+ });
245
+ const meta = {
246
+ kind: 'source',
247
+ label: o.label ?? '',
248
+ required,
249
+ prim: 'text',
250
+ column: 'text',
251
+ hints: { ext, maxBytes, noEditControl: true },
252
+ zod: optionalize(s, required),
253
+ };
254
+ return wrapKey(o, meta);
255
+ }
228
256
  export function rating(raw) {
229
257
  const o = normalizeOpts(raw);
230
258
  const required = o.required ?? false;
@@ -328,6 +356,7 @@ export const presets = {
328
356
  tags,
329
357
  markdown,
330
358
  snippet,
359
+ source,
331
360
  rating,
332
361
  duration,
333
362
  reminder,
@@ -13,6 +13,8 @@ export interface Rules {
13
13
  unique?: string[];
14
14
  fixed?: boolean;
15
15
  exts?: string[];
16
+ ext?: string;
17
+ maxBytes?: number;
16
18
  lead?: number;
17
19
  by?: FieldItem;
18
20
  }
@@ -66,6 +68,8 @@ export type NormalizedOpts = {
66
68
  unique?: string[];
67
69
  fixed?: boolean;
68
70
  exts?: string[];
71
+ ext?: string;
72
+ maxBytes?: number;
69
73
  lead?: number;
70
74
  by?: FieldItem;
71
75
  min?: number | string;
@@ -28,6 +28,8 @@ export function normalizeOpts(rawIn) {
28
28
  unique: r.unique,
29
29
  fixed: r.fixed,
30
30
  exts: r.exts,
31
+ ext: r.ext,
32
+ maxBytes: r.maxBytes,
31
33
  lead: r.lead,
32
34
  by: r.by,
33
35
  min: r.min,
package/dist/fields.d.ts CHANGED
@@ -400,6 +400,7 @@ export declare function json(o: JsonOpts & {
400
400
  }): FieldMeta;
401
401
  export interface CheckOpts extends FieldCoreOpts {
402
402
  slots?: string[];
403
+ fields?: FieldItem[];
403
404
  }
404
405
  export declare function check(o: CheckOpts & {
405
406
  key: string;
package/dist/fields.js CHANGED
@@ -509,13 +509,30 @@ export function check(raw) {
509
509
  const required = o.required ?? false;
510
510
  const multiple = o.multiple ?? false;
511
511
  const slots = o.slots && o.slots.length > 0 ? o.slots : [''];
512
+ const contentFields = o.fields?.length
513
+ ? o.fields
514
+ : [string({ key: 'text', label: 'core.fields.text' })];
515
+ const contentKeys = new Set();
516
+ for (const f of contentFields) {
517
+ if (contentKeys.has(f.key))
518
+ throw new Error(`[field.check] duplicate content field '${f.key}'`);
519
+ if (/^check\d+$/.test(f.key))
520
+ throw new Error(`[field.check] content field '${f.key}' is reserved for checkbox slots`);
521
+ if (!multiple && (f.type.virtual || f.type.columns))
522
+ throw new Error(`[field.check] single content field '${f.key}' must be a scalar stored field`);
523
+ contentKeys.add(f.key);
524
+ }
525
+ const checkFields = slots.map((label, i) => boolean({ key: `check${i}`, label: label || 'core.fields.check' }));
512
526
  if (multiple) {
513
- const fields = slots.map((label, i) => boolean({ key: `check${i}`, label: label || 'core.fields.check' }));
514
- fields.push(string({ key: 'text', label: 'core.fields.text' }));
527
+ const fields = [...checkFields, ...contentFields];
515
528
  return group({ key: o.key, label: o.label ?? o.key, multiple: true, required: required, fields, view: { kind: 'checklist' } });
516
529
  }
517
- const shape = { text: z.string() };
518
- const columns = { text: 'text' };
530
+ const shape = {};
531
+ const columns = {};
532
+ for (const f of contentFields) {
533
+ shape[f.key] = f.type.zod;
534
+ columns[f.key] = f.type.column;
535
+ }
519
536
  slots.forEach((_, i) => {
520
537
  shape[`check${i}`] = z.boolean();
521
538
  columns[`check${i}`] = 'boolean';
@@ -536,7 +553,10 @@ export function check(raw) {
536
553
  required,
537
554
  prim: 'check',
538
555
  column: 'text',
539
- hints: { slots },
556
+ hints: {
557
+ slots,
558
+ fields: contentFields.map((f) => ({ key: f.key, ...toClient(f.type) })),
559
+ },
540
560
  columns,
541
561
  zod: optionalize(s, required),
542
562
  };
package/dist/shelf.d.ts CHANGED
@@ -24,6 +24,7 @@ export interface ShelfDef {
24
24
  icon?: string;
25
25
  claude?: string;
26
26
  standalone?: boolean;
27
+ single?: boolean;
27
28
  views?: ShelfViews;
28
29
  fields: LayoutEl[];
29
30
  }
package/dist/shelf.js CHANGED
@@ -68,7 +68,9 @@ export function defineShelf(m) {
68
68
  const dup = keys.find((k, i) => keys.indexOf(k) !== i);
69
69
  if (dup)
70
70
  throw new Error(`[shelf] ${m.library}/${m.shelf}: duplicate key '${dup}'`);
71
- if (m.standalone !== false && !m.views?.list?.fields?.length)
71
+ if (m.single && !m.claude)
72
+ throw new Error(`[shelf] ${m.library}/${m.shelf}: single shelf requires \`claude\` — the agent cannot find it otherwise`);
73
+ if (m.standalone !== false && !m.single && !m.views?.list?.fields?.length)
72
74
  console.warn(`[shelf] ${m.library}/${m.shelf}: standalone shelf without an explicit views.list`);
73
75
  return m;
74
76
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coffer-org/sdk",
3
- "version": "2.1.2",
3
+ "version": "2.2.0",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=24"