@noy-db/hub 0.1.0-pre.3
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/LICENSE +21 -0
- package/README.md +197 -0
- package/dist/aggregate/index.cjs +476 -0
- package/dist/aggregate/index.cjs.map +1 -0
- package/dist/aggregate/index.d.cts +38 -0
- package/dist/aggregate/index.d.ts +38 -0
- package/dist/aggregate/index.js +53 -0
- package/dist/aggregate/index.js.map +1 -0
- package/dist/blobs/index.cjs +1480 -0
- package/dist/blobs/index.cjs.map +1 -0
- package/dist/blobs/index.d.cts +45 -0
- package/dist/blobs/index.d.ts +45 -0
- package/dist/blobs/index.js +48 -0
- package/dist/blobs/index.js.map +1 -0
- package/dist/bundle/index.cjs +436 -0
- package/dist/bundle/index.cjs.map +1 -0
- package/dist/bundle/index.d.cts +7 -0
- package/dist/bundle/index.d.ts +7 -0
- package/dist/bundle/index.js +40 -0
- package/dist/bundle/index.js.map +1 -0
- package/dist/chunk-2QR2PQTT.js +217 -0
- package/dist/chunk-2QR2PQTT.js.map +1 -0
- package/dist/chunk-4OWFYIDQ.js +79 -0
- package/dist/chunk-4OWFYIDQ.js.map +1 -0
- package/dist/chunk-5AATM2M2.js +90 -0
- package/dist/chunk-5AATM2M2.js.map +1 -0
- package/dist/chunk-ACLDOTNQ.js +543 -0
- package/dist/chunk-ACLDOTNQ.js.map +1 -0
- package/dist/chunk-BTDCBVJW.js +160 -0
- package/dist/chunk-BTDCBVJW.js.map +1 -0
- package/dist/chunk-CIMZBAZB.js +72 -0
- package/dist/chunk-CIMZBAZB.js.map +1 -0
- package/dist/chunk-E445ICYI.js +365 -0
- package/dist/chunk-E445ICYI.js.map +1 -0
- package/dist/chunk-EXQRC2L4.js +722 -0
- package/dist/chunk-EXQRC2L4.js.map +1 -0
- package/dist/chunk-FZU343FL.js +32 -0
- package/dist/chunk-FZU343FL.js.map +1 -0
- package/dist/chunk-GJILMRPO.js +354 -0
- package/dist/chunk-GJILMRPO.js.map +1 -0
- package/dist/chunk-GOUT6DND.js +1285 -0
- package/dist/chunk-GOUT6DND.js.map +1 -0
- package/dist/chunk-J66GRPNH.js +111 -0
- package/dist/chunk-J66GRPNH.js.map +1 -0
- package/dist/chunk-M2F2JAWB.js +464 -0
- package/dist/chunk-M2F2JAWB.js.map +1 -0
- package/dist/chunk-M5INGEFC.js +84 -0
- package/dist/chunk-M5INGEFC.js.map +1 -0
- package/dist/chunk-M62XNWRA.js +72 -0
- package/dist/chunk-M62XNWRA.js.map +1 -0
- package/dist/chunk-MR4424N3.js +275 -0
- package/dist/chunk-MR4424N3.js.map +1 -0
- package/dist/chunk-NPC4LFV5.js +132 -0
- package/dist/chunk-NPC4LFV5.js.map +1 -0
- package/dist/chunk-NXFEYLVG.js +311 -0
- package/dist/chunk-NXFEYLVG.js.map +1 -0
- package/dist/chunk-R36SIKES.js +79 -0
- package/dist/chunk-R36SIKES.js.map +1 -0
- package/dist/chunk-TDR6T5CJ.js +381 -0
- package/dist/chunk-TDR6T5CJ.js.map +1 -0
- package/dist/chunk-UF3BUNQZ.js +1 -0
- package/dist/chunk-UF3BUNQZ.js.map +1 -0
- package/dist/chunk-UQFSPSWG.js +1109 -0
- package/dist/chunk-UQFSPSWG.js.map +1 -0
- package/dist/chunk-USKYUS74.js +793 -0
- package/dist/chunk-USKYUS74.js.map +1 -0
- package/dist/chunk-XCL3WP6J.js +121 -0
- package/dist/chunk-XCL3WP6J.js.map +1 -0
- package/dist/chunk-XHFOENR2.js +680 -0
- package/dist/chunk-XHFOENR2.js.map +1 -0
- package/dist/chunk-ZFKD4QMV.js +430 -0
- package/dist/chunk-ZFKD4QMV.js.map +1 -0
- package/dist/chunk-ZLMV3TUA.js +490 -0
- package/dist/chunk-ZLMV3TUA.js.map +1 -0
- package/dist/chunk-ZRG4V3F5.js +17 -0
- package/dist/chunk-ZRG4V3F5.js.map +1 -0
- package/dist/consent/index.cjs +204 -0
- package/dist/consent/index.cjs.map +1 -0
- package/dist/consent/index.d.cts +24 -0
- package/dist/consent/index.d.ts +24 -0
- package/dist/consent/index.js +23 -0
- package/dist/consent/index.js.map +1 -0
- package/dist/crdt/index.cjs +152 -0
- package/dist/crdt/index.cjs.map +1 -0
- package/dist/crdt/index.d.cts +30 -0
- package/dist/crdt/index.d.ts +30 -0
- package/dist/crdt/index.js +24 -0
- package/dist/crdt/index.js.map +1 -0
- package/dist/crypto-IVKU7YTT.js +44 -0
- package/dist/crypto-IVKU7YTT.js.map +1 -0
- package/dist/delegation-XDJCBTI2.js +16 -0
- package/dist/delegation-XDJCBTI2.js.map +1 -0
- package/dist/dev-unlock-CeXic1xC.d.cts +263 -0
- package/dist/dev-unlock-KrKkcqD3.d.ts +263 -0
- package/dist/hash-9KO1BGxh.d.cts +63 -0
- package/dist/hash-ChfJjRjQ.d.ts +63 -0
- package/dist/history/index.cjs +1215 -0
- package/dist/history/index.cjs.map +1 -0
- package/dist/history/index.d.cts +62 -0
- package/dist/history/index.d.ts +62 -0
- package/dist/history/index.js +79 -0
- package/dist/history/index.js.map +1 -0
- package/dist/i18n/index.cjs +746 -0
- package/dist/i18n/index.cjs.map +1 -0
- package/dist/i18n/index.d.cts +38 -0
- package/dist/i18n/index.d.ts +38 -0
- package/dist/i18n/index.js +55 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/index-BRHBCmLt.d.ts +1940 -0
- package/dist/index-C8kQtmOk.d.ts +380 -0
- package/dist/index-DN-J-5wT.d.cts +1940 -0
- package/dist/index-DhjMjz7L.d.cts +380 -0
- package/dist/index.cjs +14756 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +269 -0
- package/dist/index.d.ts +269 -0
- package/dist/index.js +6085 -0
- package/dist/index.js.map +1 -0
- package/dist/indexing/index.cjs +736 -0
- package/dist/indexing/index.cjs.map +1 -0
- package/dist/indexing/index.d.cts +36 -0
- package/dist/indexing/index.d.ts +36 -0
- package/dist/indexing/index.js +77 -0
- package/dist/indexing/index.js.map +1 -0
- package/dist/lazy-builder-BwEoBQZ9.d.ts +304 -0
- package/dist/lazy-builder-CZVLKh0Z.d.cts +304 -0
- package/dist/ledger-2NX4L7PN.js +33 -0
- package/dist/ledger-2NX4L7PN.js.map +1 -0
- package/dist/mime-magic-CBBSOkjm.d.cts +50 -0
- package/dist/mime-magic-CBBSOkjm.d.ts +50 -0
- package/dist/periods/index.cjs +1035 -0
- package/dist/periods/index.cjs.map +1 -0
- package/dist/periods/index.d.cts +21 -0
- package/dist/periods/index.d.ts +21 -0
- package/dist/periods/index.js +25 -0
- package/dist/periods/index.js.map +1 -0
- package/dist/predicate-SBHmi6D0.d.cts +161 -0
- package/dist/predicate-SBHmi6D0.d.ts +161 -0
- package/dist/query/index.cjs +1957 -0
- package/dist/query/index.cjs.map +1 -0
- package/dist/query/index.d.cts +3 -0
- package/dist/query/index.d.ts +3 -0
- package/dist/query/index.js +62 -0
- package/dist/query/index.js.map +1 -0
- package/dist/session/index.cjs +487 -0
- package/dist/session/index.cjs.map +1 -0
- package/dist/session/index.d.cts +45 -0
- package/dist/session/index.d.ts +45 -0
- package/dist/session/index.js +44 -0
- package/dist/session/index.js.map +1 -0
- package/dist/shadow/index.cjs +133 -0
- package/dist/shadow/index.cjs.map +1 -0
- package/dist/shadow/index.d.cts +16 -0
- package/dist/shadow/index.d.ts +16 -0
- package/dist/shadow/index.js +20 -0
- package/dist/shadow/index.js.map +1 -0
- package/dist/store/index.cjs +1069 -0
- package/dist/store/index.cjs.map +1 -0
- package/dist/store/index.d.cts +491 -0
- package/dist/store/index.d.ts +491 -0
- package/dist/store/index.js +34 -0
- package/dist/store/index.js.map +1 -0
- package/dist/strategy-BSxFXGzb.d.cts +110 -0
- package/dist/strategy-BSxFXGzb.d.ts +110 -0
- package/dist/strategy-D-SrOLCl.d.cts +548 -0
- package/dist/strategy-D-SrOLCl.d.ts +548 -0
- package/dist/sync/index.cjs +1062 -0
- package/dist/sync/index.cjs.map +1 -0
- package/dist/sync/index.d.cts +42 -0
- package/dist/sync/index.d.ts +42 -0
- package/dist/sync/index.js +28 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/team/index.cjs +1233 -0
- package/dist/team/index.cjs.map +1 -0
- package/dist/team/index.d.cts +117 -0
- package/dist/team/index.d.ts +117 -0
- package/dist/team/index.js +39 -0
- package/dist/team/index.js.map +1 -0
- package/dist/tx/index.cjs +212 -0
- package/dist/tx/index.cjs.map +1 -0
- package/dist/tx/index.d.cts +20 -0
- package/dist/tx/index.d.ts +20 -0
- package/dist/tx/index.js +20 -0
- package/dist/tx/index.js.map +1 -0
- package/dist/types-BZpCZB8N.d.ts +7526 -0
- package/dist/types-Bfs0qr5F.d.cts +7526 -0
- package/dist/ulid-COREQ2RQ.js +9 -0
- package/dist/ulid-COREQ2RQ.js.map +1 -0
- package/dist/util/index.cjs +230 -0
- package/dist/util/index.cjs.map +1 -0
- package/dist/util/index.d.cts +77 -0
- package/dist/util/index.d.ts +77 -0
- package/dist/util/index.js +190 -0
- package/dist/util/index.js.map +1 -0
- package/package.json +244 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_JOIN_MAX_ROWS,
|
|
3
|
+
Query,
|
|
4
|
+
ScanBuilder,
|
|
5
|
+
applyJoins,
|
|
6
|
+
buildLiveQuery,
|
|
7
|
+
executePlan,
|
|
8
|
+
resetJoinWarnings
|
|
9
|
+
} from "../chunk-GOUT6DND.js";
|
|
10
|
+
import {
|
|
11
|
+
CollectionIndexes
|
|
12
|
+
} from "../chunk-NPC4LFV5.js";
|
|
13
|
+
import {
|
|
14
|
+
Aggregation,
|
|
15
|
+
GROUPBY_MAX_CARDINALITY,
|
|
16
|
+
GROUPBY_WARN_CARDINALITY,
|
|
17
|
+
GroupedAggregation,
|
|
18
|
+
GroupedQuery,
|
|
19
|
+
avg,
|
|
20
|
+
buildLiveAggregation,
|
|
21
|
+
count,
|
|
22
|
+
groupAndReduce,
|
|
23
|
+
max,
|
|
24
|
+
min,
|
|
25
|
+
reduceRecords,
|
|
26
|
+
resetGroupByWarnings,
|
|
27
|
+
sum
|
|
28
|
+
} from "../chunk-TDR6T5CJ.js";
|
|
29
|
+
import {
|
|
30
|
+
evaluateClause,
|
|
31
|
+
evaluateFieldClause,
|
|
32
|
+
readPath
|
|
33
|
+
} from "../chunk-M5INGEFC.js";
|
|
34
|
+
import "../chunk-ACLDOTNQ.js";
|
|
35
|
+
export {
|
|
36
|
+
Aggregation,
|
|
37
|
+
CollectionIndexes,
|
|
38
|
+
DEFAULT_JOIN_MAX_ROWS,
|
|
39
|
+
GROUPBY_MAX_CARDINALITY,
|
|
40
|
+
GROUPBY_WARN_CARDINALITY,
|
|
41
|
+
GroupedAggregation,
|
|
42
|
+
GroupedQuery,
|
|
43
|
+
Query,
|
|
44
|
+
ScanBuilder,
|
|
45
|
+
applyJoins,
|
|
46
|
+
avg,
|
|
47
|
+
buildLiveAggregation,
|
|
48
|
+
buildLiveQuery,
|
|
49
|
+
count,
|
|
50
|
+
evaluateClause,
|
|
51
|
+
evaluateFieldClause,
|
|
52
|
+
executePlan,
|
|
53
|
+
groupAndReduce,
|
|
54
|
+
max,
|
|
55
|
+
min,
|
|
56
|
+
readPath,
|
|
57
|
+
reduceRecords,
|
|
58
|
+
resetGroupByWarnings,
|
|
59
|
+
resetJoinWarnings,
|
|
60
|
+
sum
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,487 @@
|
|
|
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/session/index.ts
|
|
21
|
+
var session_exports = {};
|
|
22
|
+
__export(session_exports, {
|
|
23
|
+
PolicyEnforcer: () => PolicyEnforcer,
|
|
24
|
+
activeSessionCount: () => activeSessionCount,
|
|
25
|
+
clearDevUnlock: () => clearDevUnlock,
|
|
26
|
+
createEnforcer: () => createEnforcer,
|
|
27
|
+
createSession: () => createSession,
|
|
28
|
+
enableDevUnlock: () => enableDevUnlock,
|
|
29
|
+
isDevUnlockActive: () => isDevUnlockActive,
|
|
30
|
+
isSessionAlive: () => isSessionAlive,
|
|
31
|
+
loadDevUnlock: () => loadDevUnlock,
|
|
32
|
+
resolveSession: () => resolveSession,
|
|
33
|
+
revokeAllSessions: () => revokeAllSessions,
|
|
34
|
+
revokeSession: () => revokeSession,
|
|
35
|
+
validateSessionPolicy: () => validateSessionPolicy,
|
|
36
|
+
withSession: () => withSession
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(session_exports);
|
|
39
|
+
|
|
40
|
+
// src/errors.ts
|
|
41
|
+
var NoydbError = class extends Error {
|
|
42
|
+
/** Machine-readable error code. Stable across library versions. */
|
|
43
|
+
code;
|
|
44
|
+
constructor(code, message) {
|
|
45
|
+
super(message);
|
|
46
|
+
this.name = "NoydbError";
|
|
47
|
+
this.code = code;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
var ValidationError = class extends NoydbError {
|
|
51
|
+
constructor(message = "Validation error") {
|
|
52
|
+
super("VALIDATION_ERROR", message);
|
|
53
|
+
this.name = "ValidationError";
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
var SessionExpiredError = class extends NoydbError {
|
|
57
|
+
sessionId;
|
|
58
|
+
constructor(sessionId) {
|
|
59
|
+
super("SESSION_EXPIRED", `Session "${sessionId}" has expired. Re-unlock to continue.`);
|
|
60
|
+
this.name = "SessionExpiredError";
|
|
61
|
+
this.sessionId = sessionId;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
var SessionNotFoundError = class extends NoydbError {
|
|
65
|
+
sessionId;
|
|
66
|
+
constructor(sessionId) {
|
|
67
|
+
super("SESSION_NOT_FOUND", `Session key for "${sessionId}" not found. The session may have been revoked or the page reloaded.`);
|
|
68
|
+
this.name = "SessionNotFoundError";
|
|
69
|
+
this.sessionId = sessionId;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
var SessionPolicyError = class extends NoydbError {
|
|
73
|
+
operation;
|
|
74
|
+
constructor(operation, message) {
|
|
75
|
+
super(
|
|
76
|
+
"SESSION_POLICY",
|
|
77
|
+
message ?? `Operation "${operation}" requires re-authentication per the active session policy.`
|
|
78
|
+
);
|
|
79
|
+
this.name = "SessionPolicyError";
|
|
80
|
+
this.operation = operation;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// src/crypto.ts
|
|
85
|
+
var subtle = globalThis.crypto.subtle;
|
|
86
|
+
function bufferToBase64(buffer) {
|
|
87
|
+
const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
|
|
88
|
+
let binary = "";
|
|
89
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
90
|
+
binary += String.fromCharCode(bytes[i]);
|
|
91
|
+
}
|
|
92
|
+
return btoa(binary);
|
|
93
|
+
}
|
|
94
|
+
function base64ToBuffer(base64) {
|
|
95
|
+
const binary = atob(base64);
|
|
96
|
+
const bytes = new Uint8Array(binary.length);
|
|
97
|
+
for (let i = 0; i < binary.length; i++) {
|
|
98
|
+
bytes[i] = binary.charCodeAt(i);
|
|
99
|
+
}
|
|
100
|
+
return bytes;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/bundle/ulid.ts
|
|
104
|
+
var CROCKFORD_ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
105
|
+
function encodeBase32(value, length) {
|
|
106
|
+
let out = "";
|
|
107
|
+
let v = value;
|
|
108
|
+
for (let i = 0; i < length; i++) {
|
|
109
|
+
out = CROCKFORD_ALPHABET[v % 32] + out;
|
|
110
|
+
v = Math.floor(v / 32);
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
function generateULID() {
|
|
115
|
+
const now = Date.now();
|
|
116
|
+
const timestampHigh = Math.floor(now / 16777216);
|
|
117
|
+
const timestampLow = now & 16777215;
|
|
118
|
+
const tsPart = encodeBase32(timestampHigh, 5) + encodeBase32(timestampLow, 5);
|
|
119
|
+
const randBytes = new Uint8Array(10);
|
|
120
|
+
crypto.getRandomValues(randBytes);
|
|
121
|
+
const rand1 = randBytes[0] * 2 ** 32 + (randBytes[1] << 24 >>> 0) + (randBytes[2] << 16) + (randBytes[3] << 8) + randBytes[4];
|
|
122
|
+
const rand2 = randBytes[5] * 2 ** 32 + (randBytes[6] << 24 >>> 0) + (randBytes[7] << 16) + (randBytes[8] << 8) + randBytes[9];
|
|
123
|
+
const randPart = encodeBase32(rand1, 8) + encodeBase32(rand2, 8);
|
|
124
|
+
return tsPart + randPart;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/session/session.ts
|
|
128
|
+
var subtle2 = globalThis.crypto.subtle;
|
|
129
|
+
var DEFAULT_TTL_MS = 60 * 60 * 1e3;
|
|
130
|
+
var sessionKeyStore = /* @__PURE__ */ new Map();
|
|
131
|
+
async function createSession(keyring, vault, options = {}) {
|
|
132
|
+
const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
|
|
133
|
+
const sessionId = generateULID();
|
|
134
|
+
const expiresAt = new Date(Date.now() + ttlMs).toISOString();
|
|
135
|
+
const sessionKey = await subtle2.generateKey(
|
|
136
|
+
{ name: "AES-GCM", length: 256 },
|
|
137
|
+
false,
|
|
138
|
+
// non-extractable — this is the tab-scope security invariant
|
|
139
|
+
["encrypt", "decrypt"]
|
|
140
|
+
);
|
|
141
|
+
const dekMap = {};
|
|
142
|
+
for (const [collName, dek] of keyring.deks) {
|
|
143
|
+
const raw = await subtle2.exportKey("raw", dek);
|
|
144
|
+
dekMap[collName] = bufferToBase64(raw);
|
|
145
|
+
}
|
|
146
|
+
const payload = JSON.stringify({
|
|
147
|
+
userId: keyring.userId,
|
|
148
|
+
displayName: keyring.displayName,
|
|
149
|
+
role: keyring.role,
|
|
150
|
+
permissions: keyring.permissions,
|
|
151
|
+
deks: dekMap,
|
|
152
|
+
salt: bufferToBase64(keyring.salt)
|
|
153
|
+
});
|
|
154
|
+
const iv = globalThis.crypto.getRandomValues(new Uint8Array(12));
|
|
155
|
+
const encrypted = await subtle2.encrypt(
|
|
156
|
+
{ name: "AES-GCM", iv },
|
|
157
|
+
sessionKey,
|
|
158
|
+
new TextEncoder().encode(payload)
|
|
159
|
+
);
|
|
160
|
+
const token = {
|
|
161
|
+
_noydb_session: 1,
|
|
162
|
+
sessionId,
|
|
163
|
+
userId: keyring.userId,
|
|
164
|
+
vault,
|
|
165
|
+
role: keyring.role,
|
|
166
|
+
expiresAt,
|
|
167
|
+
wrappedKek: bufferToBase64(encrypted),
|
|
168
|
+
kekIv: bufferToBase64(iv)
|
|
169
|
+
};
|
|
170
|
+
sessionKeyStore.set(sessionId, sessionKey);
|
|
171
|
+
return { token, sessionId };
|
|
172
|
+
}
|
|
173
|
+
async function resolveSession(token) {
|
|
174
|
+
if (Date.now() > new Date(token.expiresAt).getTime()) {
|
|
175
|
+
sessionKeyStore.delete(token.sessionId);
|
|
176
|
+
throw new SessionExpiredError(token.sessionId);
|
|
177
|
+
}
|
|
178
|
+
const sessionKey = sessionKeyStore.get(token.sessionId);
|
|
179
|
+
if (!sessionKey) {
|
|
180
|
+
throw new SessionNotFoundError(token.sessionId);
|
|
181
|
+
}
|
|
182
|
+
const iv = base64ToBuffer(token.kekIv);
|
|
183
|
+
const ciphertext = base64ToBuffer(token.wrappedKek);
|
|
184
|
+
let plaintext;
|
|
185
|
+
try {
|
|
186
|
+
plaintext = await subtle2.decrypt(
|
|
187
|
+
{ name: "AES-GCM", iv },
|
|
188
|
+
sessionKey,
|
|
189
|
+
ciphertext
|
|
190
|
+
);
|
|
191
|
+
} catch {
|
|
192
|
+
throw new SessionNotFoundError(token.sessionId);
|
|
193
|
+
}
|
|
194
|
+
const payload = JSON.parse(new TextDecoder().decode(plaintext));
|
|
195
|
+
const deks = /* @__PURE__ */ new Map();
|
|
196
|
+
for (const [collName, rawBase64] of Object.entries(payload.deks)) {
|
|
197
|
+
const dek = await subtle2.importKey(
|
|
198
|
+
"raw",
|
|
199
|
+
base64ToBuffer(rawBase64),
|
|
200
|
+
{ name: "AES-GCM", length: 256 },
|
|
201
|
+
true,
|
|
202
|
+
["encrypt", "decrypt"]
|
|
203
|
+
);
|
|
204
|
+
deks.set(collName, dek);
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
userId: payload.userId,
|
|
208
|
+
displayName: payload.displayName,
|
|
209
|
+
role: payload.role,
|
|
210
|
+
permissions: payload.permissions,
|
|
211
|
+
deks,
|
|
212
|
+
kek: null,
|
|
213
|
+
// KEK not available in session context
|
|
214
|
+
salt: base64ToBuffer(payload.salt)
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
function revokeSession(sessionId) {
|
|
218
|
+
sessionKeyStore.delete(sessionId);
|
|
219
|
+
}
|
|
220
|
+
function isSessionAlive(token) {
|
|
221
|
+
if (Date.now() > new Date(token.expiresAt).getTime()) return false;
|
|
222
|
+
return sessionKeyStore.has(token.sessionId);
|
|
223
|
+
}
|
|
224
|
+
function revokeAllSessions() {
|
|
225
|
+
sessionKeyStore.clear();
|
|
226
|
+
}
|
|
227
|
+
function activeSessionCount() {
|
|
228
|
+
return sessionKeyStore.size;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// src/session/session-policy.ts
|
|
232
|
+
var PolicyEnforcer = class {
|
|
233
|
+
policy;
|
|
234
|
+
sessionId;
|
|
235
|
+
onRevoke;
|
|
236
|
+
createdAt;
|
|
237
|
+
lastActivityAt;
|
|
238
|
+
idleTimer = null;
|
|
239
|
+
absoluteTimer = null;
|
|
240
|
+
visibilityHandler = null;
|
|
241
|
+
constructor(opts) {
|
|
242
|
+
this.policy = opts.policy;
|
|
243
|
+
this.sessionId = opts.sessionId;
|
|
244
|
+
this.onRevoke = opts.onRevoke;
|
|
245
|
+
this.createdAt = Date.now();
|
|
246
|
+
this.lastActivityAt = Date.now();
|
|
247
|
+
this.scheduleIdleTimer();
|
|
248
|
+
this.scheduleAbsoluteTimer();
|
|
249
|
+
this.registerBackgroundLock();
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Record an activity timestamp and reset the idle timer.
|
|
253
|
+
* Call this at the top of every Noydb public method.
|
|
254
|
+
*/
|
|
255
|
+
touch() {
|
|
256
|
+
this.lastActivityAt = Date.now();
|
|
257
|
+
this.scheduleIdleTimer();
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Check whether the given operation is allowed under the active policy.
|
|
261
|
+
* Throws `SessionPolicyError` if the operation requires re-authentication.
|
|
262
|
+
* Throws `SessionExpiredError` if the absolute timeout has been exceeded
|
|
263
|
+
* (defensive check in case the timer fired before the call arrived).
|
|
264
|
+
*
|
|
265
|
+
* This is a synchronous check — callers don't await it.
|
|
266
|
+
*/
|
|
267
|
+
checkOperation(op) {
|
|
268
|
+
const { absoluteTimeoutMs } = this.policy;
|
|
269
|
+
if (absoluteTimeoutMs !== void 0 && Date.now() - this.createdAt >= absoluteTimeoutMs) {
|
|
270
|
+
this.expire("absolute");
|
|
271
|
+
throw new SessionExpiredError(this.sessionId);
|
|
272
|
+
}
|
|
273
|
+
const required = this.policy.requireReAuthFor ?? [];
|
|
274
|
+
if (required.includes(op)) {
|
|
275
|
+
throw new SessionPolicyError(op);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Tear down timers and background-lock listener. Call from `Noydb.close()`
|
|
280
|
+
* and whenever the session is revoked externally.
|
|
281
|
+
*/
|
|
282
|
+
destroy() {
|
|
283
|
+
if (this.idleTimer) {
|
|
284
|
+
clearTimeout(this.idleTimer);
|
|
285
|
+
this.idleTimer = null;
|
|
286
|
+
}
|
|
287
|
+
if (this.absoluteTimer) {
|
|
288
|
+
clearTimeout(this.absoluteTimer);
|
|
289
|
+
this.absoluteTimer = null;
|
|
290
|
+
}
|
|
291
|
+
if (this.visibilityHandler && typeof document !== "undefined") {
|
|
292
|
+
document.removeEventListener("visibilitychange", this.visibilityHandler);
|
|
293
|
+
this.visibilityHandler = null;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/** How long since the last activity, in ms. */
|
|
297
|
+
get idleMs() {
|
|
298
|
+
return Date.now() - this.lastActivityAt;
|
|
299
|
+
}
|
|
300
|
+
/** How long since session creation, in ms. */
|
|
301
|
+
get ageMs() {
|
|
302
|
+
return Date.now() - this.createdAt;
|
|
303
|
+
}
|
|
304
|
+
// ── Private ──────────────────────────────────────────────────────────
|
|
305
|
+
scheduleIdleTimer() {
|
|
306
|
+
const { idleTimeoutMs } = this.policy;
|
|
307
|
+
if (!idleTimeoutMs) return;
|
|
308
|
+
if (this.idleTimer) clearTimeout(this.idleTimer);
|
|
309
|
+
this.idleTimer = setTimeout(() => {
|
|
310
|
+
this.expire("idle");
|
|
311
|
+
}, idleTimeoutMs);
|
|
312
|
+
}
|
|
313
|
+
scheduleAbsoluteTimer() {
|
|
314
|
+
const { absoluteTimeoutMs } = this.policy;
|
|
315
|
+
if (!absoluteTimeoutMs) return;
|
|
316
|
+
if (this.absoluteTimer) clearTimeout(this.absoluteTimer);
|
|
317
|
+
this.absoluteTimer = setTimeout(() => {
|
|
318
|
+
this.expire("absolute");
|
|
319
|
+
}, absoluteTimeoutMs);
|
|
320
|
+
}
|
|
321
|
+
registerBackgroundLock() {
|
|
322
|
+
if (!this.policy.lockOnBackground) return;
|
|
323
|
+
if (typeof document === "undefined") return;
|
|
324
|
+
this.visibilityHandler = () => {
|
|
325
|
+
if (document.hidden) {
|
|
326
|
+
this.expire("background");
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
document.addEventListener("visibilitychange", this.visibilityHandler);
|
|
330
|
+
}
|
|
331
|
+
expire(reason) {
|
|
332
|
+
this.destroy();
|
|
333
|
+
revokeSession(this.sessionId);
|
|
334
|
+
this.onRevoke(reason);
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
function createEnforcer(opts) {
|
|
338
|
+
return new PolicyEnforcer(opts);
|
|
339
|
+
}
|
|
340
|
+
function validateSessionPolicy(policy) {
|
|
341
|
+
const { idleTimeoutMs, absoluteTimeoutMs } = policy;
|
|
342
|
+
if (idleTimeoutMs !== void 0 && (typeof idleTimeoutMs !== "number" || idleTimeoutMs <= 0)) {
|
|
343
|
+
throw new Error(`SessionPolicy.idleTimeoutMs must be a positive number, got ${idleTimeoutMs}`);
|
|
344
|
+
}
|
|
345
|
+
if (absoluteTimeoutMs !== void 0 && (typeof absoluteTimeoutMs !== "number" || absoluteTimeoutMs <= 0)) {
|
|
346
|
+
throw new Error(`SessionPolicy.absoluteTimeoutMs must be a positive number, got ${absoluteTimeoutMs}`);
|
|
347
|
+
}
|
|
348
|
+
if (idleTimeoutMs !== void 0 && absoluteTimeoutMs !== void 0 && idleTimeoutMs >= absoluteTimeoutMs) {
|
|
349
|
+
throw new Error(
|
|
350
|
+
`SessionPolicy.idleTimeoutMs (${idleTimeoutMs}ms) must be less than absoluteTimeoutMs (${absoluteTimeoutMs}ms)`
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// src/session/dev-unlock.ts
|
|
356
|
+
var REQUIRED_ACKNOWLEDGE = "I-UNDERSTAND-THIS-DISABLES-UNLOCK-SECURITY";
|
|
357
|
+
var STORAGE_PREFIX = "noydb:dev-unlock:";
|
|
358
|
+
function assertDevEnvironment() {
|
|
359
|
+
if (typeof process !== "undefined" && process.env.NODE_ENV === "production") {
|
|
360
|
+
throw new ValidationError(
|
|
361
|
+
'devUnlock is not available in production builds. process.env.NODE_ENV is "production".'
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
if (typeof globalThis !== "undefined" && globalThis.__vite_is_production__ === true) {
|
|
365
|
+
throw new ValidationError("devUnlock is not available in production builds.");
|
|
366
|
+
}
|
|
367
|
+
if (typeof window !== "undefined" && typeof window.location !== "undefined") {
|
|
368
|
+
const host = window.location.hostname;
|
|
369
|
+
if (host !== "localhost" && host !== "127.0.0.1" && host !== "::1" && !host.endsWith(".local")) {
|
|
370
|
+
throw new ValidationError(
|
|
371
|
+
`devUnlock is only available on localhost. Current hostname: "${host}". Set NODE_ENV=development and run on localhost to use dev unlock.`
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function storageKey(vault, userId) {
|
|
377
|
+
return `${STORAGE_PREFIX}${vault}:${userId}`;
|
|
378
|
+
}
|
|
379
|
+
function resolveStorage(persistAcrossTabs) {
|
|
380
|
+
if (typeof window === "undefined") {
|
|
381
|
+
throw new ValidationError("devUnlock requires a browser environment (window.sessionStorage / window.localStorage).");
|
|
382
|
+
}
|
|
383
|
+
return persistAcrossTabs ? window.localStorage : window.sessionStorage;
|
|
384
|
+
}
|
|
385
|
+
async function enableDevUnlock(vault, userId, keyring, options) {
|
|
386
|
+
if (options.acknowledge !== REQUIRED_ACKNOWLEDGE) {
|
|
387
|
+
throw new ValidationError(
|
|
388
|
+
`devUnlock requires acknowledge: '${REQUIRED_ACKNOWLEDGE}'. Got: '${options.acknowledge}'. This is intentional \u2014 the full string must appear in your source.`
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
assertDevEnvironment();
|
|
392
|
+
const storage = resolveStorage(options.persistAcrossTabs);
|
|
393
|
+
const dekMap = {};
|
|
394
|
+
for (const [collName, dek] of keyring.deks) {
|
|
395
|
+
const raw = await globalThis.crypto.subtle.exportKey("raw", dek);
|
|
396
|
+
dekMap[collName] = bufferToBase64(raw);
|
|
397
|
+
}
|
|
398
|
+
const payload = JSON.stringify({
|
|
399
|
+
_noydb_dev_unlock: 1,
|
|
400
|
+
userId: keyring.userId,
|
|
401
|
+
displayName: keyring.displayName,
|
|
402
|
+
role: keyring.role,
|
|
403
|
+
permissions: keyring.permissions,
|
|
404
|
+
deks: dekMap,
|
|
405
|
+
salt: bufferToBase64(keyring.salt)
|
|
406
|
+
});
|
|
407
|
+
storage.setItem(storageKey(vault, userId), payload);
|
|
408
|
+
console.warn(
|
|
409
|
+
"%c\u26A0\uFE0F NOYDB DEV UNLOCK ACTIVE \u26A0\uFE0F",
|
|
410
|
+
"color: red; font-size: 16px; font-weight: bold",
|
|
411
|
+
`
|
|
412
|
+
|
|
413
|
+
Compartment "${vault}" user "${userId}" is stored in ${options.persistAcrossTabs ? "localStorage" : "sessionStorage"} in PLAINTEXT DEKs.
|
|
414
|
+
This is ONLY safe for local development. Never use in production.
|
|
415
|
+
Call clearDevUnlock() to remove.`
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
async function loadDevUnlock(vault, userId, options = {}) {
|
|
419
|
+
if (typeof window === "undefined") return null;
|
|
420
|
+
const storage = resolveStorage(options.persistAcrossTabs);
|
|
421
|
+
const raw = storage.getItem(storageKey(vault, userId));
|
|
422
|
+
if (!raw) return null;
|
|
423
|
+
let parsed;
|
|
424
|
+
try {
|
|
425
|
+
parsed = JSON.parse(raw);
|
|
426
|
+
} catch {
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
if (parsed._noydb_dev_unlock !== 1) return null;
|
|
430
|
+
const deks = /* @__PURE__ */ new Map();
|
|
431
|
+
for (const [collName, rawBase64] of Object.entries(parsed.deks)) {
|
|
432
|
+
const dek = await globalThis.crypto.subtle.importKey(
|
|
433
|
+
"raw",
|
|
434
|
+
base64ToBuffer(rawBase64),
|
|
435
|
+
{ name: "AES-GCM", length: 256 },
|
|
436
|
+
true,
|
|
437
|
+
["encrypt", "decrypt"]
|
|
438
|
+
);
|
|
439
|
+
deks.set(collName, dek);
|
|
440
|
+
}
|
|
441
|
+
return {
|
|
442
|
+
userId: parsed.userId,
|
|
443
|
+
displayName: parsed.displayName,
|
|
444
|
+
role: parsed.role,
|
|
445
|
+
permissions: parsed.permissions,
|
|
446
|
+
deks,
|
|
447
|
+
kek: null,
|
|
448
|
+
salt: base64ToBuffer(parsed.salt)
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
function clearDevUnlock(vault, userId, options = {}) {
|
|
452
|
+
if (typeof window === "undefined") return;
|
|
453
|
+
const storage = resolveStorage(options.persistAcrossTabs);
|
|
454
|
+
storage.removeItem(storageKey(vault, userId));
|
|
455
|
+
}
|
|
456
|
+
function isDevUnlockActive(vault, userId, options = {}) {
|
|
457
|
+
if (typeof window === "undefined") return false;
|
|
458
|
+
const storage = resolveStorage(options.persistAcrossTabs);
|
|
459
|
+
return storage.getItem(storageKey(vault, userId)) !== null;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// src/session/active.ts
|
|
463
|
+
function withSession() {
|
|
464
|
+
return {
|
|
465
|
+
validateSessionPolicy,
|
|
466
|
+
createEnforcer,
|
|
467
|
+
revokeAllSessions
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
471
|
+
0 && (module.exports = {
|
|
472
|
+
PolicyEnforcer,
|
|
473
|
+
activeSessionCount,
|
|
474
|
+
clearDevUnlock,
|
|
475
|
+
createEnforcer,
|
|
476
|
+
createSession,
|
|
477
|
+
enableDevUnlock,
|
|
478
|
+
isDevUnlockActive,
|
|
479
|
+
isSessionAlive,
|
|
480
|
+
loadDevUnlock,
|
|
481
|
+
resolveSession,
|
|
482
|
+
revokeAllSessions,
|
|
483
|
+
revokeSession,
|
|
484
|
+
validateSessionPolicy,
|
|
485
|
+
withSession
|
|
486
|
+
});
|
|
487
|
+
//# sourceMappingURL=index.cjs.map
|