๐Ÿ“„ src/models/generated-src/Book.ts
D-OPEN SOVEREIGN

Documentation & Insights

propName -> propId (provenance column mapping)
Synchronous LRU read โ€” throws on miss (use LoadAsync to re-hydrate).
On-demand hydration from the worker after LRU eviction (SPEC ยง2.5).
1// AUTO-GENERATED by @the_library/db_schemas schemaGenerator โ€” run `pnpm generate` to rebuild.
2import { Orm, Config } from "../orm.ts";
3import { Registry, type ModelDefinition } from "../registry.ts";
4import { PatchOperation, PatchAction, LANG_ID_ENCODE, type EditInstanceFieldNumberObj, type EditInstanceFieldStringObj, type EditRelationObj } from "../binary.ts";
5import { PatchInstance } from "../patcher.ts";
6import { addRecord, getRecord, hasRecord } from "../db.ts";
7import type { uid, Lang, CommonOrmJson, SerializedCommonOrmJson, InstanceData, RecordIdentityCluster } from "../interfaces.ts";
8import { WorkerCommands, SidecarType } from "../enums.ts";
9import { ColumnType, type SidecarInput, type SidecarRelationInput, instanceSpecFromFields } from "../enriched-index.ts";
10import { encodePID, decodePID } from "../pid.ts";
11import { schemas } from "@the_library/db_schemas/schemas";
12import { commonSerializeSlim, commonSerializeExtended, commonSerializeProv } from "../serializer.ts";
13import { currentLayout } from "../versions.ts";
14import { BookMethod, CopyrightStatus, QualityScore } from "@the_library/db_schemas/enums";
15import { Person } from "./Person.ts";
16import { Tag } from "./Tag.ts";
17import { ISBNAdapter } from "@the_library/standard-adapters/isbn";
18
19const schema = schemas["Book"] as ModelDefinition;
20
21Registry.register(schema);
22
23/** propName -> propId (provenance column mapping) */
24export const BOOK_PROP_IDS: Record<string, number> = { title: 0, description: 1, originalLang: 2, ageGradeBand: 3, previewTxId: 4, safe: 5, deleted: 6, lifecycleStatus: 7, prerequisites: 8, deweyDecimal: 9, locCallNumber: 10, copyrightStatus: 11, textQuality: 12, illustrations: 13, educationQuality: 14, practicality: 15, accessibilityLevel: 16, teacherMaterials: 17, pedagogicalScaffolding: 18, clarityOfExplanation: 19, workedExamples: 20, exercisesAndAssessment: 21, selfStudyReadiness: 22, referenceApparatus: 23, safetyGuidance: 24, contentCurrency: 25, ecoRegeneration: 26, communityLiving: 27, promotesPeace: 28, businessDevelopment: 29, language: 30, downloadTxId: 31, method: 32, isAiTranslation: 33, aiEngine: 34, addedAt: 35, nbPages: 36, sizeBytes: 37, publisher: 38, publicationDate: 39, publishedAt: 40, qualityScore: 41, isbn: 42 };
25
26export interface BookSlimCols {
27    ids: Uint16Array;
28    titles: (string | null)[];
29    originalLangs: (string | null)[];
30    previewTxIds: (string | null)[];
31    ageGradeBands: Uint8Array;
32    safes: Uint8Array;
33    deleteds: Uint8Array;
34    // Default instance inlined (slim tier carries one default instance per record):
35    defaultLanguages: (string | null)[];
36    defaultDownloadTxIds: (string | null)[];
37    defaultMethod: Uint8Array;
38    defaultIsAiTranslation: Uint8Array;
39    defaultAiEngines: (string | null)[];
40    defaultAddedAt: Uint32Array;
41    defaultNbPages: Uint16Array;
42    defaultSizeBytes: Uint32Array;
43    defaultPublishers: (string | null)[];
44    defaultPublicationDates: (string | null)[];
45    defaultPublishedAts: (string | null)[];
46    defaultQualityScore: Uint8Array;
47    defaultIsbns: (string | null)[];
48}
49
50export interface BookExtendedCols {
51    ids: Uint16Array;
52    descriptions: (string | null)[];
53    prerequisites: (string | null)[];
54    deweyDecimals: (string | null)[];
55    locCallNumbers: (string | null)[];
56    copyrightStatus: Uint8Array;
57    textQuality: Uint8Array;
58    illustrations: Uint8Array;
59    educationQuality: Uint8Array;
60    practicality: Uint8Array;
61    accessibilityLevel: Uint8Array;
62    teacherMaterials: Uint8Array;
63    pedagogicalScaffolding: Uint8Array;
64    clarityOfExplanation: Uint8Array;
65    workedExamples: Uint8Array;
66    exercisesAndAssessment: Uint8Array;
67    selfStudyReadiness: Uint8Array;
68    referenceApparatus: Uint8Array;
69    safetyGuidance: Uint8Array;
70    contentCurrency: Uint8Array;
71    ecoRegeneration: Uint8Array;
72    communityLiving: Uint8Array;
73    promotesPeace: Uint8Array;
74    businessDevelopment: Uint8Array;
75    // Structured relation tails (parsed from the sidecar tail, not columnar):
76    authorIds?: number[][];
77    instances?: InstanceData[][];
78}
79
80export interface BookProvCols {
81    ids: Uint16Array;
82    fields: Record<string, Uint8Array>;
83}
84
85export interface BookInput {
86    id: number;
87    title: string | null;
88    description: string | null;
89    originalLang: string | null;
90    previewTxId: string | null;
91    ageGradeBand: number;
92    safe: boolean;
93    deleted: boolean;
94    prerequisites: string | null;
95    deweyDecimal: string | null;
96    locCallNumber: string | null;
97    copyrightStatus: CopyrightStatus;
98    textQuality: QualityScore;
99    illustrations: QualityScore;
100    educationQuality: QualityScore;
101    practicality: QualityScore;
102    accessibilityLevel: QualityScore;
103    teacherMaterials: boolean;
104    pedagogicalScaffolding: QualityScore;
105    clarityOfExplanation: QualityScore;
106    workedExamples: QualityScore;
107    exercisesAndAssessment: QualityScore;
108    selfStudyReadiness: QualityScore;
109    referenceApparatus: QualityScore;
110    safetyGuidance: QualityScore;
111    contentCurrency: QualityScore;
112    ecoRegeneration: QualityScore;
113    communityLiving: QualityScore;
114    promotesPeace: QualityScore;
115    businessDevelopment: QualityScore;
116    authorIds: number[];
117    defaultLanguage: string | null;
118    defaultDownloadTxId: string | null;
119    defaultMethod: number;
120    defaultIsAiTranslation: boolean;
121    defaultAiEngine: string | null;
122    defaultAddedAt: number;
123    defaultNbPages: number;
124    defaultSizeBytes: number;
125    defaultPublisher: string | null;
126    defaultPublicationDate: string | null;
127    defaultPublishedAt: string | null;
128    defaultQualityScore: number;
129    defaultIsbn: string | null;
130    instances: InstanceData[];
131}
132export function serializeBookSlim(records: BookInput[]): ArrayBuffer {
133    return commonSerializeSlim(schema, records, currentLayout());
134}
135export function serializeBookExtended(records: BookInput[]): ArrayBuffer {
136    return commonSerializeExtended(schema, records, currentLayout());
137}
138export function serializeBookProv(records: BookInput[]): ArrayBuffer {
139    return commonSerializeProv(schema, records);
140}
141
142export class Book extends Orm {
143    static readonly type: number = 0;
144    static readonly UID_STRATEGY = 'native-only' as const;
145    static readonly STANDARD_PRIORITY = [] as const;
146
147    static readonly serializeSlim = serializeBookSlim;
148    static readonly serializeExtended = serializeBookExtended;
149    static readonly serializeProv = serializeBookProv;
150
151    constructor(json: CommonOrmJson | SerializedCommonOrmJson, creatorUsername?: string) {
152        super(Book.type, json, creatorUsername);
153    }
154
155    /** Synchronous LRU read โ€” throws on miss (use LoadAsync to re-hydrate). */
156    static Load(id: uid, creatorUsername?: string): Book {
157        return getRecord(Book.type, id, creatorUsername) as Book;
158    }
159
160    static Has(id: uid, creatorUsername?: string): boolean {
161        return hasRecord(Book.type, id, creatorUsername);
162    }
163
164    /** On-demand hydration from the worker after LRU eviction (SPEC ยง2.5). */
165    static async LoadAsync(id: uid, creatorUsername?: string): Promise<Book> {
166        if (hasRecord(Book.type, id, creatorUsername)) {
167            return getRecord(Book.type, id, creatorUsername) as Book;
168        }
169        const thread = PatchInstance.threadInstance;
170        if (!thread) throw new Error("worker thread not initialized โ€” call PatchInstance.initThread first");
171        const res = await thread.sendRequest({
172            cmd: WorkerCommands.GetRecord,
173            payload: { objectType: Book.type, id }
174        }) as Array<{ lang: Lang, slim?: BookSlimCols, extended?: BookExtendedCols, prov?: BookProvCols }> | null;
175        if (!res || res.length === 0) throw new Error(`Book ${id} not found in worker storage`);
176        const record = NewBookWithId(id, creatorUsername);
177        for (const data of res) {
178            if (data.slim) record.mergeSlim(data.slim, 0, data.lang);
179            if (data.extended) record.mergeExtended(data.extended, 0, data.lang);
180            if (data.prov) record.mergeProv(data.prov, 0, data.lang);
181        }
182        return record;
183    }
184
185    get prerequisites(): string { return this.getProp(8); }
186    set prerequisites(value: string) { this.dispatchString(8, value); }
187    getPrerequisites(lang: Lang): string { return this.getProp(8, lang); }
188    setPrerequisites(value: string, lang: Lang) { this.dispatchString(8, value, lang); }
189
190    get deweyDecimal(): string { return this.getProp(9); }
191    set deweyDecimal(value: string) { this.dispatchString(9, value); }
192    getDeweyDecimal(lang: Lang): string { return this.getProp(9, lang); }
193    setDeweyDecimal(value: string, lang: Lang) { this.dispatchString(9, value, lang); }
194
195    get locCallNumber(): string { return this.getProp(10); }
196    set locCallNumber(value: string) { this.dispatchString(10, value); }
197    getLocCallNumber(lang: Lang): string { return this.getProp(10, lang); }
198    setLocCallNumber(value: string, lang: Lang) { this.dispatchString(10, value, lang); }
199
200    get copyrightStatus(): CopyrightStatus { return this.getProp(11) ?? 0; }
201    set copyrightStatus(value: CopyrightStatus) { this.dispatchNumber(11, value); }
202    getCopyrightStatus(lang: Lang): CopyrightStatus { return this.getProp(11, lang) ?? 0; }
203    setCopyrightStatus(value: CopyrightStatus, lang: Lang) { this.dispatchNumber(11, value, lang); }
204
205    get textQuality(): QualityScore { return this.getProp(12) ?? 0; }
206    set textQuality(value: QualityScore) { this.dispatchNumber(12, value); }
207    getTextQuality(lang: Lang): QualityScore { return this.getProp(12, lang) ?? 0; }
208    setTextQuality(value: QualityScore, lang: Lang) { this.dispatchNumber(12, value, lang); }
209
210    get illustrations(): QualityScore { return this.getProp(13) ?? 0; }
211    set illustrations(value: QualityScore) { this.dispatchNumber(13, value); }
212    getIllustrations(lang: Lang): QualityScore { return this.getProp(13, lang) ?? 0; }
213    setIllustrations(value: QualityScore, lang: Lang) { this.dispatchNumber(13, value, lang); }
214
215    get educationQuality(): QualityScore { return this.getProp(14) ?? 0; }
216    set educationQuality(value: QualityScore) { this.dispatchNumber(14, value); }
217    getEducationQuality(lang: Lang): QualityScore { return this.getProp(14, lang) ?? 0; }
218    setEducationQuality(value: QualityScore, lang: Lang) { this.dispatchNumber(14, value, lang); }
219
220    get practicality(): QualityScore { return this.getProp(15) ?? 0; }
221    set practicality(value: QualityScore) { this.dispatchNumber(15, value); }
222    getPracticality(lang: Lang): QualityScore { return this.getProp(15, lang) ?? 0; }
223    setPracticality(value: QualityScore, lang: Lang) { this.dispatchNumber(15, value, lang); }
224
225    get accessibilityLevel(): QualityScore { return this.getProp(16) ?? 0; }
226    set accessibilityLevel(value: QualityScore) { this.dispatchNumber(16, value); }
227    getAccessibilityLevel(lang: Lang): QualityScore { return this.getProp(16, lang) ?? 0; }
228    setAccessibilityLevel(value: QualityScore, lang: Lang) { this.dispatchNumber(16, value, lang); }
229
230    get teacherMaterials(): boolean { return this.getProp(17) ?? false; }
231    set teacherMaterials(value: boolean) { this.dispatchNumber(17, value ? 1 : 0); }
232    getTeacherMaterials(lang: Lang): boolean { return this.getProp(17, lang) ?? false; }
233    setTeacherMaterials(value: boolean, lang: Lang) { this.dispatchNumber(17, value ? 1 : 0, lang); }
234
235    get pedagogicalScaffolding(): QualityScore { return this.getProp(18) ?? 0; }
236    set pedagogicalScaffolding(value: QualityScore) { this.dispatchNumber(18, value); }
237    getPedagogicalScaffolding(lang: Lang): QualityScore { return this.getProp(18, lang) ?? 0; }
238    setPedagogicalScaffolding(value: QualityScore, lang: Lang) { this.dispatchNumber(18, value, lang); }
239
240    get clarityOfExplanation(): QualityScore { return this.getProp(19) ?? 0; }
241    set clarityOfExplanation(value: QualityScore) { this.dispatchNumber(19, value); }
242    getClarityOfExplanation(lang: Lang): QualityScore { return this.getProp(19, lang) ?? 0; }
243    setClarityOfExplanation(value: QualityScore, lang: Lang) { this.dispatchNumber(19, value, lang); }
244
245    get workedExamples(): QualityScore { return this.getProp(20) ?? 0; }
246    set workedExamples(value: QualityScore) { this.dispatchNumber(20, value); }
247    getWorkedExamples(lang: Lang): QualityScore { return this.getProp(20, lang) ?? 0; }
248    setWorkedExamples(value: QualityScore, lang: Lang) { this.dispatchNumber(20, value, lang); }
249
250    get exercisesAndAssessment(): QualityScore { return this.getProp(21) ?? 0; }
251    set exercisesAndAssessment(value: QualityScore) { this.dispatchNumber(21, value); }
252    getExercisesAndAssessment(lang: Lang): QualityScore { return this.getProp(21, lang) ?? 0; }
253    setExercisesAndAssessment(value: QualityScore, lang: Lang) { this.dispatchNumber(21, value, lang); }
254
255    get selfStudyReadiness(): QualityScore { return this.getProp(22) ?? 0; }
256    set selfStudyReadiness(value: QualityScore) { this.dispatchNumber(22, value); }
257    getSelfStudyReadiness(lang: Lang): QualityScore { return this.getProp(22, lang) ?? 0; }
258    setSelfStudyReadiness(value: QualityScore, lang: Lang) { this.dispatchNumber(22, value, lang); }
259
260    get referenceApparatus(): QualityScore { return this.getProp(23) ?? 0; }
261    set referenceApparatus(value: QualityScore) { this.dispatchNumber(23, value); }
262    getReferenceApparatus(lang: Lang): QualityScore { return this.getProp(23, lang) ?? 0; }
263    setReferenceApparatus(value: QualityScore, lang: Lang) { this.dispatchNumber(23, value, lang); }
264
265    get safetyGuidance(): QualityScore { return this.getProp(24) ?? 0; }
266    set safetyGuidance(value: QualityScore) { this.dispatchNumber(24, value); }
267    getSafetyGuidance(lang: Lang): QualityScore { return this.getProp(24, lang) ?? 0; }
268    setSafetyGuidance(value: QualityScore, lang: Lang) { this.dispatchNumber(24, value, lang); }
269
270    get contentCurrency(): QualityScore { return this.getProp(25) ?? 0; }
271    set contentCurrency(value: QualityScore) { this.dispatchNumber(25, value); }
272    getContentCurrency(lang: Lang): QualityScore { return this.getProp(25, lang) ?? 0; }
273    setContentCurrency(value: QualityScore, lang: Lang) { this.dispatchNumber(25, value, lang); }
274
275    get ecoRegeneration(): QualityScore { return this.getProp(26) ?? 0; }
276    set ecoRegeneration(value: QualityScore) { this.dispatchNumber(26, value); }
277    getEcoRegeneration(lang: Lang): QualityScore { return this.getProp(26, lang) ?? 0; }
278    setEcoRegeneration(value: QualityScore, lang: Lang) { this.dispatchNumber(26, value, lang); }
279
280    get communityLiving(): QualityScore { return this.getProp(27) ?? 0; }
281    set communityLiving(value: QualityScore) { this.dispatchNumber(27, value); }
282    getCommunityLiving(lang: Lang): QualityScore { return this.getProp(27, lang) ?? 0; }
283    setCommunityLiving(value: QualityScore, lang: Lang) { this.dispatchNumber(27, value, lang); }
284
285    get promotesPeace(): QualityScore { return this.getProp(28) ?? 0; }
286    set promotesPeace(value: QualityScore) { this.dispatchNumber(28, value); }
287    getPromotesPeace(lang: Lang): QualityScore { return this.getProp(28, lang) ?? 0; }
288    setPromotesPeace(value: QualityScore, lang: Lang) { this.dispatchNumber(28, value, lang); }
289
290    get businessDevelopment(): QualityScore { return this.getProp(29) ?? 0; }
291    set businessDevelopment(value: QualityScore) { this.dispatchNumber(29, value); }
292    getBusinessDevelopment(lang: Lang): QualityScore { return this.getProp(29, lang) ?? 0; }
293    setBusinessDevelopment(value: QualityScore, lang: Lang) { this.dispatchNumber(29, value, lang); }
294
295    get inTagsIds(): Array<uid> {
296        return Array.from(this._in.get(0)!.values());
297    }
298
299    get inTags(): Array<Tag> {
300        return this.inTagsIds.map(id => Tag.Load(id));
301    }
302
303    isInTag(id: uid): boolean {
304        return this._in.get(0)!.has(id);
305    }
306
307    addIntoTag(id: uid) {
308        if (this.isInTag(id)) return;
309        const op: EditRelationObj = {
310            operation: PatchOperation.EditRelation,
311            objectType: Tag.type,
312            objectId: id,
313            langId: 0,
314            relationId: 4, // canonical: Tag.books
315            action: PatchAction.Add,
316            targetId: this.id,
317        };
318        if (hasRecord(Tag.type, id)) {
319            (getRecord(Tag.type, id) as Tag).patch(op);
320        }
321        this.applyRelationRaw('in', 0, PatchAction.Add, id);
322        PatchInstance.addPatch(op);
323    }
324
325    removeFromTag(id: uid) {
326        if (!this.isInTag(id)) return;
327        const op: EditRelationObj = {
328            operation: PatchOperation.EditRelation,
329            objectType: Tag.type,
330            objectId: id,
331            langId: 0,
332            relationId: 4, // canonical: Tag.books
333            action: PatchAction.Remove,
334            targetId: this.id,
335        };
336        if (hasRecord(Tag.type, id)) {
337            (getRecord(Tag.type, id) as Tag).patch(op);
338        }
339        this.applyRelationRaw('in', 0, PatchAction.Remove, id);
340        PatchInstance.addPatch(op);
341    }
342
343    get authorsIds(): Array<uid> {
344        return Array.from(this._has.get(1)!.values());
345    }
346
347    get authors(): Array<Person> {
348        return this.authorsIds.map(id => Person.Load(id));
349    }
350
351    hasAuthor(id: uid): boolean {
352        return this._has.get(1)!.has(id);
353    }
354
355    addAuthor(id: uid) {
356        if (this.hasAuthor(id)) return;
357        this.dispatchRelation(1, PatchAction.Add, id);
358    }
359
360    removeAuthor(id: uid) {
361        if (!this.hasAuthor(id)) return;
362        this.dispatchRelation(1, PatchAction.Remove, id);
363    }
364
365    getInstances(lang: Lang = Config.userLang): BookInstance[] {
366        return this.getInstancesData(lang).map((data, i) => new BookInstance(this, lang, i, data));
367    }
368
369    getInstance(index: number, lang: Lang = Config.userLang): BookInstance | undefined {
370        return this.getInstances(lang)[index];
371    }
372
373    getDefaultInstance(lang: Lang = Config.userLang): BookInstance | undefined {
374        const instances = this.getInstances(lang);
375        return instances.find(i => i.isDefault) ?? instances[0];
376    }
377
378    // โ”€โ”€ UID methods โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
379    toPID(): string { 
380        const scope = (this.id & 0x80000000) ? 'U' : 'C';
381        return `DL:${scope}:BK:${encodePID('BK', this.id)}`; 
382    }
383    toNativePID(): string { return this.toPID(); }
384
385    static fromPID(pid: string): { wireId: number } | null {
386        try {
387            if (!pid.startsWith('DL:')) return decodePID('BK', pid);
388            const parts = pid.split(':');
389            if (parts.length >= 4 && parts[2] === 'BK') {
390                return decodePID('BK', parts[3]);
391            }
392            return null;
393        } catch {
394            return null;
395        }
396    }
397
398    // Merge methods are now implemented in Orm using the schema definition.
399
400    static async LoadInstance(pkValue: string, lang: Lang): Promise<{ record: Book; instanceIndex?: number } | null> {
401        const crosswalk = (globalThis as any).ExternalIdCrosswalk?.instance;
402        if (!crosswalk) throw new Error("ExternalIdCrosswalk not initialized โ€” bootup incomplete");
403        await crosswalk.whenReady();
404
405        // 1. Mode DERIVED
406        if (pkValue.startsWith('DL:C:DERIVED:')) {
407            const result = await crosswalk.resolveDerived(pkValue, lang);
408            if (!result) return null;
409            const record = await Book.LoadAsync(result.wireId);
410            return record ? { record, instanceIndex: result.instanceIndex } : null;
411        }
412
413        // 2. Mode N (Parent Work)
414        if (pkValue.startsWith('DL:C:')) {
415            const parsed = Book.fromPID(pkValue);
416            if (parsed) {
417                const record = await Book.LoadAsync(parsed.wireId);
418                return record ? { record } : null;
419            }
420            return null;
421        }
422
423        // 3. Mode S (Standards)
424        const colon = pkValue.indexOf(':');
425        if (colon === -1) return null;
426        const standardId = pkValue.slice(0, colon);
427        const value = pkValue.slice(colon + 1);
428        const result = await crosswalk.resolveForInstance(standardId, value, lang);
429        if (!result) return null;
430        const record = await Book.LoadAsync(result.wireId);
431        return record ? { record, instanceIndex: result.instanceIndex } : null;
432    }
433}
434
435export class BookInstance {
436    constructor(
437        public readonly parent: Book,
438        public readonly lang: string,
439        public readonly index: number,
440        private data: InstanceData,
441    ) {}
442
443    get isDefault(): boolean { return this.data['isDefault'] === true; }
444
445    get language(): string { return (this.data['language'] as string) ?? ""; }
446    set language(val: string) {
447        this.data['language'] = val;
448        const op: EditInstanceFieldStringObj = {
449            operation: PatchOperation.EditInstanceFieldString,
450            objectType: Book.type,
451            objectId: this.parent.id,
452            langId: LANG_ID_ENCODE[this.lang] ?? 0,
453            instanceIndex: this.index,
454            propertyId: 30, // language โ€” instanceField global propId
455            value: val ?? "",
456        };
457        this.parent.patch(op);
458        PatchInstance.addPatch(op);
459    }
460
461    get downloadTxId(): string { return (this.data['downloadTxId'] as string) ?? ""; }
462    set downloadTxId(val: string) {
463        this.data['downloadTxId'] = val;
464        const op: EditInstanceFieldStringObj = {
465            operation: PatchOperation.EditInstanceFieldString,
466            objectType: Book.type,
467            objectId: this.parent.id,
468            langId: LANG_ID_ENCODE[this.lang] ?? 0,
469            instanceIndex: this.index,
470            propertyId: 31, // downloadTxId โ€” instanceField global propId
471            value: val ?? "",
472        };
473        this.parent.patch(op);
474        PatchInstance.addPatch(op);
475    }
476
477    get method(): BookMethod { return (this.data['method'] as BookMethod) ?? 0; }
478    set method(val: BookMethod) {
479        this.data['method'] = val;
480        const op: EditInstanceFieldNumberObj = {
481            operation: PatchOperation.EditInstanceFieldNumber,
482            objectType: Book.type,
483            objectId: this.parent.id,
484            langId: LANG_ID_ENCODE[this.lang] ?? 0,
485            instanceIndex: this.index,
486            propertyId: 32, // method โ€” instanceField global propId
487            value: val,
488        };
489        this.parent.patch(op);
490        PatchInstance.addPatch(op);
491    }
492
493    get isAiTranslation(): boolean { return (this.data['isAiTranslation'] as boolean) ?? false; }
494    set isAiTranslation(val: boolean) {
495        this.data['isAiTranslation'] = val;
496        const op: EditInstanceFieldNumberObj = {
497            operation: PatchOperation.EditInstanceFieldNumber,
498            objectType: Book.type,
499            objectId: this.parent.id,
500            langId: LANG_ID_ENCODE[this.lang] ?? 0,
501            instanceIndex: this.index,
502            propertyId: 33, // isAiTranslation โ€” instanceField global propId
503            value: val ? 1 : 0,
504        };
505        this.parent.patch(op);
506        PatchInstance.addPatch(op);
507    }
508
509    get aiEngine(): string | null { return (this.data['aiEngine'] as string | null) ?? null; }
510    set aiEngine(val: string | null) {
511        this.data['aiEngine'] = val;
512        const op: EditInstanceFieldStringObj = {
513            operation: PatchOperation.EditInstanceFieldString,
514            objectType: Book.type,
515            objectId: this.parent.id,
516            langId: LANG_ID_ENCODE[this.lang] ?? 0,
517            instanceIndex: this.index,
518            propertyId: 34, // aiEngine โ€” instanceField global propId
519            value: val ?? "",
520        };
521        this.parent.patch(op);
522        PatchInstance.addPatch(op);
523    }
524
525    get addedAt(): number { return (this.data['addedAt'] as number) ?? 0; }
526    set addedAt(val: number) {
527        this.data['addedAt'] = val;
528        const op: EditInstanceFieldNumberObj = {
529            operation: PatchOperation.EditInstanceFieldNumber,
530            objectType: Book.type,
531            objectId: this.parent.id,
532            langId: LANG_ID_ENCODE[this.lang] ?? 0,
533            instanceIndex: this.index,
534            propertyId: 35, // addedAt โ€” instanceField global propId
535            value: val,
536        };
537        this.parent.patch(op);
538        PatchInstance.addPatch(op);
539    }
540
541    get nbPages(): number { return (this.data['nbPages'] as number) ?? 0; }
542    set nbPages(val: number) {
543        this.data['nbPages'] = val;
544        const op: EditInstanceFieldNumberObj = {
545            operation: PatchOperation.EditInstanceFieldNumber,
546            objectType: Book.type,
547            objectId: this.parent.id,
548            langId: LANG_ID_ENCODE[this.lang] ?? 0,
549            instanceIndex: this.index,
550            propertyId: 36, // nbPages โ€” instanceField global propId
551            value: val,
552        };
553        this.parent.patch(op);
554        PatchInstance.addPatch(op);
555    }
556
557    get sizeBytes(): number { return (this.data['sizeBytes'] as number) ?? 0; }
558    set sizeBytes(val: number) {
559        this.data['sizeBytes'] = val;
560        const op: EditInstanceFieldNumberObj = {
561            operation: PatchOperation.EditInstanceFieldNumber,
562            objectType: Book.type,
563            objectId: this.parent.id,
564            langId: LANG_ID_ENCODE[this.lang] ?? 0,
565            instanceIndex: this.index,
566            propertyId: 37, // sizeBytes โ€” instanceField global propId
567            value: val,
568        };
569        this.parent.patch(op);
570        PatchInstance.addPatch(op);
571    }
572
573    get publisher(): string | null { return (this.data['publisher'] as string | null) ?? null; }
574    set publisher(val: string | null) {
575        this.data['publisher'] = val;
576        const op: EditInstanceFieldStringObj = {
577            operation: PatchOperation.EditInstanceFieldString,
578            objectType: Book.type,
579            objectId: this.parent.id,
580            langId: LANG_ID_ENCODE[this.lang] ?? 0,
581            instanceIndex: this.index,
582            propertyId: 38, // publisher โ€” instanceField global propId
583            value: val ?? "",
584        };
585        this.parent.patch(op);
586        PatchInstance.addPatch(op);
587    }
588
589    get publicationDate(): string | null { return (this.data['publicationDate'] as string | null) ?? null; }
590    set publicationDate(val: string | null) {
591        this.data['publicationDate'] = val;
592        const op: EditInstanceFieldStringObj = {
593            operation: PatchOperation.EditInstanceFieldString,
594            objectType: Book.type,
595            objectId: this.parent.id,
596            langId: LANG_ID_ENCODE[this.lang] ?? 0,
597            instanceIndex: this.index,
598            propertyId: 39, // publicationDate โ€” instanceField global propId
599            value: val ?? "",
600        };
601        this.parent.patch(op);
602        PatchInstance.addPatch(op);
603    }
604
605    get publishedAt(): string | null { return (this.data['publishedAt'] as string | null) ?? null; }
606    set publishedAt(val: string | null) {
607        this.data['publishedAt'] = val;
608        const op: EditInstanceFieldStringObj = {
609            operation: PatchOperation.EditInstanceFieldString,
610            objectType: Book.type,
611            objectId: this.parent.id,
612            langId: LANG_ID_ENCODE[this.lang] ?? 0,
613            instanceIndex: this.index,
614            propertyId: 40, // publishedAt โ€” instanceField global propId
615            value: val ?? "",
616        };
617        this.parent.patch(op);
618        PatchInstance.addPatch(op);
619    }
620
621    get qualityScore(): QualityScore { return (this.data['qualityScore'] as QualityScore) ?? 0; }
622    set qualityScore(val: QualityScore) {
623        this.data['qualityScore'] = val;
624        const op: EditInstanceFieldNumberObj = {
625            operation: PatchOperation.EditInstanceFieldNumber,
626            objectType: Book.type,
627            objectId: this.parent.id,
628            langId: LANG_ID_ENCODE[this.lang] ?? 0,
629            instanceIndex: this.index,
630            propertyId: 41, // qualityScore โ€” instanceField global propId
631            value: val,
632        };
633        this.parent.patch(op);
634        PatchInstance.addPatch(op);
635    }
636
637    get isbn(): string | null { return (this.data['isbn'] as string | null) ?? null; }
638    set isbn(val: string | null) {
639        this.data['isbn'] = val;
640        const op: EditInstanceFieldStringObj = {
641            operation: PatchOperation.EditInstanceFieldString,
642            objectType: Book.type,
643            objectId: this.parent.id,
644            langId: LANG_ID_ENCODE[this.lang] ?? 0,
645            instanceIndex: this.index,
646            propertyId: 42, // isbn โ€” instanceField global propId
647            value: val ?? "",
648        };
649        this.parent.patch(op);
650        PatchInstance.addPatch(op);
651    }
652
653    derivedSfcuid(sourceSfcuid: string): string {
654        const parts = [
655            this.language ?? ''
656        ];
657        return `DL:C:DERIVED:${sourceSfcuid}:${parts.map(p => encodeURIComponent(String(p))).join(':')}`;
658    }
659    // โ”€โ”€ Inherited from isbn standard โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
660    get isbnNormalized(): string | null {
661        return this.isbn ? ISBNAdapter.normalize(this.isbn) : null;
662    }
663    validateIsbn(): ReturnType<typeof ISBNAdapter.validate> {
664        return ISBNAdapter.validate(this.isbn);
665    }
666    sfcuid(): string {
667        if (this.isbn) return `isbn:${ISBNAdapter.normalize(this.isbn)}`;
668        return this.derivedSfcuid(this.parent.toPID());
669    }
670    get identityCluster(): RecordIdentityCluster {
671        const primary = this.sfcuid();
672        const allAliases = [
673            this.isbn ? `isbn:${ISBNAdapter.normalize(this.isbn)}` : null,
674            this.parent.toPID()
675        ].filter(Boolean) as string[];
676        return {
677            sfcuid: primary,
678            native: this.parent.toPID(),
679            aliases: allAliases.filter(a => a !== primary),
680            wireId: this.parent.id,
681        };
682    }
683
684    // High-ease parent delegates
685    get description(): string {
686        return this.parent.getDescription(this.lang) ?? "";
687    }
688    set description(val: string) {
689        this.parent.setDescription(val, this.lang);
690    }
691}
692
693export const NewBookWithId = (id: uid, creatorUsername?: string): Book => {
694    if (hasRecord(Book.type, id, creatorUsername)) {
695        return getRecord(Book.type, id, creatorUsername) as Book;
696    }
697    const record = new Book(Book.createNewRequest(id), creatorUsername);
698    addRecord(Book.type, record, creatorUsername);
699    return record;
700}
701