dropspace 0.0.0-stage → 0.1.0

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.
Files changed (63) hide show
  1. package/README.md +17 -2
  2. package/dist/automerge/index.d.ts +103 -0
  3. package/dist/automerge/index.d.ts.map +1 -0
  4. package/dist/automerge/index.js +394 -0
  5. package/dist/automerge/index.js.map +1 -0
  6. package/dist/client/channel.d.ts +131 -0
  7. package/dist/client/channel.d.ts.map +1 -0
  8. package/dist/client/channel.js +566 -0
  9. package/dist/client/channel.js.map +1 -0
  10. package/dist/client/client.d.ts +310 -0
  11. package/dist/client/client.d.ts.map +1 -0
  12. package/dist/client/client.js +1176 -0
  13. package/dist/client/client.js.map +1 -0
  14. package/dist/client/events.d.ts +14 -0
  15. package/dist/client/events.d.ts.map +1 -0
  16. package/dist/client/events.js +71 -0
  17. package/dist/client/events.js.map +1 -0
  18. package/dist/client/identity.d.ts +89 -0
  19. package/dist/client/identity.d.ts.map +1 -0
  20. package/dist/client/identity.js +274 -0
  21. package/dist/client/identity.js.map +1 -0
  22. package/dist/client/index.d.ts +8 -0
  23. package/dist/client/index.d.ts.map +1 -0
  24. package/dist/client/index.js +8 -0
  25. package/dist/client/index.js.map +1 -0
  26. package/dist/client/peer-connection.d.ts +182 -0
  27. package/dist/client/peer-connection.d.ts.map +1 -0
  28. package/dist/client/peer-connection.js +593 -0
  29. package/dist/client/peer-connection.js.map +1 -0
  30. package/dist/client/peer-selection.d.ts +70 -0
  31. package/dist/client/peer-selection.d.ts.map +1 -0
  32. package/dist/client/peer-selection.js +193 -0
  33. package/dist/client/peer-selection.js.map +1 -0
  34. package/dist/client/storage.d.ts +118 -0
  35. package/dist/client/storage.d.ts.map +1 -0
  36. package/dist/client/storage.js +463 -0
  37. package/dist/client/storage.js.map +1 -0
  38. package/dist/files/index.d.ts +136 -0
  39. package/dist/files/index.d.ts.map +1 -0
  40. package/dist/files/index.js +701 -0
  41. package/dist/files/index.js.map +1 -0
  42. package/dist/files/sha256.d.ts +24 -0
  43. package/dist/files/sha256.d.ts.map +1 -0
  44. package/dist/files/sha256.js +133 -0
  45. package/dist/files/sha256.js.map +1 -0
  46. package/dist/index.d.ts +4 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +4 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/protocol/envelope.d.ts +61 -0
  51. package/dist/protocol/envelope.d.ts.map +1 -0
  52. package/dist/protocol/envelope.js +260 -0
  53. package/dist/protocol/envelope.js.map +1 -0
  54. package/dist/protocol/framing.d.ts +56 -0
  55. package/dist/protocol/framing.d.ts.map +1 -0
  56. package/dist/protocol/framing.js +93 -0
  57. package/dist/protocol/framing.js.map +1 -0
  58. package/dist/protocol/index.d.ts +3 -0
  59. package/dist/protocol/index.d.ts.map +1 -0
  60. package/dist/protocol/index.js +3 -0
  61. package/dist/protocol/index.js.map +1 -0
  62. package/fixtures/envelopes.json +132 -0
  63. package/package.json +43 -4
