๐ 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