@holoscript/structural-biology-plugin 0.0.1

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/index.js ADDED
@@ -0,0 +1,681 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ADMET_PROPERTIES: () => ADMET_PROPERTIES,
24
+ BRIDGE_TRAITS: () => BRIDGE_TRAITS,
25
+ PLUGIN_DESCRIPTOR: () => PLUGIN_DESCRIPTOR,
26
+ STRUCTURAL_BIOLOGY_OBJECT_TYPES: () => STRUCTURAL_BIOLOGY_OBJECT_TYPES,
27
+ STRUCTURAL_BIOLOGY_TRAITS: () => STRUCTURAL_BIOLOGY_TRAITS,
28
+ VERSION: () => VERSION,
29
+ admetProvenance: () => admetProvenance,
30
+ chainHash: () => chainHash,
31
+ compileAdmet: () => compileAdmet,
32
+ compileAdmetReceiptToHolo: () => compileAdmetReceiptToHolo,
33
+ compileDocking: () => compileDocking,
34
+ compileDockingReceiptToHolo: () => compileDockingReceiptToHolo,
35
+ compileDrugDiscoveryReceiptToHolo: () => compileDrugDiscoveryReceiptToHolo,
36
+ computeDrugLikeness: () => computeDrugLikeness,
37
+ countLipinskiViolations: () => countLipinskiViolations,
38
+ createAdmetReceipt: () => createAdmetReceipt,
39
+ createDockingReceipt: () => createDockingReceipt,
40
+ createDrugDiscoveryReceipt: () => createDrugDiscoveryReceipt,
41
+ dockingProvenance: () => dockingProvenance,
42
+ register: () => register,
43
+ residueAnchor: () => residueAnchor,
44
+ runDrugDiscoveryPipeline: () => runDrugDiscoveryPipeline,
45
+ selectAdmetBackend: () => selectAdmetBackend,
46
+ selectDockingBackend: () => selectDockingBackend,
47
+ traitToConfig: () => traitToConfig,
48
+ verifyChain: () => verifyChain
49
+ });
50
+ module.exports = __toCommonJS(index_exports);
51
+
52
+ // src/docking.ts
53
+ function traitToConfig(trait) {
54
+ return {
55
+ backend: trait.backend,
56
+ receptor: trait.receptor,
57
+ ligand: trait.ligand,
58
+ centerX: trait.searchCenter.x,
59
+ centerY: trait.searchCenter.y,
60
+ centerZ: trait.searchCenter.z,
61
+ sizeX: trait.searchSize.x,
62
+ sizeY: trait.searchSize.y,
63
+ sizeZ: trait.searchSize.z,
64
+ numRuns: trait.numRuns,
65
+ scoringFunction: trait.scoringFunction
66
+ };
67
+ }
68
+ function dockingProvenance(result) {
69
+ const { fnv1a: fnv1a2 } = /* @__PURE__ */ (() => {
70
+ let h = 2166136261;
71
+ const fnv = (s) => {
72
+ h = 2166136261;
73
+ for (let i = 0; i < s.length; i++) {
74
+ h ^= s.charCodeAt(i);
75
+ h = Math.imul(h, 16777619) >>> 0;
76
+ }
77
+ return (h >>> 0).toString(16).padStart(8, "0");
78
+ };
79
+ return { fnv1a: fnv };
80
+ })();
81
+ const head = `structural-biology@0.1.0|docking|${result.backend}`;
82
+ const receptorHash = fnv1a2(result.receptorId);
83
+ const ligandHash = fnv1a2(result.ligandId);
84
+ const affinityKey = `best:${result.bestAffinity?.toFixed(2) ?? "unknown"}`;
85
+ const poseKey = `poses:${result.poses.length}`;
86
+ return fnv1a2(`${head}|r:${receptorHash}|l:${ligandHash}|${affinityKey}|${poseKey}`);
87
+ }
88
+ function selectDockingBackend(questionType) {
89
+ const q = questionType.toLowerCase();
90
+ if (q.includes("screen") || q.includes("high-throughput") || q.includes("batch") || q.includes("many")) {
91
+ return "autodock-gpu";
92
+ }
93
+ if (q.includes("cnn") || q.includes("ml") || q.includes("gnina") || q.includes("neural") || q.includes("deep learning")) {
94
+ return "gnina";
95
+ }
96
+ return "autodock-vina";
97
+ }
98
+ function compileDocking(traits, opts = {}) {
99
+ const format = opts.format ?? "pdbqt";
100
+ switch (format) {
101
+ case "pdbqt":
102
+ return compileDockingToPdbqt(traits);
103
+ case "sdf":
104
+ return compileDockingToSdf(traits);
105
+ case "csv":
106
+ return compileDockingToCsv(traits);
107
+ case "holo":
108
+ return compileDockingToHolo(traits);
109
+ default:
110
+ throw new Error(`Unsupported docking format: ${format}`);
111
+ }
112
+ }
113
+ function compileDockingToPdbqt(traits) {
114
+ const lines = ["HEADER HOLOSCRIPT DOCKING PLUGIN"];
115
+ for (const t of traits) {
116
+ if (t.trait === "auto_dock") {
117
+ lines.push(`REMARK 400 DOCKING backend=${t.backend}`);
118
+ lines.push(
119
+ `REMARK 400 SEARCH_CENTER ${t.searchCenter.x},${t.searchCenter.y},${t.searchCenter.z}`
120
+ );
121
+ lines.push(`REMARK 400 SEARCH_SIZE ${t.searchSize.x},${t.searchSize.y},${t.searchSize.z}`);
122
+ lines.push(`REMARK 400 SCORING ${t.scoringFunction ?? "vina"}`);
123
+ if (t.numRuns) lines.push(`REMARK 400 RUNS ${t.numRuns}`);
124
+ } else if (t.trait === "binding_affinity") {
125
+ lines.push(`REMARK 500 BINDING_AFFINITY best=${t.bestAffinity} kcal/mol`);
126
+ lines.push(`REMARK 500 POSES ${t.poseCount}`);
127
+ lines.push(
128
+ `REMARK 500 RMSD_RANGE ${t.bestRmsdRange[0].toFixed(3)}-${t.bestRmsdRange[1].toFixed(3)}`
129
+ );
130
+ lines.push(`REMARK 500 BACKEND ${t.backend}`);
131
+ if (t.ki_nm !== void 0) lines.push(`REMARK 500 KI ${t.ki_nm.toFixed(2)} nM`);
132
+ if (t.hitRate !== void 0)
133
+ lines.push(`REMARK 500 HIT_RATE ${(t.hitRate * 100).toFixed(1)}%`);
134
+ }
135
+ }
136
+ lines.push("END");
137
+ return lines.join("\n");
138
+ }
139
+ function compileDockingToSdf(traits) {
140
+ const lines = [];
141
+ for (const t of traits) {
142
+ if (t.trait === "binding_affinity") {
143
+ lines.push("ligand");
144
+ lines.push(" HoloScript");
145
+ lines.push("");
146
+ lines.push(" 0 0 0 0 0 0 0 0 0 0 0 V2000");
147
+ lines.push("M END");
148
+ lines.push(`> <docking.backend> (${t.backend})`);
149
+ lines.push(`${t.backend}`);
150
+ lines.push("");
151
+ lines.push(`> <docking.bestAffinity> (kcal/mol)`);
152
+ lines.push(`${t.bestAffinity}`);
153
+ lines.push("");
154
+ lines.push(`> <docking.poseCount>`);
155
+ lines.push(`${t.poseCount}`);
156
+ lines.push("");
157
+ lines.push(`> <docking.rmsdRange>`);
158
+ lines.push(`${t.bestRmsdRange[0].toFixed(3)},${t.bestRmsdRange[1].toFixed(3)}`);
159
+ lines.push("");
160
+ if (t.ki_nm !== void 0) {
161
+ lines.push(`> <docking.ki_nm>`);
162
+ lines.push(`${t.ki_nm.toFixed(2)}`);
163
+ lines.push("");
164
+ }
165
+ lines.push("$$$$");
166
+ }
167
+ }
168
+ return lines.join("\n");
169
+ }
170
+ function compileDockingToCsv(traits) {
171
+ const rows = [
172
+ "trait,backend,bestAffinity_kcal_mol,poseCount,rmsdLb,rmsdUb,ki_nm,hitRate"
173
+ ];
174
+ for (const t of traits) {
175
+ if (t.trait === "binding_affinity") {
176
+ rows.push(
177
+ `binding_affinity,${t.backend},${t.bestAffinity},${t.poseCount},${t.bestRmsdRange[0]},${t.bestRmsdRange[1]},${t.ki_nm ?? ""},${t.hitRate ?? ""}`
178
+ );
179
+ } else if (t.trait === "auto_dock") {
180
+ rows.push(
181
+ `auto_dock,${t.backend},,,${t.searchCenter.x},${t.searchCenter.y},${t.searchCenter.z},`
182
+ );
183
+ }
184
+ }
185
+ return rows.join("\n");
186
+ }
187
+ function compileDockingToHolo(traits) {
188
+ const lines = ['composition "DockingScene" {'];
189
+ for (const t of traits) {
190
+ if (t.trait === "auto_dock") {
191
+ lines.push(` object "DockingConfig" @auto_dock {`);
192
+ lines.push(` backend: "${t.backend}"`);
193
+ lines.push(
194
+ ` center: { x: ${t.searchCenter.x}, y: ${t.searchCenter.y}, z: ${t.searchCenter.z} }`
195
+ );
196
+ lines.push(` size: { x: ${t.searchSize.x}, y: ${t.searchSize.y}, z: ${t.searchSize.z} }`);
197
+ if (t.numRuns) lines.push(` numRuns: ${t.numRuns}`);
198
+ if (t.scoringFunction) lines.push(` scoringFunction: "${t.scoringFunction}"`);
199
+ lines.push(" }");
200
+ } else if (t.trait === "binding_affinity") {
201
+ lines.push(` object "BindingAffinity" @binding_affinity {`);
202
+ lines.push(` bestAffinity: ${t.bestAffinity}`);
203
+ lines.push(` poseCount: ${t.poseCount}`);
204
+ lines.push(` rmsdRange: [${t.bestRmsdRange[0]}, ${t.bestRmsdRange[1]}]`);
205
+ lines.push(` backend: "${t.backend}"`);
206
+ if (t.ki_nm !== void 0) lines.push(` ki_nm: ${t.ki_nm.toFixed(2)}`);
207
+ if (t.hitRate !== void 0) lines.push(` hitRate: ${(t.hitRate * 100).toFixed(1)}%`);
208
+ lines.push(" }");
209
+ }
210
+ }
211
+ lines.push("}");
212
+ return lines.join("\n");
213
+ }
214
+
215
+ // src/admet.ts
216
+ var ADMET_PROPERTIES = [
217
+ // Absorption
218
+ "caco2_permeability",
219
+ // Caco-2 cell permeability (logPapp)
220
+ "hia",
221
+ // Human intestinal absorption (%)
222
+ "pgp_substrate",
223
+ // P-glycoprotein substrate (binary)
224
+ "pgp_inhibitor",
225
+ // P-glycoprotein inhibitor (binary)
226
+ "bbb_permeability",
227
+ // Blood-brain barrier penetration (binary)
228
+ "cns_permeability",
229
+ // CNS permeability (binary)
230
+ // Distribution
231
+ "vdss",
232
+ // Volume of distribution at steady state (L/kg)
233
+ "fraction_unbound",
234
+ // Fraction unbound in plasma (Fu)
235
+ "ppb",
236
+ // Plasma protein binding (%)
237
+ // Metabolism
238
+ "cyp2d6_substrate",
239
+ // CYP2D6 substrate (binary)
240
+ "cyp3a4_substrate",
241
+ // CYP3A4 substrate (binary)
242
+ "cyp1a2_inhibitor",
243
+ // CYP1A2 inhibitor (binary)
244
+ "cyp2c19_inhibitor",
245
+ // CYP2C19 inhibitor (binary)
246
+ "cyp2c9_inhibitor",
247
+ // CYP2C9 inhibitor (binary)
248
+ "cyp2d6_inhibitor",
249
+ // CYP2D6 inhibitor (binary)
250
+ "cyp3a4_inhibitor",
251
+ // CYP3A4 inhibitor (binary)
252
+ "cyp2d6_inhibitor_v2",
253
+ // CYP2D6 inhibitor v2 (binary, updated model)
254
+ // Excretion
255
+ "half_life",
256
+ // Elimination half-life (hours)
257
+ "clearance",
258
+ // Hepatic clearance (mL/min/kg)
259
+ // Toxicity
260
+ "hht",
261
+ // Human hepatotoxicity (binary)
262
+ "ames",
263
+ // Ames mutagenicity (binary)
264
+ "dili",
265
+ // Drug-induced liver injury (binary)
266
+ "skin_sensitization",
267
+ // Skin sensitization (binary)
268
+ "carcinogenesis",
269
+ // Carcinogenicity (binary)
270
+ "rat_oral_ld50"
271
+ // Rat oral LD50 (mol/kg)
272
+ ];
273
+ function countLipinskiViolations(smiles) {
274
+ let violations = 0;
275
+ const heavyAtomCount = (smiles.match(/[A-Z]/g) ?? []).length;
276
+ const estimatedMW = heavyAtomCount * 13;
277
+ if (estimatedMW > 500) violations++;
278
+ const carbonCount = (smiles.match(/C/g) ?? []).length;
279
+ const polarAtomCount = (smiles.match(/[NO]/g) ?? []).length;
280
+ const estimatedLogP = carbonCount * 0.54 - polarAtomCount * 1;
281
+ if (estimatedLogP > 5) violations++;
282
+ const hbdCount = (smiles.match(/OH|NH/g) ?? []).length;
283
+ if (hbdCount > 5) violations++;
284
+ if (polarAtomCount > 10) violations++;
285
+ return violations;
286
+ }
287
+ function computeDrugLikeness(predictions, lipinskiViolations) {
288
+ let score = 1;
289
+ score -= lipinskiViolations * 0.2;
290
+ for (const p of predictions) {
291
+ if (p.property === "bbb_permeability" && p.value < 0.5) {
292
+ score -= 0.15;
293
+ }
294
+ if (p.property === "hia" && p.value < 80) {
295
+ score -= 0.1;
296
+ }
297
+ if ((p.property === "ames" || p.property === "hht" || p.property === "dili") && p.value > 0.5) {
298
+ score -= 0.25;
299
+ }
300
+ if (p.property === "half_life" && p.value >= 5 && p.value <= 24) {
301
+ score += 0.1;
302
+ }
303
+ }
304
+ return Math.max(0, Math.min(1, score));
305
+ }
306
+ function selectAdmetBackend(questionType) {
307
+ const q = questionType.toLowerCase();
308
+ if (q.includes("comprehensive") || q.includes("full") || q.includes("admetlab")) {
309
+ return "admetlab";
310
+ }
311
+ if (q.includes("offline") || q.includes("local") || q.includes("latency") || q.includes("edge")) {
312
+ return "local-ml";
313
+ }
314
+ return "rdkit";
315
+ }
316
+ function admetProvenance(result) {
317
+ let h = 2166136261;
318
+ const fnv = (s) => {
319
+ h = 2166136261;
320
+ for (let i = 0; i < s.length; i++) {
321
+ h ^= s.charCodeAt(i);
322
+ h = Math.imul(h, 16777619) >>> 0;
323
+ }
324
+ return (h >>> 0).toString(16).padStart(8, "0");
325
+ };
326
+ const head = `structural-biology@0.1.0|admet|${result.backend}`;
327
+ const smilesHash = fnv(result.smiles);
328
+ const predictionKey = `props:${result.predictions.length}`;
329
+ const scoreKey = `dl:${result.drugLikenessScore?.toFixed(2) ?? "na"}`;
330
+ const lipinskiKey = `lipinski:${result.lipinskiViolations ?? "na"}`;
331
+ return fnv(`${head}|smiles:${smilesHash}|${predictionKey}|${scoreKey}|${lipinskiKey}`);
332
+ }
333
+ function compileAdmet(traits, opts = {}) {
334
+ const format = opts.format ?? "csv";
335
+ switch (format) {
336
+ case "csv":
337
+ return compileAdmetToCsv(traits);
338
+ case "json":
339
+ return compileAdmetToJson(traits);
340
+ case "sdf":
341
+ return compileAdmetToSdf(traits);
342
+ case "holo":
343
+ return compileAdmetToHolo(traits);
344
+ default:
345
+ throw new Error(`Unsupported ADMET format: ${format}`);
346
+ }
347
+ }
348
+ function compileAdmetToCsv(traits) {
349
+ const rows = [
350
+ "trait,smiles,backend,properties,drugLikenessScore,lipinskiViolations,passes"
351
+ ];
352
+ for (const t of traits) {
353
+ if (t.trait === "admet_prediction") {
354
+ const props = t.properties?.join(";") ?? "all";
355
+ rows.push(`admet_prediction,${t.smiles},${t.backend ?? "rdkit"},${props},,`);
356
+ } else if (t.trait === "admet_result") {
357
+ rows.push(
358
+ `admet_result,${t.smiles},${t.backend},${t.propertyCount},${t.drugLikenessScore},${t.lipinskiViolations},${t.passes}`
359
+ );
360
+ }
361
+ }
362
+ return rows.join("\n");
363
+ }
364
+ function compileAdmetToJson(traits) {
365
+ return JSON.stringify(traits, null, 2);
366
+ }
367
+ function compileAdmetToSdf(traits) {
368
+ const lines = [];
369
+ for (const t of traits) {
370
+ lines.push("molecule");
371
+ lines.push(" HoloScript/ADMET");
372
+ lines.push("");
373
+ lines.push(" 0 0 0 0 0 0 0 0 0 0 0 V2000");
374
+ lines.push("M END");
375
+ if (t.trait === "admet_result") {
376
+ lines.push(`> <admet.drugLikenessScore>`);
377
+ lines.push(`${t.drugLikenessScore}`);
378
+ lines.push("");
379
+ lines.push(`> <admet.lipinskiViolations>`);
380
+ lines.push(`${t.lipinskiViolations}`);
381
+ lines.push("");
382
+ lines.push(`> <admet.passes>`);
383
+ lines.push(`${t.passes}`);
384
+ lines.push("");
385
+ }
386
+ lines.push("$$$$");
387
+ }
388
+ return lines.join("\n");
389
+ }
390
+ function compileAdmetToHolo(traits) {
391
+ const lines = ['composition "AdmetScene" {'];
392
+ for (const t of traits) {
393
+ if (t.trait === "admet_prediction") {
394
+ lines.push(` object "AdmetPrediction" @admet_prediction {`);
395
+ lines.push(` smiles: "${t.smiles}"`);
396
+ if (t.backend) lines.push(` backend: "${t.backend}"`);
397
+ if (t.properties && t.properties.length > 0) {
398
+ lines.push(` properties: [${t.properties.map((p) => `"${p}"`).join(", ")}]`);
399
+ }
400
+ lines.push(" }");
401
+ } else if (t.trait === "admet_result") {
402
+ lines.push(` object "AdmetResult" @admet_result {`);
403
+ lines.push(` smiles: "${t.smiles}"`);
404
+ lines.push(` drugLikenessScore: ${t.drugLikenessScore}`);
405
+ lines.push(` lipinskiViolations: ${t.lipinskiViolations}`);
406
+ lines.push(` propertyCount: ${t.propertyCount}`);
407
+ lines.push(` backend: "${t.backend}"`);
408
+ lines.push(` passes: ${t.passes}`);
409
+ lines.push(" }");
410
+ }
411
+ }
412
+ lines.push("}");
413
+ return lines.join("\n");
414
+ }
415
+
416
+ // src/receipt.ts
417
+ var PLUGIN_ID = "structural-biology@0.2.0";
418
+ function createDockingReceipt(config, result) {
419
+ const provenanceHash = dockingProvenance(result);
420
+ const verified = result.status === "success";
421
+ return {
422
+ receiptId: `dock-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
423
+ issuedAt: (/* @__PURE__ */ new Date()).toISOString(),
424
+ pluginId: PLUGIN_ID,
425
+ scale: "atomistic",
426
+ backend: result.backend,
427
+ config,
428
+ result,
429
+ provenanceHash,
430
+ executionTimeMs: result.executionTimeMs,
431
+ verified
432
+ };
433
+ }
434
+ function createAdmetReceipt(config, result) {
435
+ const provenanceHash = admetProvenance(result);
436
+ const verified = result.status === "success";
437
+ return {
438
+ receiptId: `admet-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
439
+ issuedAt: (/* @__PURE__ */ new Date()).toISOString(),
440
+ pluginId: PLUGIN_ID,
441
+ scale: "empirical-surrogate",
442
+ backend: result.backend,
443
+ config,
444
+ result,
445
+ provenanceHash,
446
+ executionTimeMs: result.executionTimeMs,
447
+ verified
448
+ };
449
+ }
450
+ function createDrugDiscoveryReceipt(dockingReceipt, admetReceipt) {
451
+ const compositeInput = `${dockingReceipt.provenanceHash}|${admetReceipt.provenanceHash}`;
452
+ let h = 2166136261;
453
+ for (let i = 0; i < compositeInput.length; i++) {
454
+ h ^= compositeInput.charCodeAt(i);
455
+ h = Math.imul(h, 16777619) >>> 0;
456
+ }
457
+ const compositeProvenanceHash = (h >>> 0).toString(16).padStart(8, "0");
458
+ const bestAffinity = dockingReceipt.result.bestAffinity ?? 0;
459
+ const drugLikenessScore = admetReceipt.result.drugLikenessScore ?? 0;
460
+ const lipinskiViolations = admetReceipt.result.lipinskiViolations ?? 0;
461
+ const propertyCount = admetReceipt.result.predictions.length;
462
+ const passes = drugLikenessScore > 0.5 && lipinskiViolations <= 1;
463
+ const assessment = {
464
+ bestAffinity,
465
+ drugLikenessScore,
466
+ lipinskiViolations,
467
+ propertyCount,
468
+ passes,
469
+ ki_nm: dockingReceipt.result.poses[0] ? Math.exp(dockingReceipt.result.bestAffinity * 1e3 / (1989e-6 * 298.15)) : void 0,
470
+ hitRate: dockingReceipt.result.poses.length > 0 ? dockingReceipt.result.poses.filter((p) => p.affinity <= -7).length / dockingReceipt.result.poses.length : void 0,
471
+ summary: formatAssessmentSummary(bestAffinity, drugLikenessScore, lipinskiViolations, passes)
472
+ };
473
+ return {
474
+ receiptId: `drug-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
475
+ issuedAt: (/* @__PURE__ */ new Date()).toISOString(),
476
+ pluginId: PLUGIN_ID,
477
+ docking: dockingReceipt,
478
+ admet: admetReceipt,
479
+ compositeProvenanceHash,
480
+ assessment
481
+ };
482
+ }
483
+ function formatAssessmentSummary(bestAffinity, drugLikenessScore, lipinskiViolations, passes) {
484
+ const affinityStr = bestAffinity < -7 ? `strong binding (${bestAffinity.toFixed(1)} kcal/mol)` : bestAffinity < -5 ? `moderate binding (${bestAffinity.toFixed(1)} kcal/mol)` : `weak binding (${bestAffinity.toFixed(1)} kcal/mol)`;
485
+ const drugStr = passes ? `drug-like (score=${drugLikenessScore.toFixed(2)}, ${lipinskiViolations} Lipinski violations)` : `not drug-like (score=${drugLikenessScore.toFixed(2)}, ${lipinskiViolations} Lipinski violations)`;
486
+ return `${affinityStr}, ${drugStr}`;
487
+ }
488
+ function runDrugDiscoveryPipeline(dockingConfig, dockingResult, admetConfig, admetResult) {
489
+ const dockingReceipt = createDockingReceipt(dockingConfig, dockingResult);
490
+ const admetReceipt = createAdmetReceipt(admetConfig, admetResult);
491
+ return createDrugDiscoveryReceipt(dockingReceipt, admetReceipt);
492
+ }
493
+ function compileDockingReceiptToHolo(receipt) {
494
+ const lines = [
495
+ 'composition "DockingReceipt" {',
496
+ ` meta {`,
497
+ ` receiptId: "${receipt.receiptId}"`,
498
+ ` issuedAt: "${receipt.issuedAt}"`,
499
+ ` pluginId: "${receipt.pluginId}"`,
500
+ ` scale: "${receipt.scale}"`,
501
+ ` backend: "${receipt.backend}"`,
502
+ ` provenanceHash: "${receipt.provenanceHash}"`,
503
+ ` verified: ${receipt.verified}`,
504
+ ` }`,
505
+ ` config @auto_dock {`,
506
+ ` receptor: "${receipt.config.receptor}"`,
507
+ ` ligand: "${receipt.config.ligand}"`,
508
+ ` center: { x: ${receipt.config.centerX}, y: ${receipt.config.centerY}, z: ${receipt.config.centerZ} }`,
509
+ ` size: { x: ${receipt.config.sizeX}, y: ${receipt.config.sizeY}, z: ${receipt.config.sizeZ} }`,
510
+ ` }`,
511
+ ` result @binding_affinity {`,
512
+ ` bestAffinity: ${receipt.result.bestAffinity ?? "null"}`,
513
+ ` poseCount: ${receipt.result.poses.length}`,
514
+ ` status: "${receipt.result.status}"`,
515
+ ` }`,
516
+ `}`
517
+ ];
518
+ return lines.join("\n");
519
+ }
520
+ function compileAdmetReceiptToHolo(receipt) {
521
+ const lines = [
522
+ 'composition "AdmetReceipt" {',
523
+ ` meta {`,
524
+ ` receiptId: "${receipt.receiptId}"`,
525
+ ` issuedAt: "${receipt.issuedAt}"`,
526
+ ` pluginId: "${receipt.pluginId}"`,
527
+ ` scale: "${receipt.scale}"`,
528
+ ` backend: "${receipt.backend}"`,
529
+ ` provenanceHash: "${receipt.provenanceHash}"`,
530
+ ` verified: ${receipt.verified}`,
531
+ ` }`,
532
+ ` config @admet_prediction {`,
533
+ ` smiles: "${receipt.config.smiles}"`,
534
+ ` }`,
535
+ ` result @admet_result {`,
536
+ ` drugLikenessScore: ${receipt.result.drugLikenessScore ?? "null"}`,
537
+ ` lipinskiViolations: ${receipt.result.lipinskiViolations ?? "null"}`,
538
+ ` propertyCount: ${receipt.result.predictions.length}`,
539
+ ` status: "${receipt.result.status}"`,
540
+ ` }`,
541
+ `}`
542
+ ];
543
+ return lines.join("\n");
544
+ }
545
+ function compileDrugDiscoveryReceiptToHolo(receipt) {
546
+ const lines = [
547
+ 'composition "DrugDiscoveryReceipt" {',
548
+ ` meta {`,
549
+ ` receiptId: "${receipt.receiptId}"`,
550
+ ` issuedAt: "${receipt.issuedAt}"`,
551
+ ` pluginId: "${receipt.pluginId}"`,
552
+ ` compositeProvenanceHash: "${receipt.compositeProvenanceHash}"`,
553
+ ` }`,
554
+ ` docking {`,
555
+ ` receiptId: "${receipt.docking.receiptId}"`,
556
+ ` provenanceHash: "${receipt.docking.provenanceHash}"`,
557
+ ` backend: "${receipt.docking.backend}"`,
558
+ ` bestAffinity: ${receipt.assessment.bestAffinity}`,
559
+ ` poseCount: ${receipt.docking.result.poses.length}`,
560
+ ` }`,
561
+ ` admet {`,
562
+ ` receiptId: "${receipt.admet.receiptId}"`,
563
+ ` provenanceHash: "${receipt.admet.provenanceHash}"`,
564
+ ` backend: "${receipt.admet.backend}"`,
565
+ ` drugLikenessScore: ${receipt.assessment.drugLikenessScore}`,
566
+ ` lipinskiViolations: ${receipt.assessment.lipinskiViolations}`,
567
+ ` passes: ${receipt.assessment.passes}`,
568
+ ` }`,
569
+ ` assessment {`,
570
+ ` passes: ${receipt.assessment.passes}`,
571
+ ` drugLikenessScore: ${receipt.assessment.drugLikenessScore}`,
572
+ ` bestAffinity: ${receipt.assessment.bestAffinity}`,
573
+ ` summary: "${receipt.assessment.summary}"`,
574
+ ` }`,
575
+ `}`
576
+ ];
577
+ return lines.join("\n");
578
+ }
579
+
580
+ // src/index.ts
581
+ var STRUCTURAL_BIOLOGY_OBJECT_TYPES = ["protein", "ligand", "chain"];
582
+ var STRUCTURAL_BIOLOGY_TRAITS = [
583
+ "foldable",
584
+ // protein backbone supports folding state transitions
585
+ "helix",
586
+ // alpha-helix secondary-structure annotation
587
+ "sheet",
588
+ // beta-sheet secondary-structure annotation
589
+ "residue_anchor"
590
+ // per-residue provenance anchor (chain id + index)
591
+ ];
592
+ var BRIDGE_TRAITS = [
593
+ "auto_dock",
594
+ // Automated molecular docking (core trait)
595
+ "binding_affinity",
596
+ // Binding affinity metrics (core trait)
597
+ "admet_prediction",
598
+ // ADMET property prediction (new)
599
+ "admet_result"
600
+ // ADMET prediction result (new)
601
+ ];
602
+ var PLUGIN_DESCRIPTOR = {
603
+ id: "structural-biology",
604
+ version: "0.2.0",
605
+ objectTypes: STRUCTURAL_BIOLOGY_OBJECT_TYPES,
606
+ traits: STRUCTURAL_BIOLOGY_TRAITS,
607
+ bridgeTraits: BRIDGE_TRAITS
608
+ };
609
+ function register(host) {
610
+ for (const name of STRUCTURAL_BIOLOGY_OBJECT_TYPES) {
611
+ host.registerObjectType(name, { plugin: PLUGIN_DESCRIPTOR.id });
612
+ }
613
+ for (const name of STRUCTURAL_BIOLOGY_TRAITS) {
614
+ host.registerTrait(name, { plugin: PLUGIN_DESCRIPTOR.id });
615
+ }
616
+ for (const name of BRIDGE_TRAITS) {
617
+ host.registerTrait(name, { plugin: PLUGIN_DESCRIPTOR.id });
618
+ }
619
+ return PLUGIN_DESCRIPTOR;
620
+ }
621
+ function fnv1a(s) {
622
+ let h = 2166136261;
623
+ for (let i = 0; i < s.length; i++) {
624
+ h ^= s.charCodeAt(i);
625
+ h = Math.imul(h, 16777619) >>> 0;
626
+ }
627
+ return h >>> 0;
628
+ }
629
+ function hashHex(s) {
630
+ return fnv1a(s).toString(16).padStart(8, "0");
631
+ }
632
+ function residueAnchor(residue) {
633
+ return hashHex(
634
+ `${PLUGIN_DESCRIPTOR.id}@${PLUGIN_DESCRIPTOR.version}|${residue.chain}:${residue.index}:${residue.resname}:${residue.secondary ?? "loop"}`
635
+ );
636
+ }
637
+ function chainHash(obj) {
638
+ const head = `${PLUGIN_DESCRIPTOR.id}@${PLUGIN_DESCRIPTOR.version}|${obj.type}|${obj.name}`;
639
+ if (obj.type === "protein") {
640
+ const tail = obj.residues.map(residueAnchor).join(":");
641
+ return hashHex(`${head}|${tail}|traits:${obj.traits.join(",")}`);
642
+ }
643
+ const traitsTail = `traits:${obj.traits.join(",")}`;
644
+ if (obj.type === "ligand") {
645
+ return hashHex(`${head}|smiles:${obj.smiles ?? ""}|${traitsTail}`);
646
+ }
647
+ return hashHex(`${head}|parent:${obj.parentProtein}|count:${obj.residueCount}|${traitsTail}`);
648
+ }
649
+ function verifyChain(obj, expected) {
650
+ return chainHash(obj) === expected;
651
+ }
652
+ var VERSION = "0.2.0";
653
+ // Annotate the CommonJS export names for ESM import in node:
654
+ 0 && (module.exports = {
655
+ ADMET_PROPERTIES,
656
+ BRIDGE_TRAITS,
657
+ PLUGIN_DESCRIPTOR,
658
+ STRUCTURAL_BIOLOGY_OBJECT_TYPES,
659
+ STRUCTURAL_BIOLOGY_TRAITS,
660
+ VERSION,
661
+ admetProvenance,
662
+ chainHash,
663
+ compileAdmet,
664
+ compileAdmetReceiptToHolo,
665
+ compileDocking,
666
+ compileDockingReceiptToHolo,
667
+ compileDrugDiscoveryReceiptToHolo,
668
+ computeDrugLikeness,
669
+ countLipinskiViolations,
670
+ createAdmetReceipt,
671
+ createDockingReceipt,
672
+ createDrugDiscoveryReceipt,
673
+ dockingProvenance,
674
+ register,
675
+ residueAnchor,
676
+ runDrugDiscoveryPipeline,
677
+ selectAdmetBackend,
678
+ selectDockingBackend,
679
+ traitToConfig,
680
+ verifyChain
681
+ });