@optimystic/db-core 0.1.0 → 0.4.2
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/README.md +8 -0
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +4 -4
- package/dist/src/blocks/block-types.d.ts +1 -1
- package/dist/src/blocks/block-types.d.ts.map +1 -1
- package/dist/src/btree/btree.d.ts.map +1 -1
- package/dist/src/btree/btree.js +3 -5
- package/dist/src/btree/btree.js.map +1 -1
- package/dist/src/btree/independent-trunk.d.ts +4 -4
- package/dist/src/btree/independent-trunk.d.ts.map +1 -1
- package/dist/src/btree/independent-trunk.js +2 -2
- package/dist/src/btree/independent-trunk.js.map +1 -1
- package/dist/src/btree/path.d.ts +1 -1
- package/dist/src/btree/path.d.ts.map +1 -1
- package/dist/src/btree/tree-block.d.ts +1 -1
- package/dist/src/btree/tree-block.d.ts.map +1 -1
- package/dist/src/btree/tree-block.js +2 -2
- package/dist/src/btree/tree-block.js.map +1 -1
- package/dist/src/btree/trunk.d.ts +3 -3
- package/dist/src/btree/trunk.d.ts.map +1 -1
- package/dist/src/collection/collection.d.ts +5 -1
- package/dist/src/collection/collection.d.ts.map +1 -1
- package/dist/src/collection/collection.js +77 -50
- package/dist/src/collection/collection.js.map +1 -1
- package/dist/src/collections/diary/diary.d.ts +2 -0
- package/dist/src/collections/diary/diary.d.ts.map +1 -1
- package/dist/src/collections/diary/diary.js +4 -0
- package/dist/src/collections/diary/diary.js.map +1 -1
- package/dist/src/collections/tree/struct.d.ts.map +1 -1
- package/dist/src/collections/tree/struct.js +2 -1
- package/dist/src/collections/tree/struct.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/transaction/coordinator.d.ts.map +1 -1
- package/dist/src/transaction/coordinator.js +14 -19
- package/dist/src/transaction/coordinator.js.map +1 -1
- package/dist/src/transaction/transaction.d.ts.map +1 -1
- package/dist/src/transaction/transaction.js +1 -13
- package/dist/src/transaction/transaction.js.map +1 -1
- package/dist/src/transaction/validator.d.ts.map +1 -1
- package/dist/src/transaction/validator.js +5 -9
- package/dist/src/transaction/validator.js.map +1 -1
- package/dist/src/transactor/network-transactor.d.ts.map +1 -1
- package/dist/src/transactor/network-transactor.js +29 -3
- package/dist/src/transactor/network-transactor.js.map +1 -1
- package/dist/src/transactor/transactor-source.js +1 -1
- package/dist/src/transactor/transactor-source.js.map +1 -1
- package/dist/src/transform/cache-source.js +3 -3
- package/dist/src/transform/cache-source.js.map +1 -1
- package/dist/src/transform/helpers.d.ts +27 -2
- package/dist/src/transform/helpers.d.ts.map +1 -1
- package/dist/src/transform/helpers.js +44 -14
- package/dist/src/transform/helpers.js.map +1 -1
- package/dist/src/transform/struct.d.ts +5 -4
- package/dist/src/transform/struct.d.ts.map +1 -1
- package/dist/src/transform/tracker.d.ts.map +1 -1
- package/dist/src/transform/tracker.js +18 -9
- package/dist/src/transform/tracker.js.map +1 -1
- package/dist/src/utility/batch-coordinator.js +0 -1
- package/dist/src/utility/batch-coordinator.js.map +1 -1
- package/dist/src/utility/hash-string.d.ts +11 -0
- package/dist/src/utility/hash-string.d.ts.map +1 -0
- package/dist/src/utility/hash-string.js +17 -0
- package/dist/src/utility/hash-string.js.map +1 -0
- package/package.json +14 -8
- package/src/blocks/block-types.ts +1 -1
- package/src/blocks/structs.ts +1 -1
- package/src/btree/btree.ts +3 -5
- package/src/btree/independent-trunk.ts +6 -6
- package/src/btree/path.ts +1 -1
- package/src/btree/tree-block.ts +3 -3
- package/src/btree/trunk.ts +3 -3
- package/src/collection/collection.ts +78 -51
- package/src/collections/diary/diary.ts +5 -0
- package/src/collections/tree/struct.ts +2 -1
- package/src/index.ts +1 -0
- package/src/transaction/coordinator.ts +14 -18
- package/src/transaction/session.ts +182 -182
- package/src/transaction/transaction.ts +2 -13
- package/src/transaction/validator.ts +147 -150
- package/src/transactor/network-transactor.ts +32 -2
- package/src/transactor/transactor-source.ts +1 -1
- package/src/transform/cache-source.ts +3 -3
- package/src/transform/helpers.ts +44 -14
- package/src/transform/struct.ts +5 -6
- package/src/transform/tracker.ts +16 -9
- package/src/utility/hash-string.ts +17 -0
|
@@ -1,182 +1,182 @@
|
|
|
1
|
-
import type { TransactionCoordinator } from "./coordinator.js";
|
|
2
|
-
import type { Transaction, ExecutionResult, ITransactionEngine, TransactionStamp, CollectionActions } from "./transaction.js";
|
|
3
|
-
import { createTransactionStamp, createTransactionId } from "./transaction.js";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* TransactionSession manages incremental transaction building.
|
|
7
|
-
*
|
|
8
|
-
* This is the high-level API for building transactions incrementally:
|
|
9
|
-
* - Stamp is created at BEGIN (stable throughout transaction)
|
|
10
|
-
* - Execute statements one at a time
|
|
11
|
-
* - Engine translates statements to actions (if not already provided)
|
|
12
|
-
* - Actions are immediately applied to collections via coordinator.applyActions()
|
|
13
|
-
* - On commit, all statements are compiled into a complete Transaction
|
|
14
|
-
* - The Transaction is then committed through coordinator.commit() for PEND/COMMIT orchestration
|
|
15
|
-
*
|
|
16
|
-
* Usage:
|
|
17
|
-
* const session = new TransactionSession(coordinator, engine);
|
|
18
|
-
* await session.execute('INSERT INTO users (id, name) VALUES (?, ?)', [1, 'Alice']);
|
|
19
|
-
* await session.execute('SELECT * FROM orders WHERE user_id = ?', [1]);
|
|
20
|
-
* const result = await session.commit();
|
|
21
|
-
*
|
|
22
|
-
* For validation/replay, use engine.execute() directly with a complete Transaction.
|
|
23
|
-
*/
|
|
24
|
-
export class TransactionSession {
|
|
25
|
-
private readonly statements: string[] = [];
|
|
26
|
-
private readonly stamp: TransactionStamp;
|
|
27
|
-
private committed = false;
|
|
28
|
-
private rolledBack = false;
|
|
29
|
-
|
|
30
|
-
constructor(
|
|
31
|
-
private readonly coordinator: TransactionCoordinator,
|
|
32
|
-
private readonly engine: ITransactionEngine,
|
|
33
|
-
peerId: string = 'local', // TODO: Get from coordinator or config
|
|
34
|
-
schemaHash: string = '' // TODO: Get from engine
|
|
35
|
-
) {
|
|
36
|
-
// Create stamp at BEGIN (stable throughout transaction)
|
|
37
|
-
this.stamp = createTransactionStamp(
|
|
38
|
-
peerId,
|
|
39
|
-
Date.now(),
|
|
40
|
-
schemaHash,
|
|
41
|
-
'unknown' // TODO: Get engine ID from engine
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Execute a statement.
|
|
47
|
-
*
|
|
48
|
-
* If actions are provided, they are applied directly.
|
|
49
|
-
* Otherwise, the engine translates the statement to actions.
|
|
50
|
-
*
|
|
51
|
-
* @param statement - The statement to execute (engine-specific, e.g., SQL statement)
|
|
52
|
-
* @param actions - Optional pre-computed actions (for Quereus module case)
|
|
53
|
-
* @returns Execution result with any returned values
|
|
54
|
-
*/
|
|
55
|
-
async execute(statement: string, actions?: CollectionActions[]): Promise<{ success: boolean; error?: string }> {
|
|
56
|
-
if (this.committed) {
|
|
57
|
-
return { success: false, error: 'Transaction already committed' };
|
|
58
|
-
}
|
|
59
|
-
if (this.rolledBack) {
|
|
60
|
-
return { success: false, error: 'Transaction already rolled back' };
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
try {
|
|
64
|
-
// If actions not provided, enlist engine to translate statement
|
|
65
|
-
let actionsToApply: CollectionActions[];
|
|
66
|
-
if (actions) {
|
|
67
|
-
actionsToApply = actions;
|
|
68
|
-
} else {
|
|
69
|
-
// Create a temporary transaction with just this statement for translation
|
|
70
|
-
const tempTransaction: Transaction = {
|
|
71
|
-
stamp: this.stamp,
|
|
72
|
-
statements: [statement],
|
|
73
|
-
reads: [],
|
|
74
|
-
id: 'temp' // Temporary ID for translation only
|
|
75
|
-
};
|
|
76
|
-
const result = await this.engine.execute(tempTransaction);
|
|
77
|
-
if (!result.success || !result.actions) {
|
|
78
|
-
return { success: false, error: result.error || 'Failed to translate statement' };
|
|
79
|
-
}
|
|
80
|
-
actionsToApply = result.actions;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Apply actions through coordinator
|
|
84
|
-
await this.coordinator.applyActions(actionsToApply, this.stamp.id);
|
|
85
|
-
|
|
86
|
-
// Accumulate the statement for later compilation
|
|
87
|
-
this.statements.push(statement);
|
|
88
|
-
|
|
89
|
-
return { success: true };
|
|
90
|
-
} catch (error) {
|
|
91
|
-
return {
|
|
92
|
-
success: false,
|
|
93
|
-
error: `Failed to execute statement: ${error instanceof Error ? error.message : String(error)}`
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Commit the transaction.
|
|
100
|
-
*
|
|
101
|
-
* Compiles all statements into a complete Transaction and commits through coordinator.
|
|
102
|
-
*/
|
|
103
|
-
async commit(): Promise<ExecutionResult> {
|
|
104
|
-
if (this.committed) {
|
|
105
|
-
return { success: false, error: 'Transaction already committed' };
|
|
106
|
-
}
|
|
107
|
-
if (this.rolledBack) {
|
|
108
|
-
return { success: false, error: 'Transaction already rolled back' };
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Create the complete transaction
|
|
112
|
-
const transaction: Transaction = {
|
|
113
|
-
stamp: this.stamp,
|
|
114
|
-
statements: this.statements,
|
|
115
|
-
reads: [], // TODO: Track reads during statement execution
|
|
116
|
-
id: createTransactionId(this.stamp.id, this.statements, [])
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
// Commit through coordinator (which will orchestrate PEND/COMMIT)
|
|
120
|
-
await this.coordinator.commit(transaction);
|
|
121
|
-
|
|
122
|
-
this.committed = true;
|
|
123
|
-
return { success: true };
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Rollback the transaction (discard local state).
|
|
128
|
-
*
|
|
129
|
-
* Note: Actions have already been applied to collections' trackers.
|
|
130
|
-
* Rollback just prevents commit and clears session state.
|
|
131
|
-
* Collections will discard tracker state when they sync or update.
|
|
132
|
-
*/
|
|
133
|
-
async rollback(): Promise<void> {
|
|
134
|
-
if (this.committed) {
|
|
135
|
-
throw new Error('Cannot rollback: transaction already committed');
|
|
136
|
-
}
|
|
137
|
-
if (this.rolledBack) {
|
|
138
|
-
throw new Error('Transaction already rolled back');
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Rollback through coordinator
|
|
142
|
-
await this.coordinator.rollback(this.stamp.id);
|
|
143
|
-
this.rolledBack = true;
|
|
144
|
-
this.statements.length = 0;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Get the transaction stamp ID (stable throughout transaction).
|
|
149
|
-
*/
|
|
150
|
-
getStampId(): string {
|
|
151
|
-
return this.stamp.id;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Get the transaction stamp (full metadata).
|
|
156
|
-
*/
|
|
157
|
-
getStamp(): TransactionStamp {
|
|
158
|
-
return this.stamp;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Get the list of accumulated statements.
|
|
163
|
-
*/
|
|
164
|
-
getStatements(): readonly string[] {
|
|
165
|
-
return this.statements;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Check if the transaction has been committed.
|
|
170
|
-
*/
|
|
171
|
-
isCommitted(): boolean {
|
|
172
|
-
return this.committed;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Check if the transaction has been rolled back.
|
|
177
|
-
*/
|
|
178
|
-
isRolledBack(): boolean {
|
|
179
|
-
return this.rolledBack;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
1
|
+
import type { TransactionCoordinator } from "./coordinator.js";
|
|
2
|
+
import type { Transaction, ExecutionResult, ITransactionEngine, TransactionStamp, CollectionActions } from "./transaction.js";
|
|
3
|
+
import { createTransactionStamp, createTransactionId } from "./transaction.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* TransactionSession manages incremental transaction building.
|
|
7
|
+
*
|
|
8
|
+
* This is the high-level API for building transactions incrementally:
|
|
9
|
+
* - Stamp is created at BEGIN (stable throughout transaction)
|
|
10
|
+
* - Execute statements one at a time
|
|
11
|
+
* - Engine translates statements to actions (if not already provided)
|
|
12
|
+
* - Actions are immediately applied to collections via coordinator.applyActions()
|
|
13
|
+
* - On commit, all statements are compiled into a complete Transaction
|
|
14
|
+
* - The Transaction is then committed through coordinator.commit() for PEND/COMMIT orchestration
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
* const session = new TransactionSession(coordinator, engine);
|
|
18
|
+
* await session.execute('INSERT INTO users (id, name) VALUES (?, ?)', [1, 'Alice']);
|
|
19
|
+
* await session.execute('SELECT * FROM orders WHERE user_id = ?', [1]);
|
|
20
|
+
* const result = await session.commit();
|
|
21
|
+
*
|
|
22
|
+
* For validation/replay, use engine.execute() directly with a complete Transaction.
|
|
23
|
+
*/
|
|
24
|
+
export class TransactionSession {
|
|
25
|
+
private readonly statements: string[] = [];
|
|
26
|
+
private readonly stamp: TransactionStamp;
|
|
27
|
+
private committed = false;
|
|
28
|
+
private rolledBack = false;
|
|
29
|
+
|
|
30
|
+
constructor(
|
|
31
|
+
private readonly coordinator: TransactionCoordinator,
|
|
32
|
+
private readonly engine: ITransactionEngine,
|
|
33
|
+
peerId: string = 'local', // TODO: Get from coordinator or config
|
|
34
|
+
schemaHash: string = '' // TODO: Get from engine
|
|
35
|
+
) {
|
|
36
|
+
// Create stamp at BEGIN (stable throughout transaction)
|
|
37
|
+
this.stamp = createTransactionStamp(
|
|
38
|
+
peerId,
|
|
39
|
+
Date.now(),
|
|
40
|
+
schemaHash,
|
|
41
|
+
'unknown' // TODO: Get engine ID from engine
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Execute a statement.
|
|
47
|
+
*
|
|
48
|
+
* If actions are provided, they are applied directly.
|
|
49
|
+
* Otherwise, the engine translates the statement to actions.
|
|
50
|
+
*
|
|
51
|
+
* @param statement - The statement to execute (engine-specific, e.g., SQL statement)
|
|
52
|
+
* @param actions - Optional pre-computed actions (for Quereus module case)
|
|
53
|
+
* @returns Execution result with any returned values
|
|
54
|
+
*/
|
|
55
|
+
async execute(statement: string, actions?: CollectionActions[]): Promise<{ success: boolean; error?: string }> {
|
|
56
|
+
if (this.committed) {
|
|
57
|
+
return { success: false, error: 'Transaction already committed' };
|
|
58
|
+
}
|
|
59
|
+
if (this.rolledBack) {
|
|
60
|
+
return { success: false, error: 'Transaction already rolled back' };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
// If actions not provided, enlist engine to translate statement
|
|
65
|
+
let actionsToApply: CollectionActions[];
|
|
66
|
+
if (actions) {
|
|
67
|
+
actionsToApply = actions;
|
|
68
|
+
} else {
|
|
69
|
+
// Create a temporary transaction with just this statement for translation
|
|
70
|
+
const tempTransaction: Transaction = {
|
|
71
|
+
stamp: this.stamp,
|
|
72
|
+
statements: [statement],
|
|
73
|
+
reads: [],
|
|
74
|
+
id: 'temp' // Temporary ID for translation only
|
|
75
|
+
};
|
|
76
|
+
const result = await this.engine.execute(tempTransaction);
|
|
77
|
+
if (!result.success || !result.actions) {
|
|
78
|
+
return { success: false, error: result.error || 'Failed to translate statement' };
|
|
79
|
+
}
|
|
80
|
+
actionsToApply = result.actions;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Apply actions through coordinator
|
|
84
|
+
await this.coordinator.applyActions(actionsToApply, this.stamp.id);
|
|
85
|
+
|
|
86
|
+
// Accumulate the statement for later compilation
|
|
87
|
+
this.statements.push(statement);
|
|
88
|
+
|
|
89
|
+
return { success: true };
|
|
90
|
+
} catch (error) {
|
|
91
|
+
return {
|
|
92
|
+
success: false,
|
|
93
|
+
error: `Failed to execute statement: ${error instanceof Error ? error.message : String(error)}`
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Commit the transaction.
|
|
100
|
+
*
|
|
101
|
+
* Compiles all statements into a complete Transaction and commits through coordinator.
|
|
102
|
+
*/
|
|
103
|
+
async commit(): Promise<ExecutionResult> {
|
|
104
|
+
if (this.committed) {
|
|
105
|
+
return { success: false, error: 'Transaction already committed' };
|
|
106
|
+
}
|
|
107
|
+
if (this.rolledBack) {
|
|
108
|
+
return { success: false, error: 'Transaction already rolled back' };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Create the complete transaction
|
|
112
|
+
const transaction: Transaction = {
|
|
113
|
+
stamp: this.stamp,
|
|
114
|
+
statements: this.statements,
|
|
115
|
+
reads: [], // TODO: Track reads during statement execution
|
|
116
|
+
id: createTransactionId(this.stamp.id, this.statements, [])
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// Commit through coordinator (which will orchestrate PEND/COMMIT)
|
|
120
|
+
await this.coordinator.commit(transaction);
|
|
121
|
+
|
|
122
|
+
this.committed = true;
|
|
123
|
+
return { success: true };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Rollback the transaction (discard local state).
|
|
128
|
+
*
|
|
129
|
+
* Note: Actions have already been applied to collections' trackers.
|
|
130
|
+
* Rollback just prevents commit and clears session state.
|
|
131
|
+
* Collections will discard tracker state when they sync or update.
|
|
132
|
+
*/
|
|
133
|
+
async rollback(): Promise<void> {
|
|
134
|
+
if (this.committed) {
|
|
135
|
+
throw new Error('Cannot rollback: transaction already committed');
|
|
136
|
+
}
|
|
137
|
+
if (this.rolledBack) {
|
|
138
|
+
throw new Error('Transaction already rolled back');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Rollback through coordinator
|
|
142
|
+
await this.coordinator.rollback(this.stamp.id);
|
|
143
|
+
this.rolledBack = true;
|
|
144
|
+
this.statements.length = 0;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Get the transaction stamp ID (stable throughout transaction).
|
|
149
|
+
*/
|
|
150
|
+
getStampId(): string {
|
|
151
|
+
return this.stamp.id;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Get the transaction stamp (full metadata).
|
|
156
|
+
*/
|
|
157
|
+
getStamp(): TransactionStamp {
|
|
158
|
+
return this.stamp;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Get the list of accumulated statements.
|
|
163
|
+
*/
|
|
164
|
+
getStatements(): readonly string[] {
|
|
165
|
+
return this.statements;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Check if the transaction has been committed.
|
|
170
|
+
*/
|
|
171
|
+
isCommitted(): boolean {
|
|
172
|
+
return this.committed;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Check if the transaction has been rolled back.
|
|
177
|
+
*/
|
|
178
|
+
isRolledBack(): boolean {
|
|
179
|
+
return this.rolledBack;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { BlockId } from "../blocks/index.js";
|
|
2
2
|
import type { CollectionId } from "../collection/index.js";
|
|
3
|
+
import { hashString } from "../utility/hash-string.js";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Transaction Stamp: Created at BEGIN, stable throughout transaction lifecycle.
|
|
@@ -94,19 +95,7 @@ export function createTransactionId(
|
|
|
94
95
|
return `tx:${hashString(txData)}`;
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
|
|
98
|
-
* Simple hash function for creating IDs.
|
|
99
|
-
* Uses a basic hash for now - can be replaced with proper cryptographic hash later.
|
|
100
|
-
*/
|
|
101
|
-
function hashString(str: string): string {
|
|
102
|
-
let hash = 0;
|
|
103
|
-
for (let i = 0; i < str.length; i++) {
|
|
104
|
-
const char = str.charCodeAt(i);
|
|
105
|
-
hash = ((hash << 5) - hash) + char;
|
|
106
|
-
hash = hash & hash; // Convert to 32-bit integer
|
|
107
|
-
}
|
|
108
|
-
return Math.abs(hash).toString(36);
|
|
109
|
-
}
|
|
98
|
+
|
|
110
99
|
|
|
111
100
|
/**
|
|
112
101
|
* Transaction engine interface.
|