@@ -0,0 +1,70 @@
1
+ import type { PeerPresence, PermissionScope } from "../protocol/index.js";
2
+ export interface PeerConnectionQualityMetrics {
3
+ rttMs?: number;
4
+ recentSuccessCount: number;
5
+ recentFailureCount: number;
6
+ lastConnectedAt?: number;
7
+ }
8
+ export interface PeerCandidateInput {
9
+ presence: PeerPresence;
10
+ spaceMember: boolean;
11
+ revoked?: boolean;
12
+ hasHealthyActiveConnection: boolean;
13
+ quality?: PeerConnectionQualityMetrics;
14
+ }
15
+ export interface PeerSelectionQuery {
16
+ spaceId: string;
17
+ requiredPermissions: PermissionScope[];
18
+ requiredCapabilities: string[];
19
+ preferredDeviceId?: string;
20
+ requireOnline?: boolean;
21
+ }
22
+ export interface PeerScoreBreakdown {
23
+ deviceId: string;
24
+ totalScore: number;
25
+ preferredDeviceBonus: number;
26
+ healthyConnectionBonus: number;
27
+ qualityScore: number;
28
+ reasons: string[];
29
+ }
30
+ export interface PeerRejectionReason {
31
+ deviceId: string;
32
+ code: "REVOKED" | "NOT_SPACE_MEMBER" | "MISSING_PERMISSION" | "MISSING_CAPABILITY" | "OFFLINE";
33
+ detail: string;
34
+ }
35
+ export interface PeerSelectionDecision {
36
+ spaceId: string;
37
+ selectedDeviceId: string | null;
38
+ selectedPermissions: PermissionScope[];
39
+ explanation: string;
40
+ hysteresisApplied: boolean;
41
+ switchedFromDeviceId: string | null;
42
+ rankedCandidates: PeerScoreBreakdown[];
43
+ rejectedCandidates: PeerRejectionReason[];
44
+ evaluatedAt: number;
45
+ }
46
+ export interface SmartPeerSelectorOptions {
47
+ /** Minimum score improvement required to switch away from an eligible active selection */
48
+ hysteresisDelta?: number;
49
+ /** Minimum cooldown period (ms) before switching between eligible peers */
50
+ switchCooldownMs?: number;
51
+ now?: () => number;
52
+ }
53
+ /**
54
+ * Deterministic policy engine for smart peer selection (Section 8).
55
+ *
56
+ * Security Invariants:
57
+ * 1. Self-declared capabilities or deviceHints (e.g. "desktop", "alwaysOnHint") are NEVER treated as authorization.
58
+ * 2. Verified space membership and server-authorized permissions are mandatory gates before scoring.
59
+ * 3. Failover/switching to an alternative peer restricts operations strictly to that peer's granted permissions.
60
+ */
61
+ export declare class SmartPeerSelector {
62
+ private readonly hysteresisDelta;
63
+ private readonly switchCooldownMs;
64
+ private readonly now;
65
+ private readonly lastSelectedBySpace;
66
+ constructor(options?: SmartPeerSelectorOptions);
67
+ selectPeer(query: PeerSelectionQuery, candidates: readonly PeerCandidateInput[]): PeerSelectionDecision;
68
+ clearHistory(spaceId?: string): void;
69
+ }
70
+ //# sourceMappingURL=peer-selection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peer-selection.d.ts","sourceRoot":"","sources":["../../src/client/peer-selection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE1E,MAAM,WAAW,4BAA4B;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,YAAY,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0BAA0B,EAAE,OAAO,CAAC;IACpC,OAAO,CAAC,EAAE,4BAA4B,CAAC;CACxC;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB,EAAE,eAAe,EAAE,CAAC;IACvC,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EACA,SAAS,GACT,kBAAkB,GAClB,oBAAoB,GACpB,oBAAoB,GACpB,SAAS,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,mBAAmB,EAAE,eAAe,EAAE,CAAC;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,gBAAgB,EAAE,kBAAkB,EAAE,CAAC;IACvC,kBAAkB,EAAE,mBAAmB,EAAE,CAAC;IAC1C,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,wBAAwB;IACvC,0FAA0F;IAC1F,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAGhC;gBAEQ,OAAO,GAAE,wBAA6B;IAM3C,UAAU,CACf,KAAK,EAAE,kBAAkB,EACzB,UAAU,EAAE,SAAS,kBAAkB,EAAE,GACxC,qBAAqB;IA+LjB,YAAY,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI;CAO5C"}
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Deterministic policy engine for smart peer selection (Section 8).
3
+ *
4
+ * Security Invariants:
5
+ * 1. Self-declared capabilities or deviceHints (e.g. "desktop", "alwaysOnHint") are NEVER treated as authorization.
6
+ * 2. Verified space membership and server-authorized permissions are mandatory gates before scoring.
7
+ * 3. Failover/switching to an alternative peer restricts operations strictly to that peer's granted permissions.
8
+ */
9
+ export class SmartPeerSelector {
10
+ hysteresisDelta;
11
+ switchCooldownMs;
12
+ now;
13
+ lastSelectedBySpace = new Map();
14
+ constructor(options = {}) {
15
+ this.hysteresisDelta = options.hysteresisDelta ?? 25;
16
+ this.switchCooldownMs = options.switchCooldownMs ?? 10_000;
17
+ this.now = options.now ?? (() => Date.now());
18
+ }
19
+ selectPeer(query, candidates) {
20
+ const nowMs = this.now();
21
+ const requireOnline = query.requireOnline ?? true;
22
+ const ranked = [];
23
+ const rejected = [];
24
+ for (let i = 0; i < candidates.length; i++) {
25
+ const candidate = candidates[i];
26
+ const { presence } = candidate;
27
+ // 1. Revocation & Space membership gate
28
+ if (candidate.revoked) {
29
+ rejected.push({
30
+ deviceId: presence.deviceId,
31
+ code: "REVOKED",
32
+ detail: "Device access has been revoked",
33
+ });
34
+ continue;
35
+ }
36
+ if (!candidate.spaceMember) {
37
+ rejected.push({
38
+ deviceId: presence.deviceId,
39
+ code: "NOT_SPACE_MEMBER",
40
+ detail: `Device is not an authorized member of space '${query.spaceId}'`,
41
+ });
42
+ continue;
43
+ }
44
+ // 2. Permission gate (Capability != Permission; must check server-verified permissions)
45
+ const grantedSet = new Set(presence.permissions);
46
+ const missingPermission = query.requiredPermissions.find((p) => !grantedSet.has(p));
47
+ if (missingPermission) {
48
+ rejected.push({
49
+ deviceId: presence.deviceId,
50
+ code: "MISSING_PERMISSION",
51
+ detail: `Missing required permission '${missingPermission}'`,
52
+ });
53
+ continue;
54
+ }
55
+ // 3. Capability gate
56
+ const capSet = new Set(presence.capabilities);
57
+ const missingCapability = query.requiredCapabilities.find((c) => !capSet.has(c));
58
+ if (missingCapability) {
59
+ rejected.push({
60
+ deviceId: presence.deviceId,
61
+ code: "MISSING_CAPABILITY",
62
+ detail: `Peer does not advertise capability '${missingCapability}'`,
63
+ });
64
+ continue;
65
+ }
66
+ // 4. Online / reachable gate
67
+ if (requireOnline && !presence.online) {
68
+ rejected.push({
69
+ deviceId: presence.deviceId,
70
+ code: "OFFLINE",
71
+ detail: "Peer is currently offline",
72
+ });
73
+ continue;
74
+ }
75
+ // Eligible candidate scoring (deterministic)
76
+ const reasons = [
77
+ `Authorized in space '${query.spaceId}' with permissions [${query.requiredPermissions.join(", ")}]`,
78
+ ];
79
+ let preferredDeviceBonus = 0;
80
+ if (query.preferredDeviceId && presence.deviceId === query.preferredDeviceId) {
81
+ preferredDeviceBonus = 100;
82
+ reasons.push("Matches user-preferred device (+100)");
83
+ }
84
+ let healthyConnectionBonus = 0;
85
+ if (candidate.hasHealthyActiveConnection) {
86
+ healthyConnectionBonus = 60;
87
+ reasons.push("Has existing healthy WebRTC connection (+60)");
88
+ }
89
+ let qualityScore = 0;
90
+ if (candidate.quality) {
91
+ const { rttMs, recentSuccessCount, recentFailureCount } = candidate.quality;
92
+ if (typeof rttMs === "number" && Number.isFinite(rttMs) && rttMs >= 0) {
93
+ // Up to +25 points for low latency
94
+ const rttPoints = Math.max(0, Math.round(25 - Math.min(250, rttMs) / 10));
95
+ qualityScore += rttPoints;
96
+ reasons.push(`RTT ${Math.round(rttMs)}ms (+${rttPoints})`);
97
+ }
98
+ const reliabilityPoints = Math.max(-30, Math.min(20, recentSuccessCount * 4 - recentFailureCount * 10));
99
+ if (reliabilityPoints !== 0) {
100
+ qualityScore += reliabilityPoints;
101
+ reasons.push(`Historical reliability (${recentSuccessCount} ok / ${recentFailureCount} fail: ${reliabilityPoints >= 0 ? "+" : ""}${reliabilityPoints})`);
102
+ }
103
+ }
104
+ const totalScore = 100 + preferredDeviceBonus + healthyConnectionBonus + qualityScore;
105
+ ranked.push({
106
+ deviceId: presence.deviceId,
107
+ totalScore,
108
+ preferredDeviceBonus,
109
+ healthyConnectionBonus,
110
+ qualityScore,
111
+ reasons,
112
+ });
113
+ }
114
+ // Deterministic sort: highest totalScore first; tie-break by lexicographical deviceId ascending
115
+ ranked.sort((a, b) => {
116
+ if (b.totalScore !== a.totalScore) {
117
+ return b.totalScore - a.totalScore;
118
+ }
119
+ return a.deviceId.localeCompare(b.deviceId);
120
+ });
121
+ if (ranked.length === 0) {
122
+ return {
123
+ spaceId: query.spaceId,
124
+ selectedDeviceId: null,
125
+ selectedPermissions: [],
126
+ explanation: "No eligible peer met space membership, permission, capability, and reachability criteria",
127
+ hysteresisApplied: false,
128
+ switchedFromDeviceId: null,
129
+ rankedCandidates: [],
130
+ rejectedCandidates: rejected,
131
+ evaluatedAt: nowMs,
132
+ };
133
+ }
134
+ const topCandidate = ranked[0];
135
+ const prev = this.lastSelectedBySpace.get(query.spaceId);
136
+ let chosen = topCandidate;
137
+ let hysteresisApplied = false;
138
+ let switchedFromDeviceId = null;
139
+ if (prev && prev.deviceId !== topCandidate.deviceId) {
140
+ const prevStillEligible = ranked.find((r) => r.deviceId === prev.deviceId);
141
+ if (prevStillEligible) {
142
+ const scoreDiff = topCandidate.totalScore - prevStillEligible.totalScore;
143
+ const withinCooldown = nowMs - prev.selectedAt < this.switchCooldownMs;
144
+ // Do not flip-flop unless user explicitly changed preferredDeviceId to the new top candidate
145
+ const isExplicitUserPreference = query.preferredDeviceId === topCandidate.deviceId;
146
+ if (!isExplicitUserPreference &&
147
+ (scoreDiff < this.hysteresisDelta || withinCooldown)) {
148
+ chosen = prevStillEligible;
149
+ hysteresisApplied = true;
150
+ }
151
+ else {
152
+ switchedFromDeviceId = prev.deviceId;
153
+ }
154
+ }
155
+ else {
156
+ switchedFromDeviceId = prev.deviceId;
157
+ }
158
+ }
159
+ if (!hysteresisApplied) {
160
+ this.lastSelectedBySpace.set(query.spaceId, {
161
+ deviceId: chosen.deviceId,
162
+ score: chosen.totalScore,
163
+ selectedAt: nowMs,
164
+ });
165
+ }
166
+ const chosenInput = candidates.find((c) => c.presence.deviceId === chosen.deviceId);
167
+ // Never expand permissions beyond what the selected peer actually holds
168
+ const selectedPermissions = [...chosenInput.presence.permissions];
169
+ const explanation = hysteresisApplied
170
+ ? `Retained active peer '${chosen.deviceId}' (score ${chosen.totalScore}) due to hysteresis/cooldown over '${topCandidate.deviceId}' (score ${topCandidate.totalScore})`
171
+ : `Selected peer '${chosen.deviceId}' (score ${chosen.totalScore}): ${chosen.reasons.join("; ")}`;
172
+ return {
173
+ spaceId: query.spaceId,
174
+ selectedDeviceId: chosen.deviceId,
175
+ selectedPermissions,
176
+ explanation,
177
+ hysteresisApplied,
178
+ switchedFromDeviceId,
179
+ rankedCandidates: ranked,
180
+ rejectedCandidates: rejected,
181
+ evaluatedAt: nowMs,
182
+ };
183
+ }
184
+ clearHistory(spaceId) {
185
+ if (spaceId) {
186
+ this.lastSelectedBySpace.delete(spaceId);
187
+ }
188
+ else {
189
+ this.lastSelectedBySpace.clear();
190
+ }
191
+ }
192
+ }
193
+ //# sourceMappingURL=peer-selection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peer-selection.js","sourceRoot":"","sources":["../../src/client/peer-selection.ts"],"names":[],"mappings":"AAiEA;;;;;;;GAOG;AACH,MAAM,OAAO,iBAAiB;IACX,eAAe,CAAS;IACxB,gBAAgB,CAAS;IACzB,GAAG,CAAe;IAClB,mBAAmB,GAAG,IAAI,GAAG,EAG3C,CAAC;IAEJ,YAAY,UAAoC,EAAE;QAChD,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,EAAE,CAAC;QACrD,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,MAAM,CAAC;QAC3D,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC/C,CAAC;IAEM,UAAU,CACf,KAAyB,EACzB,UAAyC;QAEzC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzB,MAAM,aAAa,GAAG,KAAK,CAAC,aAAa,IAAI,IAAI,CAAC;QAClD,MAAM,MAAM,GAAyB,EAAE,CAAC;QACxC,MAAM,QAAQ,GAA0B,EAAE,CAAC;QAE3C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3C,MAAM,SAAS,GAAG,UAAU,CAAC,CAAC,CAAE,CAAC;YACjC,MAAM,EAAE,QAAQ,EAAE,GAAG,SAAS,CAAC;YAE/B,wCAAwC;YACxC,IAAI,SAAS,CAAC,OAAO,EAAE,CAAC;gBACtB,QAAQ,CAAC,IAAI,CAAC;oBACZ,QAAQ,EAAE,QAAQ,CAAC,QAAQ;oBAC3B,IAAI,EAAE,SAAS;oBACf,MAAM,EAAE,gCAAgC;iBACzC,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;gBAC3B,QAAQ,CAAC,IAAI,CAAC;oBACZ,QAAQ,EAAE,QAAQ,CAAC,QAAQ;oBAC3B,IAAI,EAAE,kBAAkB;oBACxB,MAAM,EAAE,gDAAgD,KAAK,CAAC,OAAO,GAAG;iBACzE,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,wFAAwF;YACxF,MAAM,UAAU,GAAG,IAAI,GAAG,CAAkB,QAAQ,CAAC,WAAW,CAAC,CAAC;YAClE,MAAM,iBAAiB,GAAG,KAAK,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACpF,IAAI,iBAAiB,EAAE,CAAC;gBACtB,QAAQ,CAAC,IAAI,CAAC;oBACZ,QAAQ,EAAE,QAAQ,CAAC,QAAQ;oBAC3B,IAAI,EAAE,oBAAoB;oBAC1B,MAAM,EAAE,gCAAgC,iBAAiB,GAAG;iBAC7D,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,qBAAqB;YACrB,MAAM,MAAM,GAAG,IAAI,GAAG,CAAS,QAAQ,CAAC,YAAY,CAAC,CAAC;YACtD,MAAM,iBAAiB,GAAG,KAAK,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACjF,IAAI,iBAAiB,EAAE,CAAC;gBACtB,QAAQ,CAAC,IAAI,CAAC;oBACZ,QAAQ,EAAE,QAAQ,CAAC,QAAQ;oBAC3B,IAAI,EAAE,oBAAoB;oBAC1B,MAAM,EAAE,uCAAuC,iBAAiB,GAAG;iBACpE,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,6BAA6B;YAC7B,IAAI,aAAa,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;gBACtC,QAAQ,CAAC,IAAI,CAAC;oBACZ,QAAQ,EAAE,QAAQ,CAAC,QAAQ;oBAC3B,IAAI,EAAE,SAAS;oBACf,MAAM,EAAE,2BAA2B;iBACpC,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,6CAA6C;YAC7C,MAAM,OAAO,GAAa;gBACxB,wBAAwB,KAAK,CAAC,OAAO,uBAAuB,KAAK,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;aACpG,CAAC;YAEF,IAAI,oBAAoB,GAAG,CAAC,CAAC;YAC7B,IAAI,KAAK,CAAC,iBAAiB,IAAI,QAAQ,CAAC,QAAQ,KAAK,KAAK,CAAC,iBAAiB,EAAE,CAAC;gBAC7E,oBAAoB,GAAG,GAAG,CAAC;gBAC3B,OAAO,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;YACvD,CAAC;YAED,IAAI,sBAAsB,GAAG,CAAC,CAAC;YAC/B,IAAI,SAAS,CAAC,0BAA0B,EAAE,CAAC;gBACzC,sBAAsB,GAAG,EAAE,CAAC;gBAC5B,OAAO,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;YAC/D,CAAC;YAED,IAAI,YAAY,GAAG,CAAC,CAAC;YACrB,IAAI,SAAS,CAAC,OAAO,EAAE,CAAC;gBACtB,MAAM,EAAE,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,GAAG,SAAS,CAAC,OAAO,CAAC;gBAC5E,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;oBACtE,mCAAmC;oBACnC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;oBAC1E,YAAY,IAAI,SAAS,CAAC;oBAC1B,OAAO,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,SAAS,GAAG,CAAC,CAAC;gBAC7D,CAAC;gBACD,MAAM,iBAAiB,GAAG,IAAI,CAAC,GAAG,CAChC,CAAC,EAAE,EACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,kBAAkB,GAAG,CAAC,GAAG,kBAAkB,GAAG,EAAE,CAAC,CAC/D,CAAC;gBACF,IAAI,iBAAiB,KAAK,CAAC,EAAE,CAAC;oBAC5B,YAAY,IAAI,iBAAiB,CAAC;oBAClC,OAAO,CAAC,IAAI,CACV,2BAA2B,kBAAkB,SAAS,kBAAkB,UAAU,iBAAiB,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,iBAAiB,GAAG,CAC3I,CAAC;gBACJ,CAAC;YACH,CAAC;YAED,MAAM,UAAU,GAAG,GAAG,GAAG,oBAAoB,GAAG,sBAAsB,GAAG,YAAY,CAAC;YACtF,MAAM,CAAC,IAAI,CAAC;gBACV,QAAQ,EAAE,QAAQ,CAAC,QAAQ;gBAC3B,UAAU;gBACV,oBAAoB;gBACpB,sBAAsB;gBACtB,YAAY;gBACZ,OAAO;aACR,CAAC,CAAC;QACL,CAAC;QAED,gGAAgG;QAChG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YACnB,IAAI,CAAC,CAAC,UAAU,KAAK,CAAC,CAAC,UAAU,EAAE,CAAC;gBAClC,OAAO,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC;YACrC,CAAC;YACD,OAAO,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;QAEH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,OAAO;gBACL,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,gBAAgB,EAAE,IAAI;gBACtB,mBAAmB,EAAE,EAAE;gBACvB,WAAW,EAAE,0FAA0F;gBACvG,iBAAiB,EAAE,KAAK;gBACxB,oBAAoB,EAAE,IAAI;gBAC1B,gBAAgB,EAAE,EAAE;gBACpB,kBAAkB,EAAE,QAAQ;gBAC5B,WAAW,EAAE,KAAK;aACnB,CAAC;QACJ,CAAC;QAED,MAAM,YAAY,GAAG,MAAM,CAAC,CAAC,CAAE,CAAC;QAChC,MAAM,IAAI,GAAG,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACzD,IAAI,MAAM,GAAG,YAAY,CAAC;QAC1B,IAAI,iBAAiB,GAAG,KAAK,CAAC;QAC9B,IAAI,oBAAoB,GAAkB,IAAI,CAAC;QAE/C,IAAI,IAAI,IAAI,IAAI,CAAC,QAAQ,KAAK,YAAY,CAAC,QAAQ,EAAE,CAAC;YACpD,MAAM,iBAAiB,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC3E,IAAI,iBAAiB,EAAE,CAAC;gBACtB,MAAM,SAAS,GAAG,YAAY,CAAC,UAAU,GAAG,iBAAiB,CAAC,UAAU,CAAC;gBACzE,MAAM,cAAc,GAAG,KAAK,GAAG,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,gBAAgB,CAAC;gBACvE,6FAA6F;gBAC7F,MAAM,wBAAwB,GAC5B,KAAK,CAAC,iBAAiB,KAAK,YAAY,CAAC,QAAQ,CAAC;gBACpD,IACE,CAAC,wBAAwB;oBACzB,CAAC,SAAS,GAAG,IAAI,CAAC,eAAe,IAAI,cAAc,CAAC,EACpD,CAAC;oBACD,MAAM,GAAG,iBAAiB,CAAC;oBAC3B,iBAAiB,GAAG,IAAI,CAAC;gBAC3B,CAAC;qBAAM,CAAC;oBACN,oBAAoB,GAAG,IAAI,CAAC,QAAQ,CAAC;gBACvC,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,oBAAoB,GAAG,IAAI,CAAC,QAAQ,CAAC;YACvC,CAAC;QACH,CAAC;QAED,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACvB,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE;gBAC1C,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,KAAK,EAAE,MAAM,CAAC,UAAU;gBACxB,UAAU,EAAE,KAAK;aAClB,CAAC,CAAC;QACL,CAAC;QAED,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,KAAK,MAAM,CAAC,QAAQ,CAAE,CAAC;QACrF,wEAAwE;QACxE,MAAM,mBAAmB,GAAG,CAAC,GAAG,WAAW,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAElE,MAAM,WAAW,GAAG,iBAAiB;YACnC,CAAC,CAAC,yBAAyB,MAAM,CAAC,QAAQ,YAAY,MAAM,CAAC,UAAU,sCAAsC,YAAY,CAAC,QAAQ,YAAY,YAAY,CAAC,UAAU,GAAG;YACxK,CAAC,CAAC,kBAAkB,MAAM,CAAC,QAAQ,YAAY,MAAM,CAAC,UAAU,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAEpG,OAAO;YACL,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,gBAAgB,EAAE,MAAM,CAAC,QAAQ;YACjC,mBAAmB;YACnB,WAAW;YACX,iBAAiB;YACjB,oBAAoB;YACpB,gBAAgB,EAAE,MAAM;YACxB,kBAAkB,EAAE,QAAQ;YAC5B,WAAW,EAAE,KAAK;SACnB,CAAC;IACJ,CAAC;IAEM,YAAY,CAAC,OAAgB;QAClC,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC3C,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,mBAAmB,CAAC,KAAK,EAAE,CAAC;QACnC,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,118 @@
1
+ export type StorageErrorCode = "QUOTA_EXCEEDED" | "PERMISSION_DENIED" | "NOT_SUPPORTED" | "NOT_FOUND" | "PATH_TRAVERSAL_BLOCKED" | "OVERWRITE_BLOCKED" | "IO_ERROR";
2
+ export declare class DropspaceStorageError extends Error {
3
+ readonly code: StorageErrorCode;
4
+ readonly causeError?: unknown | undefined;
5
+ constructor(message: string, code: StorageErrorCode, causeError?: unknown | undefined);
6
+ }
7
+ export interface StorageCapabilities {
8
+ indexedDb: boolean;
9
+ opfs: boolean;
10
+ fileSystemAccess: boolean;
11
+ storageEstimate: boolean;
12
+ persistentStorageRequest: boolean;
13
+ }
14
+ export interface StorageQuotaInfo {
15
+ usageBytes: number;
16
+ quotaBytes: number;
17
+ usageRatio: number;
18
+ persisted: boolean | null;
19
+ supported: boolean;
20
+ }
21
+ /**
22
+ * Runtime feature detection for browser storage APIs.
23
+ * Never accesses browser globals at module import time (SSR safe).
24
+ */
25
+ export declare function detectStorageCapabilities(): StorageCapabilities;
26
+ export declare function getStorageQuotaEstimate(): Promise<StorageQuotaInfo>;
27
+ export declare function requestPersistentStorage(): Promise<boolean>;
28
+ export declare function normalizeStorageError(err: unknown): DropspaceStorageError;
29
+ /**
30
+ * Sanitizes an untrusted remote filename to prevent path traversal, null bytes,
31
+ * reserved OS device names, and hidden dot-files.
32
+ */
33
+ export declare function sanitizeFileName(rawName: string): string;
34
+ export interface BinaryStagingStorageAdapter {
35
+ readonly kind: "opfs" | "indexeddb" | "memory";
36
+ writeChunk(transferId: string, chunkIndex: number, data: Uint8Array): Promise<void>;
37
+ readChunk(transferId: string, chunkIndex: number): Promise<Uint8Array | null>;
38
+ listWrittenChunks(transferId: string): Promise<number[]>;
39
+ finalizeTransfer(transferId: string, safeFileName: string, totalChunks: number, options?: {
40
+ allowOverwrite?: boolean;
41
+ }): Promise<{
42
+ finalKey: string;
43
+ byteLength: number;
44
+ }>;
45
+ readCompletedFile(finalKey: string): Promise<Uint8Array | null>;
46
+ deleteTransferStaging(transferId: string): Promise<void>;
47
+ exportBackup(): Promise<Record<string, string>>;
48
+ importBackup(snapshot: Record<string, string>): Promise<void>;
49
+ }
50
+ /**
51
+ * In-memory staging & file storage adapter with configurable quota limit (for deterministic quota testing and SSR).
52
+ */
53
+ export declare class InMemoryBinaryStagingAdapter implements BinaryStagingStorageAdapter {
54
+ private maxQuotaBytes;
55
+ readonly kind: "memory";
56
+ private readonly stagingChunks;
57
+ private readonly completedFiles;
58
+ private currentBytes;
59
+ constructor(maxQuotaBytes?: number);
60
+ setMaxQuotaBytes(bytes: number): void;
61
+ getUsedBytes(): number;
62
+ writeChunk(transferId: string, chunkIndex: number, data: Uint8Array): Promise<void>;
63
+ readChunk(transferId: string, chunkIndex: number): Promise<Uint8Array | null>;
64
+ listWrittenChunks(transferId: string): Promise<number[]>;
65
+ finalizeTransfer(transferId: string, rawFileName: string, totalChunks: number, options?: {
66
+ allowOverwrite?: boolean;
67
+ }): Promise<{
68
+ finalKey: string;
69
+ byteLength: number;
70
+ }>;
71
+ readCompletedFile(finalKey: string): Promise<Uint8Array | null>;
72
+ deleteTransferStaging(transferId: string): Promise<void>;
73
+ exportBackup(): Promise<Record<string, string>>;
74
+ importBackup(snapshot: Record<string, string>): Promise<void>;
75
+ }
76
+ /**
77
+ * Origin Private File System (OPFS) adapter for large binary staging and completed files.
78
+ * Keeps incomplete transfers in `.staging/<transferId>/chunk_<idx>.part` and promotes to `completed/<safeFileName>`.
79
+ */
80
+ export declare class OpfsBinaryStagingAdapter implements BinaryStagingStorageAdapter {
81
+ readonly kind: "opfs";
82
+ private getRootDir;
83
+ private getTransferStagingDir;
84
+ private getCompletedDir;
85
+ writeChunk(transferId: string, chunkIndex: number, data: Uint8Array): Promise<void>;
86
+ readChunk(transferId: string, chunkIndex: number): Promise<Uint8Array | null>;
87
+ listWrittenChunks(transferId: string): Promise<number[]>;
88
+ finalizeTransfer(transferId: string, rawFileName: string, totalChunks: number, options?: {
89
+ allowOverwrite?: boolean;
90
+ }): Promise<{
91
+ finalKey: string;
92
+ byteLength: number;
93
+ }>;
94
+ readCompletedFile(finalKey: string): Promise<Uint8Array | null>;
95
+ deleteTransferStaging(transferId: string): Promise<void>;
96
+ exportBackup(): Promise<Record<string, string>>;
97
+ importBackup(snapshot: Record<string, string>): Promise<void>;
98
+ }
99
+ /**
100
+ * Real user directory adapter using File System Access API (`FileSystemDirectoryHandle`),
101
+ * strictly distinct from Origin-Private Storage (OPFS).
102
+ * Handles permission re-prompting on stored handles and download fallback when unsupported.
103
+ */
104
+ export declare class UserDirectoryStorageAdapter {
105
+ private directoryHandle;
106
+ constructor(directoryHandle?: FileSystemDirectoryHandle | null);
107
+ setDirectoryHandle(handle: FileSystemDirectoryHandle): void;
108
+ getDirectoryHandle(): FileSystemDirectoryHandle | null;
109
+ verifyOrRequestPermission(mode?: "read" | "readwrite"): Promise<boolean>;
110
+ saveFileToUserDirectory(rawFileName: string, content: Uint8Array, options?: {
111
+ allowOverwrite?: boolean;
112
+ }): Promise<void>;
113
+ /**
114
+ * Browser download fallback when File System Access API is unavailable.
115
+ */
116
+ triggerBrowserDownloadFallback(rawFileName: string, content: Uint8Array, mimeType?: string): void;
117
+ }
118
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/client/storage.ts"],"names":[],"mappings":"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"}