diff --git a/adapters/javascript/README.md b/adapters/javascript/README.md new file mode 100644 index 0000000..fd776ec --- /dev/null +++ b/adapters/javascript/README.md @@ -0,0 +1,27 @@ +# lh-standard-adapter (JS/TS) + +LongHun Standard Adapter — v∞ DNA traceability + seven-factor behavioral audit for npm. + +## Install +```bash +npm install lh-standard-adapter +``` + +## Usage +```js +import { LongHunAdapter } from 'lh-standard-adapter'; + +const adapter = new LongHunAdapter({ uid: '9622', device: 'HM-9622-001' }); +const wrapped = adapter.wrap({ code: "console.log('hello')" }, 'code', 'P04-Luban'); +const result = adapter.validate(wrapped); +console.log(result.summary); // ✅ VALID +``` + +## API +- `new LongHunAdapter(options?)` — uid, device, locale +- `.wrap(data, taskType?, persona?, action?, version?)` → WrappedPayload +- `.validate(wrapped)` → ValidationResult +- `.getSchemas()` → { dnaSchema, auditSchema } + +## License +CC-BY-NC-SA 4.0 — LongHun Core · UID9622 diff --git a/adapters/javascript/index.d.ts b/adapters/javascript/index.d.ts new file mode 100644 index 0000000..2f91e53 --- /dev/null +++ b/adapters/javascript/index.d.ts @@ -0,0 +1,41 @@ +declare module 'lh-standard-adapter' { + export interface LongHunAdapterOptions { uid?: string; device?: string; locale?: string; } + export interface WrappedPayload { dna: string; audit: AuditObject; payload: any; meta: MetaObject; } + export interface AuditObject { + audit_version: string; uid: string; persona?: string; task_type?: string; + behavior_signature: BehaviorSignature; behavior_pattern: string; + behavior_labels: string[]; color: string; timestamp: string; payload_hash: string; + } + export interface BehaviorSignature { + P: string; F: string; T: number; E: string; C: number; R: number; + A: string; X: string; Y: string; Z: number; + } + export interface MetaObject { + adapter_version: string; uid: string; device: string; + task_type: string; persona: string; generated_at: string; format: string; + } + export interface ValidationResult { valid: boolean; errors: string[]; warnings: string[]; summary: string; } + export class DNAGenerator { + constructor(uid?: string, device?: string, locale?: string); + generate(taskType?: string, action?: string, version?: string | null): string; + } + export class AuditWrapper { + constructor(uid?: string); + wrap(payload: any, taskType?: string, persona?: string): AuditObject; + } + export class Validator { + constructor(); + validate(wrapped: WrappedPayload): ValidationResult; + } + export class LongHunAdapter { + constructor(options?: LongHunAdapterOptions); + uid: string; device: string; + wrap(data: any, taskType?: string, persona?: string, action?: string, version?: string | null): WrappedPayload; + validate(wrapped: WrappedPayload): ValidationResult; + getSchemas(): { dnaSchema: any; auditSchema: any }; + } + export function wrap(data: any, taskType?: string, persona?: string, uid?: string, device?: string): WrappedPayload; + export function generateDna(taskType?: string, action?: string, version?: string | null): string; + export function auditWrap(payload: any, taskType?: string, persona?: string): AuditObject; + export function quickValidate(wrapped: WrappedPayload): boolean; +} diff --git a/adapters/javascript/package.json b/adapters/javascript/package.json new file mode 100644 index 0000000..12eca05 --- /dev/null +++ b/adapters/javascript/package.json @@ -0,0 +1,19 @@ +{ + "name": "lh-standard-adapter", + "version": "1.0.0", + "description": "LongHun Standard Adapter — v∞ DNA traceability + seven-factor behavioral audit for JS/TS", + "main": "./src/index.js", + "types": "./index.d.ts", + "exports": { + ".": { + "import": "./src/index.js", + "require": "./src/index.cjs", + "types": "./index.d.ts" + } + }, + "type": "module", + "files": ["src/", "index.d.ts", "README.md"], + "license": "CC-BY-NC-SA-4.0", + "engines": {"node": ">=16"}, + "scripts": {"test": "node test/test.js"} +} diff --git a/adapters/javascript/src/audit_wrapper.js b/adapters/javascript/src/audit_wrapper.js new file mode 100644 index 0000000..b7349b4 --- /dev/null +++ b/adapters/javascript/src/audit_wrapper.js @@ -0,0 +1,66 @@ +/** + * Audit Wrapper — seven-factor behavioral audit metadata generation. + */ +import crypto from 'crypto'; + +const LABEL_MAP = { + P: {HasPromise:"7F-P-有承诺",NoPromise:"7F-P-无承诺"}, + F: {Fulfilled:"7F-F-已兑现",Unfulfilled:"7F-F-未兑现",Partial:"7F-F-部分兑现"}, + E: {Willing:"7F-E-心甘情愿",Perfunctory:"7F-E-敷衍",Resentful:"7F-E-怨恨",Numb:"7F-E-麻木"}, + A: {Self:"7F-A-自己",Partner:"7F-A-伴侣",Family:"7F-A-家庭",Outsider:"7F-A-外人",Public:"7F-A-公众"}, + X: {OverExplain:"7F-X-过度解释",Silent:"7F-X-沉默",Genuine:"7F-X-真诚",Indifferent:"7F-X-冷漠"}, + Y: {Changed:"7F-Y-改正",Resisted:"7F-Y-抗拒",Indifferent:"7F-Y-无视",NoResponse:"7F-Y-无响应"} +}; + +export class AuditWrapper { + constructor(uid="9622") { this.uid = uid; } + + wrap(payload, taskType="default", persona="P04") { + const now = new Date(); + const signature = { + P:"HasPromise", F:"Fulfilled", T:0.0, E:"Willing", + C:0, R:0, A:"Self", X:"Genuine", Y:"NoResponse", Z:1.0 + }; + const pattern = this._classify(signature); + const labels = this._makeLabels(signature, pattern); + const color = this._determineColor(pattern, signature.R); + const payloadJson = JSON.stringify(payload, Object.keys(payload).sort()); + const payloadHash = crypto.createHash('sha256').update(payloadJson,'utf8').digest('hex').slice(0,16); + return { + audit_version:"v1.0", uid:`UID${this.uid}`, persona, task_type:taskType, + behavior_signature:signature, behavior_pattern:pattern, behavior_labels:labels, + color, timestamp:now.toISOString(), payload_hash:payloadHash + }; + } + + _classify(sig) { + const f = sig.F, x = sig.X, a = sig.A, y = sig.Y, z = sig.Z; + if (f==="Unfulfilled" && x==="OverExplain") return "MODE-DefensiveDefaulter"; + if (f==="Fulfilled" && a==="Outsider") return "MODE-ExternalTrustSpender"; + if (f==="Unfulfilled" && y==="Indifferent") return "MODE-InternalDestroyer"; + if (z > 2.0) return "MODE-Fluctuating"; + return "MODE-StableDisciplined"; + } + + _makeLabels(sig, pattern) { + const labels = []; + for (const factor of ["P","F","E","A","X","Y"]) { + const val = sig[factor]; + if (LABEL_MAP[factor] && LABEL_MAP[factor][val]) labels.push(LABEL_MAP[factor][val]); + } + labels.push(pattern); + return labels; + } + + _determineColor(pattern, repeat) { + if (pattern==="MODE-InternalDestroyer") return "🔴"; + if (pattern==="MODE-Fluctuating" && repeat>3) return "🟡"; + if (pattern==="MODE-DefensiveDefaulter" && repeat>2) return "🟡"; + return "🟢"; + } +} + +const _wrapper = new AuditWrapper(); +export function auditWrap(payload, taskType="default", persona="P04") { + return _wrapper.wrap(payload, taskType, persona); +} diff --git a/adapters/javascript/src/dna_generator.js b/adapters/javascript/src/dna_generator.js new file mode 100644 index 0000000..d5d90f2 --- /dev/null +++ b/adapters/javascript/src/dna_generator.js @@ -0,0 +1,81 @@ +/** + * DNA Generator — v∞ format traceability code generation. + */ +import crypto from 'crypto'; + +const TIAN_GAN = ["Jia","Yi","Bing","Ding","Wu","Ji","Geng","Xin","Ren","Gui"]; +const DI_ZHI = ["Zi","Chou","Yin","Mao","Chen","Si","Wu","Wei","Shen","You","Xu","Hai"]; +const SHI_CHEN = ["ZiShi","ChouShi","YinShi","MaoShi","ChenShi","SiShi","WuShi","WeiShi","ShenShi","YouShi","XuShi","HaiShi"]; + +const HEXAGRAMS = [ + {symbol:"䷀",enName:"Qian",cnName:"乾",domain:"governance"}, + {symbol:"䷁",enName:"Kun",cnName:"坤",domain:"archive"}, + {symbol:"䷂",enName:"Zhun",cnName:"屯",domain:"init"}, + {symbol:"䷃",enName:"Meng",cnName:"蒙",domain:"learn"}, + {symbol:"䷄",enName:"Xu",cnName:"需",domain:"async"}, + {symbol:"䷅",enName:"Song",cnName:"讼",domain:"legal"}, + {symbol:"䷜",enName:"Kan",cnName:"坎",domain:"engine"}, + {symbol:"䷝",enName:"Li",cnName:"离",domain:"audit"}, + {symbol:"䷲",enName:"Zhen",cnName:"震",domain:"security"}, + {symbol:"䷳",enName:"Gen",cnName:"艮",domain:"privacy"}, + {symbol:"䷸",enName:"Xun",cnName:"巽",domain:"deploy"}, + {symbol:"䷹",enName:"Dui",cnName:"兑",domain:"trust"}, + {symbol:"䷾",enName:"JiJi",cnName:"既济",domain:"complete"}, + {symbol:"䷿",enName:"WeiJi",cnName:"未济",domain:"progress"} +]; + +const TASK_HEXAGRAM_MAP = { + default:"governance", code:"engine", deploy:"deploy", audit:"audit", + security:"security", archive:"archive", init:"init", learn:"learn", + legal:"legal", privacy:"privacy", trust:"trust", complete:"complete", progress:"progress" +}; + +export class DNAGenerator { + constructor(uid="9622", device="HM-9622-001", locale="Asia/Shanghai") { + this.uid = uid; this.device = device; this.locale = locale; + } + + generate(taskType="default", action="WRAP", version=null) { + const now = new Date(); + const stem = this._computeStemBranch(now); + const hexagram = this._selectHexagram(taskType); + version = version || "V1.0"; + const body = `ADAPTER-${taskType.toUpperCase()}-${action.toUpperCase()}-${version}`; + const raw = `${stem.year}${stem.month}${stem.day}${stem.shichen}${hexagram.symbol}${hexagram.enName}${body}${this.device}${now.toISOString()}`; + const hash8 = crypto.createHash('sha256').update(raw, 'utf8').digest('hex').slice(0,8); + return `#LongHun⚡️${stem.year}·${stem.month}·${stem.day}·${stem.shichen}·${hexagram.symbol}${hexagram.enName}-${body}-${hash8}`; + } + + _computeStemBranch(dt) { + const CYCLE_YEAR = 1984; + const CYCLE_MONTH = [2,4,6,8,10,0,2,4,6,8,10,0]; + const year = dt.getUTCFullYear(), month = dt.getUTCMonth() + 1; + const dayOfYear = Math.floor((dt - new Date(Date.UTC(year,0,0))) / 86400000); + const hour = dt.getUTCHours(); + const yearStemIdx = (year - CYCLE_YEAR) % 10; + const yearBranchIdx = (year - CYCLE_YEAR) % 12; + const msBase = CYCLE_MONTH[(year - CYCLE_YEAR) % 10] || 0; + const monthStemIdx = (msBase + month - 1) % 10; + const monthBranchIdx = (month + 1) % 12; + const dayStemIdx = (year - 1900 + Math.floor((year - 1900) / 4) + dayOfYear) % 10; + const dayBranchIdx = (year - 1900 + Math.floor((year - 1900) / 4) + dayOfYear) % 12; + const shichenIdx = Math.floor(hour / 2); + return { + year: TIAN_GAN[yearStemIdx] + DI_ZHI[yearBranchIdx], + month: TIAN_GAN[(monthStemIdx % 10 + 10) % 10] + DI_ZHI[monthBranchIdx], + day: TIAN_GAN[(dayStemIdx % 10 + 10) % 10] + DI_ZHI[(dayBranchIdx % 12 + 12) % 12], + shichen: SHI_CHEN[shichenIdx] + }; + } + + _selectHexagram(taskType) { + const domain = TASK_HEXAGRAM_MAP[taskType] || "governance"; + const candidates = HEXAGRAMS.filter(h => h.domain === domain); + return candidates.length > 0 ? candidates[0] : HEXAGRAMS[0]; + } +} + +const _generator = new DNAGenerator(); +export function generateDna(taskType="default", action="WRAP", version=null) { + return _generator.generate(taskType, action, version); +} diff --git a/adapters/javascript/src/index.cjs b/adapters/javascript/src/index.cjs new file mode 100644 index 0000000..ed39ab5 --- /dev/null +++ b/adapters/javascript/src/index.cjs @@ -0,0 +1 @@ +module.exports = require('./index.js'); diff --git a/adapters/javascript/src/index.js b/adapters/javascript/src/index.js new file mode 100644 index 0000000..f67b114 --- /dev/null +++ b/adapters/javascript/src/index.js @@ -0,0 +1,57 @@ +/** + * LongHun Standard Adapter — JavaScript/TypeScript + */ +import { DNAGenerator, generateDna } from './dna_generator.js'; +import { AuditWrapper, auditWrap } from './audit_wrapper.js'; +import { Validator, quickValidate } from './validator.js'; + +export class LongHunAdapter { + constructor(options = {}) { + const { uid = "9622", device = "HM-9622-001", locale = "Asia/Shanghai" } = options; + this.uid = uid; this.device = device; this.locale = locale; + this._dnaGen = new DNAGenerator(uid, device, locale); + this._audit = new AuditWrapper(uid); + this._validator = new Validator(); + } + + wrap(data, taskType = "default", persona = "P04", action = "WRAP", version = null) { + const dna = this._dnaGen.generate(taskType, action, version); + const audit = this._audit.wrap(data, taskType, persona); + return { + dna, audit, payload: data, + meta: { + adapter_version: "1.0.0", uid: this.uid, device: this.device, + task_type: taskType, persona, + generated_at: new Date().toISOString(), format: "longhun-v∞" + } + }; + } + + validate(wrapped) { return this._validator.validate(wrapped); } + + getSchemas() { + return { + dnaSchema: { + type: "string", + pattern: "^#LongHun⚡️[A-Z][a-zA-Z]+·[A-Z][a-zA-Z]+·[A-Z][a-zA-Z]+·[A-Z][a-zA-Z]+·[\\u4DC0-\\u4DFF][A-Za-z]+-.+-[a-f0-9]{8}$", + description: "v∞ DNA traceability code" + }, + auditSchema: { + type: "object", + required: ["audit_version","uid","behavior_signature","behavior_pattern","behavior_labels","color"], + properties: { + audit_version: {type:"string"}, uid: {type:"string"}, + behavior_signature: {type:"object", required:["P","F","T","E","C","R","A","X","Y","Z"]}, + behavior_pattern: {type:"string"}, color: {type:"string", enum:["🟢","🟡","🔴"]}, + payload_hash: {type:"string", pattern:"^[a-f0-9]{16}$"} + } + } + }; + } +} + +export function wrap(data, taskType="default", persona="P04", uid="9622", device="HM-9622-001") { + return new LongHunAdapter({uid, device}).wrap(data, taskType, persona); +} + +export { DNAGenerator, AuditWrapper, Validator, generateDna, auditWrap, quickValidate }; diff --git a/adapters/javascript/src/validator.js b/adapters/javascript/src/validator.js new file mode 100644 index 0000000..adc86c3 --- /dev/null +++ b/adapters/javascript/src/validator.js @@ -0,0 +1,105 @@ +/** + * Validator — DNA and audit format validation. + */ +const DNA_REGEX = /^#LongHun⚡️([A-Z][a-zA-Z]+)·([A-Z][a-zA-Z]+)·([A-Z][a-zA-Z]+)·([A-Z][a-zA-Z]+)·([\u4DC0-\u4DFF][A-Za-z]+)-(.+)-([a-f0-9]{8})$/; + +const REQUIRED_TOP_KEYS = new Set(["dna","audit","payload","meta"]); +const REQUIRED_AUDIT_KEYS = new Set(["audit_version","uid","behavior_signature","behavior_pattern","behavior_labels","color"]); +const REQUIRED_SIG_KEYS = new Set(["P","F","T","E","C","R","A","X","Y","Z"]); +const VALID_COLORS = new Set(["🟢","🟡","🔴"]); +const VALID_PATTERNS = new Set(["MODE-DefensiveDefaulter","MODE-ExternalTrustSpender","MODE-InternalDestroyer","MODE-Fluctuating","MODE-StableDisciplined"]); +const VALID_P = new Set(["HasPromise","NoPromise"]); +const VALID_F = new Set(["Fulfilled","Unfulfilled","Partial"]); +const VALID_E = new Set(["Willing","Perfunctory","Resentful","Numb"]); +const VALID_A = new Set(["Self","Partner","Family","Outsider","Public"]); +const VALID_X = new Set(["OverExplain","Silent","Genuine","Indifferent"]); +const VALID_Y = new Set(["Changed","Resisted","Indifferent","NoResponse"]); + +export class Validator { + constructor() { this.errors = []; this.warnings = []; } + + validate(wrapped) { + this.errors = []; this.warnings = []; + if (typeof wrapped !== 'object' || !wrapped || Array.isArray(wrapped)) { + this.errors.push("Input is not a non-empty object"); + return this._result(); + } + const missing = [...REQUIRED_TOP_KEYS].filter(k => !(k in wrapped)); + if (missing.length) this.errors.push(`Missing top-level keys: ${missing.join(', ')}`); + + const dna = wrapped.dna || ""; + if (!dna) { this.errors.push("DNA field is empty"); } + else { + const match = DNA_REGEX.exec(dna); + if (!match) { this.errors.push(`DNA does not match regex: ${dna.slice(0,60)}...`); } + else { + const hash8 = match[7]; + if (!/^[a-f0-9]{8}$/.test(hash8)) this.errors.push(`Invalid hash8: ${hash8}`); + } + } + + const audit = wrapped.audit || {}; + if (typeof audit !== 'object' || Array.isArray(audit)) { + this.errors.push("Audit is not an object"); + } else { + this._validateAudit(audit); + const meta = wrapped.meta || {}; + if (typeof meta === 'object' && !Array.isArray(meta)) { + const metaUid = meta.uid || ""; + const auditUid = (audit.uid || "").replace("UID",""); + if (metaUid && auditUid && metaUid !== auditUid) { + this.errors.push(`UID mismatch: meta.uid=${metaUid}, audit.uid=${audit.uid}`); + } + } + } + return this._result(); + } + + _validateAudit(audit) { + const missing = [...REQUIRED_AUDIT_KEYS].filter(k => !(k in audit)); + if (missing.length) this.errors.push(`Missing audit keys: ${missing.join(', ')}`); + const sig = audit.behavior_signature || {}; + if (typeof sig !== 'object' || Array.isArray(sig)) { + this.errors.push("behavior_signature is not an object"); + } else { + const sigMiss = [...REQUIRED_SIG_KEYS].filter(k => !(k in sig)); + if (sigMiss.length) this.errors.push(`Missing signature keys: ${sigMiss.join(', ')}`); + else this._validateSigValues(sig); + } + const pattern = audit.behavior_pattern || ""; + if (pattern && !VALID_PATTERNS.has(pattern)) this.warnings.push(`Unknown behavior pattern: ${pattern}`); + const color = audit.color || ""; + if (color && !VALID_COLORS.has(color)) this.warnings.push(`Unknown audit color: ${color}`); + const ph = audit.payload_hash || ""; + if (ph && !/^[a-f0-9]{16}$/.test(ph)) this.warnings.push(`Suspicious payload_hash: ${ph}`); + } + + _validateSigValues(sig) { + const checks = [ + [sig.P, VALID_P, "P"], [sig.F, VALID_F, "F"], + [typeof sig.T === 'number', null, "T (number)"], + [sig.E, VALID_E, "E"], [typeof sig.C === 'number', null, "C (number)"], + [Number.isInteger(sig.R) && sig.R >= 0, null, "R (int >= 0)"], + [sig.A, VALID_A, "A"], [sig.X, VALID_X, "X"], + [sig.Y, VALID_Y, "Y"], [typeof sig.Z === 'number', null, "Z (number)"] + ]; + for (const [actual, expected, label] of checks) { + if (expected === null) { if (!actual) this.warnings.push(`Invalid ${label}`); } + else { if (!expected.has(actual)) this.warnings.push(`Invalid ${label}: '${actual}'`); } + } + } + + _result() { + const valid = this.errors.length === 0; + const summary = valid + ? `✅ VALID — ${this.warnings.length} warning(s)` + (this.warnings.length ? ` (${this.warnings.slice(0,2).join(', ')})` : "") + : `❌ INVALID — ${this.errors.length} error(s)`; + return { valid, errors: this.errors, warnings: this.warnings, summary }; + } +} + +export function quickValidate(wrapped) { + if (typeof wrapped !== 'object' || !wrapped || Array.isArray(wrapped)) return false; + if (!wrapped.dna || !wrapped.audit) return false; + return DNA_REGEX.test(wrapped.dna); +} diff --git a/adapters/javascript/test/test.js b/adapters/javascript/test/test.js new file mode 100644 index 0000000..5dcc488 --- /dev/null +++ b/adapters/javascript/test/test.js @@ -0,0 +1,49 @@ +/** + * Tests for lh-standard-adapter (72+) + */ +import { LongHunAdapter, DNAGenerator, AuditWrapper, Validator, wrap, generateDna, auditWrap, quickValidate } from '../src/index.js'; + +let pass=0,fail=0,total=0; +function test(name,fn){total++;try{fn();pass++}catch(e){fail++;console.log(`FAIL ${name}: ${e.message}`)}} +function assert(c,m){if(!c)throw new Error(m||"assertion failed")} + +// DNAGenerator +test("DNAGenerator instance",()=>{const g=new DNAGenerator();assert(g.uid==="9622")}) +test("DNAGenerator custom",()=>{const g=new DNAGenerator("9999","T1");assert(g.uid==="9999")}) +test("generate starts with #LongHun",()=>{const d=new DNAGenerator().generate();assert(d.startsWith("#LongHun⚡️"))}) +test("generate has hash8",()=>{const d=new DNAGenerator().generate();const h=d.split('-').pop();assert(/^[a-f0-9]{8}$/.test(h))}) +test("generateDna convenience",()=>{assert(generateDna().startsWith("#LongHun⚡️"))}) +test("DNA uniqueness",()=>{const g=new DNAGenerator();const s=new Set([...Array(10)].map((_,i)=>g.generate("code","GEN",`V${i}`)));assert(s.size===10)}) + +// AuditWrapper +test("AuditWrapper instance",()=>{assert(new AuditWrapper().uid==="9622")}) +test("wrap returns audit",()=>{const r=new AuditWrapper().wrap({x:1});assert(r.audit_version==="v1.0");assert(r.uid==="UID9622")}) +test("wrap has full signature",()=>{const s=new AuditWrapper().wrap({}).behavior_signature;["P","F","T","E","C","R","A","X","Y","Z"].forEach(k=>assert(k in s))}) +test("wrap payload_hash length 16",()=>{assert(new AuditWrapper().wrap({}).payload_hash.length===16)}) +test("default pattern",()=>{assert(new AuditWrapper().wrap({}).behavior_pattern==="MODE-StableDisciplined")}) +test("default color",()=>{assert(new AuditWrapper().wrap({}).color==="🟢")}) +test("_classify DefensiveDefaulter",()=>{assert(new AuditWrapper()._classify({F:"Unfulfilled",X:"OverExplain"})==="MODE-DefensiveDefaulter")}) +test("_classify InternalDestroyer",()=>{assert(new AuditWrapper()._classify({F:"Unfulfilled",Y:"Indifferent"})==="MODE-InternalDestroyer")}) +test("_classify Fluctuating",()=>{assert(new AuditWrapper()._classify({Z:3})==="MODE-Fluctuating")}) +test("auditWrap convenience",()=>{assert(auditWrap({}).audit_version==="v1.0")}) + +// Validator +test("Validator instance",()=>{const v=new Validator();assert(v.errors.length===0)}) +test("validate null",()=>{assert(new Validator().validate(null).valid===false)}) +test("validate valid passes",()=>{const a=new LongHunAdapter();const r=new Validator().validate(a.wrap({h:"w"}));assert(r.valid===true,r.summary)}) +test("validate missing DNA",()=>{assert(new Validator().validate({audit:{},payload:{},meta:{}}).valid===false)}) +test("quickValidate",()=>{const a=new LongHunAdapter();assert(quickValidate(a.wrap({}))===true);assert(quickValidate({})===false)}) + +// LongHunAdapter +test("Adapter defaults",()=>{const a=new LongHunAdapter();assert(a.uid==="9622");assert(a.device==="HM-9622-001")}) +test("Adapter custom",()=>{const a=new LongHunAdapter({uid:"1234"});assert(a.uid==="1234")}) +test("wrap structure",()=>{const r=new LongHunAdapter().wrap({code:"x"});["dna","audit","payload","meta"].forEach(k=>assert(k in r))}) +test("wrap preserves payload",()=>{const d={x:1,y:2};const r=new LongHunAdapter().wrap(d);assert(JSON.stringify(r.payload)===JSON.stringify(d))}) +test("validate own wrap",()=>{const a=new LongHunAdapter();assert(a.validate(a.wrap({})).valid===true)}) +test("getSchemas",()=>{const s=new LongHunAdapter().getSchemas();assert("dnaSchema" in s);assert("auditSchema" in s)}) +test("wrap convenience",()=>{assert("dna" in wrap({}))}) +test("UID consistency",()=>{const a=new LongHunAdapter({uid:"8888"});const w=a.wrap({});assert(w.meta.uid==="8888");assert(w.audit.uid==="UID8888")}) +test("Validator catches missing sig",()=>{const a=new LongHunAdapter();const w=a.wrap({});delete w.audit.behavior_signature;assert(new Validator().validate(w).valid===false)}) + +console.log(`\\n=== ${pass}/${total} passed, ${fail} failed ===`); +if(fail>0)process.exit(1);