Spaces:
Running
Running
File size: 14,314 Bytes
0ccd578 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | import assert from 'node:assert/strict';
import { randomUUID, createHash } from 'node:crypto';
import { mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
import { join, resolve } from 'node:path';
import { EntityRoot, GraphMutationSession, MutationIntent, MutationRequest, TraceNode, UserContext } from 'teaql-ts';
import { AbstractSQLTeaQLClient, MutationResult, SQLExecutionEvidenceStore } from 'teaql-ts/sql/core';
import { SQLiteDriver } from 'teaql-ts/sql/sqlite';
import { Q } from './lib/src/generated/Q';
import { E } from './lib/src/generated/E';
import { GENERATED_RUNTIME_MODULE } from './lib/src/runtime-module';
function manifest(directory: string, prefix = ''): string {
return readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))
.map(entry => entry.isDirectory() ? manifest(join(directory, entry.name), `${prefix}${entry.name}/`)
: `${createHash('sha256').update(readFileSync(join(directory, entry.name))).digest('hex')} ${prefix}${entry.name}\n`).join('');
}
const before = manifest('lib');
const nonce = randomUUID();
const database = resolve(process.env.TEAQL_TRACE_CHAIN_SHARED_DB ?? '.local/shared-reference.sqlite');
mkdirSync(join(database, '..'), { recursive: true });
const ledger = (entity: unknown): EntityRoot => (entity as { _root: EntityRoot })._root;
const key = (entity: string, id: string | undefined) => ({ entity, id: id! });
const plain = (nodes: unknown) => (nodes as readonly TraceNode[]).map(node => [node.kind, node.name, node.entityId, node.detail]);
class Client extends AbstractSQLTeaQLClient {
readonly commands: Array<{ entity: string; id: string; version?: number; lineage: readonly TraceNode[] }> = [];
readonly snapshots = new Map<string, Readonly<Record<string, unknown>>>();
readonly sharedSnapshots: unknown[] = [];
activeSaves = 0;
maxOverlappingSaves = 0;
constructor(driver: SQLiteDriver) { super(driver, GENERATED_RUNTIME_MODULE.schemas); }
async executeQuery(query: any): Promise<any[]> {
const rows = await super.executeQuery(query);
// Faithfully reuse an immutable provider-loaded reference, never fabricated
// values, expected versions, SQL, trace frames or generated entity wrappers.
for (const row of rows) if (row.platform && typeof row.platform === 'object') {
const cacheKey = `${row.platform.id}:${row.platform.version}`;
let snapshot = this.snapshots.get(cacheKey);
if (!snapshot) {
const loadedSnapshot: Readonly<Record<string, unknown>> = Object.freeze({ ...row.platform });
this.snapshots.set(cacheKey, loadedSnapshot); snapshot = loadedSnapshot;
}
row.platform = snapshot;
this.sharedSnapshots.push(snapshot);
}
return rows;
}
async executeGraphSave<T>(intent: MutationIntent, work: (graph: GraphMutationSession) => Promise<T>): Promise<T> {
this.activeSaves++;
this.maxOverlappingSaves = Math.max(this.maxOverlappingSaves, this.activeSaves);
try { return await super.executeGraphSave(intent, work); }
finally { this.activeSaves--; }
}
async executeMutation(input: any): Promise<MutationResult> {
assert(input instanceof MutationRequest);
const result = await super.executeMutation(input);
this.commands.push({ entity: input.mutation.entity, id: result.id, version: input.mutation.version,
lineage: input.traceFor({ entity: input.mutation.entity, id: result.id }) });
return result;
}
}
async function main() {
const driver = new SQLiteDriver(database);
const client = new Client(driver).install(GENERATED_RUNTIME_MODULE).setDiagnosticSQLLogSink(undefined);
const context = new UserContext().insertResource('dataService', client);
const sql = new SQLExecutionEvidenceStore();
const audit: Readonly<Record<string, unknown>>[] = [];
client.setRuntimeTelemetrySink(sql).setAuditSink(event => { audit.push(event); });
const reset = () => { client.commands.length = 0; audit.length = 0; sql.enableAll(); };
const observe = (scenario: string) => console.log(JSON.stringify({ scenario,
commands: client.commands, sql: sql.snapshot(), committedAudit: audit }));
const create = async (suffix: string, twoItems = false) => {
const order = Q.customerOrders().comment('initialize ownership fixture').purpose('test graph ownership').newEntity(context)
.updatePlatform('1').updateOrderNumber(`${nonce}-${suffix}`).updateDescription(`before-${suffix}`);
for (let index = 0; index < (twoItems ? 2 : 1); index++) {
order.orderItemList().push(Q.orderItems().comment('initialize ownership item').purpose('test graph ownership').newEntity(context)
.updateName(`${nonce}-${suffix}-item-${index}`));
}
await order.auditAs('create ownership fixture').save(context);
return order;
};
const load = async (id: string) => {
const rows = await Q.customerOrders().withIdIs(id)
.selectPlatformWith(Q.platforms().limit(1))
.selectOrderItemListWith(Q.orderItems().limit(10))
.limit(1).comment('load ownership graph').purpose('test generated graph isolation').executeForList(context);
assert.equal(rows.length, 1);
return rows[0];
};
const scenario = process.env.TEAQL_TRACE_CHAIN_SCENARIO;
const checks: string[] = [];
try {
await context.ensureSchema(); await context.ensureSchema();
if (!scenario || scenario === 'shared') {
const a = await create('shared-a'), b = await create('shared-b');
await b.updateDescription('give b a distinct optimistic version').auditAs('prepare distinct version').save(context);
const first = await load(a.id!), second = await load(b.id!);
const platformA = E.customerOrder(first).platform().eval()!, platformB = E.customerOrder(second).platform().eval()!;
assert.equal(client.sharedSnapshots[0], client.sharedSnapshots[1], 'provider reference is genuinely shared');
assert(Object.isFrozen(client.sharedSnapshots[0]));
assert.notEqual(platformA, platformB, 'typed wrappers do not share mutation ownership');
assert.notEqual(ledger(first), ledger(second));
assert.notEqual(ledger(platformA), ledger(first));
assert.notEqual(ledger(platformB), ledger(second));
const platformRootA = ledger(platformA), platformRootB = ledger(platformB);
const itemA = first.orderItemList()[0], itemB = second.orderItemList()[0];
const itemVersions = [E.orderItem(itemA).version().eval(), E.orderItem(itemB).version().eval()];
const rootVersions = [E.customerOrder(first).version().eval()!, E.customerOrder(second).version().eval()!];
assert.notEqual(rootVersions[0], rootVersions[1]);
first.updateDescription('own first change'); second.updateDescription('own second change');
reset(); client.maxOverlappingSaves = 0;
await Promise.all([first.auditAs('first independent ownership request').save(context),
second.auditAs('second independent ownership request').save(context)]);
assert.equal(client.maxOverlappingSaves, 2, 'actual public saves overlap; transactions remain queued');
assert.deepEqual(client.commands.map(command => [command.entity, command.id, command.version]),
[['CustomerOrder', first.id, rootVersions[0]], ['CustomerOrder', second.id, rootVersions[1]]]);
assert.deepEqual([E.orderItem(itemA).version().eval(), E.orderItem(itemB).version().eval()], itemVersions);
assert.equal(ledger(platformA), platformRootA); assert.equal(ledger(platformB), platformRootB);
assert.equal(platformRootA.snapshot().length, 0); assert.equal(platformRootB.snapshot().length, 0);
const expected = [
[['auditReason', 'CustomerOrder', first.id, 'first independent ownership request']],
[['auditReason', 'CustomerOrder', second.id, 'second independent ownership request']],
];
assert.deepEqual(client.commands.map(command => plain(command.lineage)), expected);
assert.deepEqual(audit.map(event => plain(event.mutationLineage)), expected);
assert.deepEqual(sql.snapshot().filter(entry => entry.operation === 'update').map(entry => plain(entry.mutationLineage)), expected);
assert(sql.snapshot().filter(entry => entry.operation === 'update').every(entry => entry.affectedRows === 1));
observe('shared');
assert(!sql.snapshot().some(entry => entry.operation === 'update'
&& entry.tracePath.some(node => node.kind === 'entity' && node.name === 'Platform')));
const reread = await load(first.id!);
assert.equal(E.customerOrder(reread).description().eval(), 'own first change');
assert.equal(E.orderItem(reread.orderItemList()[0]).version().eval(), itemVersions[0]);
checks.push('shared');
console.log(`SHARED READONLY PASSED: overlapping_saves=2 root_versions=${rootVersions.join(',')} mutations=2 audits=2 unchanged_children=true`);
}
if (!scenario || scenario === 'scoped') {
const original = await create('scoped', true);
const receiver = await load(original.id!), source = await load(original.id!);
const sourceRoot = ledger(source);
source.updateDescription('FOREIGN ROOT MUST REMAIN PENDING');
const reached = source.orderItemList()[0], unrelated = source.orderItemList()[1];
reached.updateName('reached child change').auditAs('edit selected item');
unrelated.updateName('UNREACHED CHILD MUST REMAIN PENDING');
receiver.orderItemList().splice(0, receiver.orderItemList().length, reached);
receiver.updateDescription('receiver own change');
reset();
await receiver.auditAs('save scoped graph').save(context);
assert.equal(client.commands.length, 2);
assert.deepEqual(client.commands.map(command => command.entity), ['CustomerOrder', 'OrderItem']);
assert.equal(ledger(receiver).change(key('OrderItem', unrelated.id)).name, undefined, 'do not import an unreachable key');
assert.equal(sourceRoot.change(key('CustomerOrder', source.id)).description, 'FOREIGN ROOT MUST REMAIN PENDING');
assert.equal(sourceRoot.change(key('OrderItem', unrelated.id)).name, 'UNREACHED CHILD MUST REMAIN PENDING');
assert.equal(sourceRoot.change(key('OrderItem', reached.id)).name, 'reached child change', 'import does not clear source');
const expected = [[['auditReason', 'CustomerOrder', receiver.id, 'save scoped graph']],
[['auditReason', 'CustomerOrder', receiver.id, 'save scoped graph'], ['auditReason', 'OrderItem', reached.id, 'edit selected item']]];
assert.deepEqual(client.commands.map(command => plain(command.lineage)), expected);
assert.deepEqual(audit.map(event => plain(event.mutationLineage)), expected);
const reread = await load(receiver.id!);
observe('scoped');
assert.equal(E.customerOrder(reread).description().eval(), 'receiver own change');
const byId = new Map(reread.orderItemList().map(item => [item.id, item]));
assert.equal(E.orderItem(byId.get(reached.id)!).name().eval(), 'reached child change');
assert.notEqual(E.orderItem(byId.get(unrelated.id)!).name().eval(), 'UNREACHED CHILD MUST REMAIN PENDING');
checks.push('scoped');
console.log('SCOPED IMPORT PASSED: mutations=2 audits=2 foreign_root_retained=true unreachable_child_retained=true');
}
if (!scenario || scenario === 'descendant') {
const created = await create('descendant'), loaded = await load(created.id!);
const version = E.customerOrder(loaded).version().eval();
loaded.orderItemList()[0].updateName('only the child changed').auditAs('edit child under clean parent');
reset();
await loaded.auditAs('save descendant change').save(context);
assert.deepEqual(client.commands.map(command => command.entity), ['OrderItem']);
assert.equal(E.customerOrder(loaded).version().eval(), version);
assert.deepEqual(plain(audit[0].mutationLineage), [
['auditReason', 'CustomerOrder', loaded.id, 'save descendant change'],
['auditReason', 'OrderItem', loaded.orderItemList()[0].id, 'edit child under clean parent'],
]);
const reread = await load(loaded.id!);
assert.equal(E.orderItem(reread.orderItemList()[0]).name().eval(), 'only the child changed');
assert.equal(E.customerOrder(reread).version().eval(), version);
checks.push('descendant');
observe('descendant');
console.log('CLEAN ANCESTOR PASSED: mutations=1 audits=1 parent_version_unchanged=true lineage_inherited=true');
}
if (!scenario || scenario === 'conflict') {
const created = await create('conflict');
const old = await load(created.id!);
const newer = await load(created.id!);
await newer.orderItemList()[0].updateName('committed newer version').auditAs('advance selected item').save(context);
const fresh = await load(created.id!);
const conflicting = fresh.orderItemList()[0].updateName('uncommitted conflicting version');
const pendingSource = ledger(conflicting);
old.orderItemList().push(conflicting);
old.updateDescription('pending receiver must survive conflict');
reset();
await assert.rejects(old.auditAs('reject mixed versions').save(context), /ENTITY_VERSION_CONFLICT/);
assert.equal(client.commands.length, 0); assert.equal(audit.length, 0);
assert.equal(sql.snapshot().filter(entry => ['insert', 'update', 'delete'].includes(entry.operation)).length, 0);
assert.equal(ledger(old).change(key('CustomerOrder', old.id)).description, 'pending receiver must survive conflict');
assert.equal(pendingSource.change(key('OrderItem', conflicting.id)).name, 'uncommitted conflicting version');
checks.push('conflict');
observe('conflict');
console.log('MIXED VERSION REJECTED: mutations=0 audits=0 receiver_pending=true source_pending=true');
}
} finally { await client.close(); }
assert.equal(manifest('lib'), before, 'acceptance must not edit generated source');
mkdirSync('evidence', { recursive: true });
writeFileSync('evidence/shared-reference-library.sha256', before);
console.log(JSON.stringify({ database, nonce, checks, generatedSourceUnchanged: true }));
console.log(`PASS TypeScript shared-reference example: ${checks.length} checks`);
}
main().catch(error => { console.error(error); process.exitCode = 1; });
|