@symbo.ls/sdk 3.2.3 → 3.2.7

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 (183) hide show
  1. package/README.md +141 -0
  2. package/dist/cjs/config/environment.js +94 -10
  3. package/dist/cjs/index.js +152 -12
  4. package/dist/cjs/services/AdminService.js +351 -0
  5. package/dist/cjs/services/AuthService.js +738 -305
  6. package/dist/cjs/services/BaseService.js +158 -6
  7. package/dist/cjs/services/BranchService.js +484 -0
  8. package/dist/cjs/services/CollabService.js +439 -116
  9. package/dist/cjs/services/DnsService.js +340 -0
  10. package/dist/cjs/services/FeatureFlagService.js +175 -0
  11. package/dist/cjs/services/FileService.js +201 -0
  12. package/dist/cjs/services/IntegrationService.js +538 -0
  13. package/dist/cjs/services/MetricsService.js +62 -0
  14. package/dist/cjs/services/PaymentService.js +271 -0
  15. package/dist/cjs/services/PlanService.js +426 -0
  16. package/dist/cjs/services/ProjectService.js +1207 -0
  17. package/dist/cjs/services/PullRequestService.js +503 -0
  18. package/dist/cjs/services/ScreenshotService.js +304 -0
  19. package/dist/cjs/services/SubscriptionService.js +396 -0
  20. package/dist/cjs/services/TrackingService.js +661 -0
  21. package/dist/cjs/services/WaitlistService.js +148 -0
  22. package/dist/cjs/services/index.js +60 -4
  23. package/dist/cjs/state/RootStateManager.js +2 -23
  24. package/dist/cjs/state/rootEventBus.js +9 -0
  25. package/dist/cjs/utils/CollabClient.js +78 -12
  26. package/dist/cjs/utils/TokenManager.js +16 -3
  27. package/dist/cjs/utils/changePreprocessor.js +199 -0
  28. package/dist/cjs/utils/jsonDiff.js +46 -4
  29. package/dist/cjs/utils/ordering.js +309 -0
  30. package/dist/cjs/utils/services.js +285 -128
  31. package/dist/cjs/utils/validation.js +0 -3
  32. package/dist/esm/config/environment.js +94 -10
  33. package/dist/esm/index.js +47862 -18248
  34. package/dist/esm/services/AdminService.js +1132 -0
  35. package/dist/esm/services/AuthService.js +1493 -386
  36. package/dist/esm/services/BaseService.js +757 -6
  37. package/dist/esm/services/BranchService.js +1265 -0
  38. package/dist/esm/services/CollabService.js +24956 -16089
  39. package/dist/esm/services/DnsService.js +1121 -0
  40. package/dist/esm/services/FeatureFlagService.js +956 -0
  41. package/dist/esm/services/FileService.js +982 -0
  42. package/dist/esm/services/IntegrationService.js +1319 -0
  43. package/dist/esm/services/MetricsService.js +843 -0
  44. package/dist/esm/services/PaymentService.js +1052 -0
  45. package/dist/esm/services/PlanService.js +1207 -0
  46. package/dist/esm/services/ProjectService.js +2526 -0
  47. package/dist/esm/services/PullRequestService.js +1284 -0
  48. package/dist/esm/services/ScreenshotService.js +1085 -0
  49. package/dist/esm/services/SubscriptionService.js +1177 -0
  50. package/dist/esm/services/TrackingService.js +18454 -0
  51. package/dist/esm/services/WaitlistService.js +929 -0
  52. package/dist/esm/services/index.js +47373 -18027
  53. package/dist/esm/state/RootStateManager.js +11 -23
  54. package/dist/esm/state/rootEventBus.js +9 -0
  55. package/dist/esm/utils/CollabClient.js +17526 -16120
  56. package/dist/esm/utils/TokenManager.js +16 -3
  57. package/dist/esm/utils/changePreprocessor.js +542 -0
  58. package/dist/esm/utils/jsonDiff.js +958 -43
  59. package/dist/esm/utils/ordering.js +291 -0
  60. package/dist/esm/utils/services.js +285 -128
  61. package/dist/esm/utils/validation.js +116 -50
  62. package/dist/node/config/environment.js +94 -10
  63. package/dist/node/index.js +183 -16
  64. package/dist/node/services/AdminService.js +332 -0
  65. package/dist/node/services/AuthService.js +742 -310
  66. package/dist/node/services/BaseService.js +148 -6
  67. package/dist/node/services/BranchService.js +465 -0
  68. package/dist/node/services/CollabService.js +439 -116
  69. package/dist/node/services/DnsService.js +321 -0
  70. package/dist/node/services/FeatureFlagService.js +156 -0
  71. package/dist/node/services/FileService.js +182 -0
  72. package/dist/node/services/IntegrationService.js +519 -0
  73. package/dist/node/services/MetricsService.js +43 -0
  74. package/dist/node/services/PaymentService.js +252 -0
  75. package/dist/node/services/PlanService.js +407 -0
  76. package/dist/node/services/ProjectService.js +1188 -0
  77. package/dist/node/services/PullRequestService.js +484 -0
  78. package/dist/node/services/ScreenshotService.js +285 -0
  79. package/dist/node/services/SubscriptionService.js +377 -0
  80. package/dist/node/services/TrackingService.js +632 -0
  81. package/dist/node/services/WaitlistService.js +129 -0
  82. package/dist/node/services/index.js +60 -4
  83. package/dist/node/state/RootStateManager.js +2 -23
  84. package/dist/node/state/rootEventBus.js +9 -0
  85. package/dist/node/utils/CollabClient.js +77 -11
  86. package/dist/node/utils/TokenManager.js +16 -3
  87. package/dist/node/utils/changePreprocessor.js +180 -0
  88. package/dist/node/utils/jsonDiff.js +46 -4
  89. package/dist/node/utils/ordering.js +290 -0
  90. package/dist/node/utils/services.js +285 -128
  91. package/dist/node/utils/validation.js +0 -3
  92. package/package.json +30 -18
  93. package/src/config/environment.js +95 -10
  94. package/src/index.js +190 -23
  95. package/src/services/AdminService.js +374 -0
  96. package/src/services/AuthService.js +874 -328
  97. package/src/services/BaseService.js +166 -6
  98. package/src/services/BranchService.js +536 -0
  99. package/src/services/CollabService.js +557 -148
  100. package/src/services/DnsService.js +366 -0
  101. package/src/services/FeatureFlagService.js +174 -0
  102. package/src/services/FileService.js +213 -0
  103. package/src/services/IntegrationService.js +548 -0
  104. package/src/services/MetricsService.js +40 -0
  105. package/src/services/PaymentService.js +287 -0
  106. package/src/services/PlanService.js +468 -0
  107. package/src/services/ProjectService.js +1366 -0
  108. package/src/services/PullRequestService.js +537 -0
  109. package/src/services/ScreenshotService.js +258 -0
  110. package/src/services/SubscriptionService.js +425 -0
  111. package/src/services/TrackingService.js +853 -0
  112. package/src/services/WaitlistService.js +130 -0
  113. package/src/services/index.js +79 -5
  114. package/src/services/tests/BranchService/createBranch.test.js +153 -0
  115. package/src/services/tests/BranchService/deleteBranch.test.js +173 -0
  116. package/src/services/tests/BranchService/getBranchChanges.test.js +146 -0
  117. package/src/services/tests/BranchService/listBranches.test.js +87 -0
  118. package/src/services/tests/BranchService/mergeBranch.test.js +210 -0
  119. package/src/services/tests/BranchService/publishVersion.test.js +183 -0
  120. package/src/services/tests/BranchService/renameBranch.test.js +240 -0
  121. package/src/services/tests/BranchService/resetBranch.test.js +152 -0
  122. package/src/services/tests/FeatureFlagService/adminFeatureFlags.test.js +67 -0
  123. package/src/services/tests/FeatureFlagService/getFeatureFlags.test.js +75 -0
  124. package/src/services/tests/FileService/createFileFormData.test.js +74 -0
  125. package/src/services/tests/FileService/getFileUrl.test.js +69 -0
  126. package/src/services/tests/FileService/updateProjectIcon.test.js +109 -0
  127. package/src/services/tests/FileService/uploadDocument.test.js +36 -0
  128. package/src/services/tests/FileService/uploadFile.test.js +78 -0
  129. package/src/services/tests/FileService/uploadFileWithValidation.test.js +114 -0
  130. package/src/services/tests/FileService/uploadImage.test.js +36 -0
  131. package/src/services/tests/FileService/uploadMultipleFiles.test.js +111 -0
  132. package/src/services/tests/FileService/validateFile.test.js +63 -0
  133. package/src/services/tests/PlanService/createPlan.test.js +104 -0
  134. package/src/services/tests/PlanService/createPlanWithValidation.test.js +523 -0
  135. package/src/services/tests/PlanService/deletePlan.test.js +92 -0
  136. package/src/services/tests/PlanService/getActivePlans.test.js +123 -0
  137. package/src/services/tests/PlanService/getAdminPlans.test.js +84 -0
  138. package/src/services/tests/PlanService/getPlan.test.js +50 -0
  139. package/src/services/tests/PlanService/getPlanByKey.test.js +109 -0
  140. package/src/services/tests/PlanService/getPlanWithValidation.test.js +85 -0
  141. package/src/services/tests/PlanService/getPlans.test.js +53 -0
  142. package/src/services/tests/PlanService/getPlansByPriceRange.test.js +109 -0
  143. package/src/services/tests/PlanService/getPlansWithValidation.test.js +48 -0
  144. package/src/services/tests/PlanService/initializePlans.test.js +75 -0
  145. package/src/services/tests/PlanService/updatePlan.test.js +111 -0
  146. package/src/services/tests/PlanService/updatePlanWithValidation.test.js +556 -0
  147. package/src/state/RootStateManager.js +37 -32
  148. package/src/state/rootEventBus.js +19 -0
  149. package/src/utils/CollabClient.js +99 -12
  150. package/src/utils/TokenManager.js +20 -3
  151. package/src/utils/changePreprocessor.js +239 -0
  152. package/src/utils/jsonDiff.js +40 -5
  153. package/src/utils/ordering.js +271 -0
  154. package/src/utils/services.js +306 -139
  155. package/src/utils/validation.js +0 -3
  156. package/dist/cjs/services/AIService.js +0 -155
  157. package/dist/cjs/services/BasedService.js +0 -1185
  158. package/dist/cjs/services/CoreService.js +0 -2295
  159. package/dist/cjs/services/SocketService.js +0 -309
  160. package/dist/cjs/services/SymstoryService.js +0 -571
  161. package/dist/cjs/utils/basedQuerys.js +0 -181
  162. package/dist/cjs/utils/symstoryClient.js +0 -259
  163. package/dist/esm/services/AIService.js +0 -185
  164. package/dist/esm/services/BasedService.js +0 -5262
  165. package/dist/esm/services/CoreService.js +0 -2827
  166. package/dist/esm/services/SocketService.js +0 -456
  167. package/dist/esm/services/SymstoryService.js +0 -7025
  168. package/dist/esm/utils/basedQuerys.js +0 -163
  169. package/dist/esm/utils/symstoryClient.js +0 -354
  170. package/dist/node/services/AIService.js +0 -136
  171. package/dist/node/services/BasedService.js +0 -1156
  172. package/dist/node/services/CoreService.js +0 -2266
  173. package/dist/node/services/SocketService.js +0 -280
  174. package/dist/node/services/SymstoryService.js +0 -542
  175. package/dist/node/utils/basedQuerys.js +0 -162
  176. package/dist/node/utils/symstoryClient.js +0 -230
  177. package/src/services/AIService.js +0 -150
  178. package/src/services/BasedService.js +0 -1302
  179. package/src/services/CoreService.js +0 -2548
  180. package/src/services/SocketService.js +0 -336
  181. package/src/services/SymstoryService.js +0 -649
  182. package/src/utils/basedQuerys.js +0 -164
  183. package/src/utils/symstoryClient.js +0 -252
@@ -2,18 +2,54 @@ import { BaseService } from "./BaseService.js";
2
2
  import { CollabClient } from "../utils/CollabClient.js";
3
3
  import { RootStateManager } from "../state/RootStateManager.js";
4
4
  import { rootBus } from "../state/rootEventBus.js";
5
+ import { validateParams } from "../utils/validation.js";
6
+ import { deepStringifyFunctions } from "@domql/utils";
7
+ import { preprocessChanges } from "../utils/changePreprocessor.js";
8
+ const FUNCTION_META_KEYS = ["node", "__ref", "__element", "parent", "parse"];
9
+ function stringifyFunctionsForTransport(value, seen = /* @__PURE__ */ new WeakMap()) {
10
+ if (value === null || typeof value !== "object") {
11
+ return typeof value === "function" ? value.toString() : value;
12
+ }
13
+ if (seen.has(value)) {
14
+ return seen.get(value);
15
+ }
16
+ const clone = Array.isArray(value) ? [] : {};
17
+ seen.set(value, clone);
18
+ if (Array.isArray(value)) {
19
+ for (let i = 0; i < value.length; i++) {
20
+ clone[i] = stringifyFunctionsForTransport(value[i], seen);
21
+ }
22
+ return clone;
23
+ }
24
+ const keys = Object.keys(value);
25
+ for (let i = 0; i < keys.length; i++) {
26
+ const key = keys[i];
27
+ if (!FUNCTION_META_KEYS.includes(key)) {
28
+ clone[key] = stringifyFunctionsForTransport(value[key], seen);
29
+ }
30
+ }
31
+ return clone;
32
+ }
5
33
  class CollabService extends BaseService {
6
34
  constructor(config) {
7
35
  super(config);
8
36
  this._client = null;
9
37
  this._stateManager = null;
10
38
  this._connected = false;
39
+ this._connecting = false;
40
+ this._connectPromise = null;
41
+ this._connectionMeta = null;
42
+ this._pendingConnectReject = null;
11
43
  this._undoStack = [];
12
44
  this._redoStack = [];
13
45
  this._isUndoRedo = false;
14
46
  this._pendingOps = [];
47
+ this._onSocketConnect = this._onSocketConnect.bind(this);
48
+ this._onSocketDisconnect = this._onSocketDisconnect.bind(this);
49
+ this._onSocketError = this._onSocketError.bind(this);
15
50
  }
16
51
  init({ context }) {
52
+ super.init({ context });
17
53
  if (context == null ? void 0 : context.state) {
18
54
  try {
19
55
  this._stateManager = new RootStateManager(context.state);
@@ -41,90 +77,240 @@ class CollabService extends BaseService {
41
77
  * caller can react accordingly.
42
78
  */
43
79
  _ensureStateManager() {
44
- var _a;
80
+ var _a, _b;
45
81
  if (!this._stateManager) {
46
82
  if (!((_a = this._context) == null ? void 0 : _a.state)) {
47
83
  throw new Error("[CollabService] Cannot operate without root state");
48
84
  }
49
85
  this._stateManager = new RootStateManager(this._context.state);
50
86
  }
87
+ const root = (_b = this._stateManager) == null ? void 0 : _b.root;
88
+ if (root && !root.__element) {
89
+ root.__element = {
90
+ /**
91
+ * Very small subset of the DOMQL `call` API that we rely on inside the
92
+ * CollabService for browser notifications and data helpers.
93
+ * In a Node.js test context we simply log or return fallbacks.
94
+ */
95
+ call: (method, ...args) => {
96
+ switch (method) {
97
+ case "openNotification": {
98
+ const [payload = {}] = args;
99
+ const { type = "info", title = "", message = "" } = payload;
100
+ const logger = type === "error" ? console.error : console.log;
101
+ logger(`[Notification] ${title}${message ? ` \u2013 ${message}` : ""}`);
102
+ return;
103
+ }
104
+ case "deepStringifyFunctions": {
105
+ return deepStringifyFunctions(...args);
106
+ }
107
+ default:
108
+ return {};
109
+ }
110
+ }
111
+ };
112
+ }
51
113
  }
52
114
  /* ---------- Connection Management ---------- */
53
115
  async connect(options = {}) {
54
- var _a, _b, _c, _d, _e;
55
- this._ensureStateManager();
56
- const {
57
- authToken: jwt,
58
- projectId,
59
- branch = "main",
60
- pro
61
- } = {
62
- ...this._context,
63
- ...options
64
- };
65
- console.log(jwt, projectId, branch, pro);
66
- if (!projectId) {
67
- throw new Error("projectId is required for CollabService connection");
68
- }
69
- if (this._client) {
70
- await this.disconnect();
116
+ if (this._connectPromise) {
117
+ return this._connectPromise;
71
118
  }
72
- try {
119
+ this._connectPromise = (async () => {
120
+ var _a;
121
+ this._connecting = true;
122
+ this._connected = false;
123
+ this._ensureStateManager();
124
+ const mergedOptions = {
125
+ ...this._context,
126
+ ...options
127
+ };
128
+ let { authToken: jwt } = mergedOptions;
129
+ const { projectId, branch = "main", pro } = mergedOptions;
130
+ if (!jwt && this._tokenManager) {
131
+ try {
132
+ jwt = await this._tokenManager.ensureValidToken();
133
+ } catch (error) {
134
+ console.warn(
135
+ "[CollabService] Failed to obtain auth token from token manager",
136
+ error
137
+ );
138
+ }
139
+ if (!jwt && typeof this._tokenManager.getAccessToken === "function") {
140
+ jwt = this._tokenManager.getAccessToken();
141
+ }
142
+ }
143
+ if (!jwt) {
144
+ throw new Error("[CollabService] Cannot connect without auth token");
145
+ }
146
+ this._context = {
147
+ ...this._context,
148
+ authToken: jwt,
149
+ projectId,
150
+ branch,
151
+ pro
152
+ };
153
+ if (!projectId) {
154
+ const state = (_a = this._stateManager) == null ? void 0 : _a.root;
155
+ const el = state.__element;
156
+ el.call("openNotification", {
157
+ type: "error",
158
+ title: "projectId is required",
159
+ message: "projectId is required for CollabService connection"
160
+ });
161
+ throw new Error("projectId is required for CollabService connection");
162
+ }
163
+ if (this._client) {
164
+ await this.disconnect();
165
+ }
73
166
  this._client = new CollabClient({
74
167
  jwt,
75
168
  projectId,
76
169
  branch,
77
170
  live: Boolean(pro)
78
171
  });
79
- await new Promise((resolve) => {
80
- var _a2, _b2;
81
- if ((_a2 = this._client.socket) == null ? void 0 : _a2.connected) {
82
- resolve();
83
- } else {
84
- (_b2 = this._client.socket) == null ? void 0 : _b2.once("connect", resolve);
85
- }
86
- });
87
- console.log("[CollabService] socket connected");
88
- (_a = this._client.socket) == null ? void 0 : _a.on("ops", ({ changes }) => {
172
+ const { socket } = this._client;
173
+ try {
174
+ await new Promise((resolve, reject) => {
175
+ if (!socket) {
176
+ reject(new Error("[CollabService] Socket instance missing"));
177
+ return;
178
+ }
179
+ if (socket.connected) {
180
+ resolve();
181
+ return;
182
+ }
183
+ const cleanup = () => {
184
+ socket.off("connect", handleConnect);
185
+ socket.off("connect_error", handleError);
186
+ socket.off("error", handleError);
187
+ socket.off("disconnect", handleDisconnect);
188
+ if (this._pendingConnectReject === handleError) {
189
+ this._pendingConnectReject = null;
190
+ }
191
+ };
192
+ const handleConnect = () => {
193
+ cleanup();
194
+ resolve();
195
+ };
196
+ const handleError = (error) => {
197
+ cleanup();
198
+ reject(
199
+ error instanceof Error ? error : new Error(String(error || "Unknown connection error"))
200
+ );
201
+ };
202
+ const handleDisconnect = (reason) => {
203
+ handleError(
204
+ reason instanceof Error ? reason : new Error(
205
+ `[CollabService] Socket disconnected before connect: ${reason || "unknown"}`
206
+ )
207
+ );
208
+ };
209
+ this._pendingConnectReject = handleError;
210
+ socket.once("connect", handleConnect);
211
+ socket.once("connect_error", handleError);
212
+ socket.once("error", handleError);
213
+ socket.once("disconnect", handleDisconnect);
214
+ });
215
+ } catch (error) {
216
+ socket == null ? void 0 : socket.disconnect();
217
+ this._client = null;
218
+ this._connectionMeta = null;
219
+ throw error;
220
+ }
221
+ this._attachSocketLifecycleListeners();
222
+ if (socket == null ? void 0 : socket.connected) {
223
+ this._onSocketConnect();
224
+ }
225
+ socket == null ? void 0 : socket.on("ops", ({ changes }) => {
89
226
  console.log(`ops event`);
90
- console.log(changes);
91
227
  this._stateManager.applyChanges(changes, { fromSocket: true });
92
228
  });
93
- (_b = this._client.socket) == null ? void 0 : _b.on("commit", ({ version }) => {
94
- this._stateManager.setVersion(version);
229
+ socket == null ? void 0 : socket.on("commit", ({ version }) => {
230
+ if (version) {
231
+ this._stateManager.setVersion(version);
232
+ }
95
233
  rootBus.emit("checkpoint:done", { version, origin: "auto" });
96
234
  });
97
- (_c = this._client.socket) == null ? void 0 : _c.on("clients", this._handleClientsEvent.bind(this));
98
- (_d = this._client.socket) == null ? void 0 : _d.on("presence", this._handleClientsEvent.bind(this));
99
- (_e = this._client.socket) == null ? void 0 : _e.on("cursor", this._handleCursorEvent.bind(this));
235
+ socket == null ? void 0 : socket.on("clients", this._handleClientsEvent.bind(this));
236
+ socket == null ? void 0 : socket.on("bundle:done", this._handleBundleDoneEvent.bind(this));
237
+ socket == null ? void 0 : socket.on("bundle:error", this._handleBundleErrorEvent.bind(this));
100
238
  if (this._pendingOps.length) {
101
239
  console.log(
102
240
  `[CollabService] Flushing ${this._pendingOps.length} offline operation batch(es)`
103
241
  );
104
- this._pendingOps.forEach(({ tuples }) => {
105
- this.socket.emit("ops", { changes: tuples, ts: Date.now() });
106
- });
242
+ this._pendingOps.forEach(
243
+ ({ changes, granularChanges, orders, options: opOptions }) => {
244
+ const { message } = opOptions || {};
245
+ const ts = Date.now();
246
+ const payload = {
247
+ changes,
248
+ granularChanges,
249
+ orders,
250
+ ts
251
+ };
252
+ if (message) {
253
+ payload.message = message;
254
+ }
255
+ this.socket.emit("ops", payload);
256
+ }
257
+ );
107
258
  this._pendingOps.length = 0;
108
259
  }
109
- this._connected = true;
110
- console.log("[CollabService] Connected to project:", projectId);
111
- } catch (err) {
112
- console.error("[CollabService] Connection failed:", err);
113
- throw err;
260
+ await this._client.ready;
261
+ this._connectionMeta = {
262
+ projectId,
263
+ branch,
264
+ live: Boolean(pro)
265
+ };
266
+ return this.getConnectionInfo();
267
+ })();
268
+ try {
269
+ return await this._connectPromise;
270
+ } finally {
271
+ this._connecting = false;
272
+ this._connectPromise = null;
114
273
  }
115
274
  }
116
275
  disconnect() {
117
276
  var _a;
118
277
  if ((_a = this._client) == null ? void 0 : _a.socket) {
119
- this._client.socket.disconnect();
278
+ if (this._pendingConnectReject) {
279
+ this._pendingConnectReject(
280
+ new Error("[CollabService] Connection attempt aborted")
281
+ );
282
+ this._pendingConnectReject = null;
283
+ }
284
+ this._detachSocketLifecycleListeners();
285
+ if (typeof this._client.dispose === "function") {
286
+ this._client.dispose();
287
+ } else {
288
+ this._client.socket.disconnect();
289
+ }
120
290
  }
121
291
  this._client = null;
122
292
  this._connected = false;
293
+ this._connecting = false;
294
+ this._connectionMeta = null;
295
+ this._pendingConnectReject = null;
123
296
  console.log("[CollabService] Disconnected");
124
297
  }
125
298
  isConnected() {
126
299
  var _a, _b;
127
- return this._connected && ((_b = (_a = this._client) == null ? void 0 : _a.socket) == null ? void 0 : _b.connected);
300
+ return Boolean(this._connected && ((_b = (_a = this._client) == null ? void 0 : _a.socket) == null ? void 0 : _b.connected));
301
+ }
302
+ getConnectionInfo() {
303
+ var _a, _b, _c;
304
+ return {
305
+ connected: this.isConnected(),
306
+ connecting: this._connecting,
307
+ projectId: ((_a = this._connectionMeta) == null ? void 0 : _a.projectId) ?? null,
308
+ branch: ((_b = this._connectionMeta) == null ? void 0 : _b.branch) ?? null,
309
+ live: ((_c = this._connectionMeta) == null ? void 0 : _c.live) ?? null,
310
+ pendingOps: this._pendingOps.length,
311
+ undoStackSize: this.getUndoStackSize(),
312
+ redoStackSize: this.getRedoStackSize()
313
+ };
128
314
  }
129
315
  /* convenient shortcuts */
130
316
  get ydoc() {
@@ -149,20 +335,47 @@ class CollabService extends BaseService {
149
335
  }
150
336
  /* ---------- data helpers ---------- */
151
337
  updateData(tuples, options = {}) {
152
- var _a;
338
+ var _a, _b;
153
339
  this._ensureStateManager();
154
340
  const { isUndo = false, isRedo = false } = options;
155
341
  if (!isUndo && !isRedo && !this._isUndoRedo) {
156
342
  this._trackForUndo(tuples, options);
157
343
  }
344
+ const root = (_a = this._stateManager) == null ? void 0 : _a.root;
345
+ const { granularChanges: processedTuples, orders } = preprocessChanges(
346
+ root,
347
+ tuples,
348
+ options
349
+ );
350
+ if (options.append && options.append.length) {
351
+ processedTuples.push(...options.append);
352
+ }
158
353
  this._stateManager.applyChanges(tuples, { ...options });
354
+ const stringifiedGranularTuples = stringifyFunctionsForTransport(processedTuples);
355
+ const stringifiedTuples = stringifyFunctionsForTransport(tuples);
356
+ const { message } = options;
159
357
  if (!this.isConnected()) {
160
358
  console.warn("[CollabService] Not connected, queuing real-time update");
161
- this._pendingOps.push({ tuples, options });
359
+ this._pendingOps.push({
360
+ changes: stringifiedTuples,
361
+ granularChanges: stringifiedGranularTuples,
362
+ orders,
363
+ options
364
+ });
162
365
  return;
163
366
  }
164
- if ((_a = this.socket) == null ? void 0 : _a.connected) {
165
- this.socket.emit("ops", { changes: tuples, ts: Date.now() });
367
+ if ((_b = this.socket) == null ? void 0 : _b.connected) {
368
+ const ts = Date.now();
369
+ const payload = {
370
+ changes: stringifiedTuples,
371
+ granularChanges: stringifiedGranularTuples,
372
+ orders,
373
+ ts
374
+ };
375
+ if (message) {
376
+ payload.message = message;
377
+ }
378
+ this.socket.emit("ops", payload);
166
379
  }
167
380
  return { success: true };
168
381
  }
@@ -205,7 +418,14 @@ class CollabService extends BaseService {
205
418
  }
206
419
  }
207
420
  undo() {
421
+ var _a;
208
422
  if (!this._undoStack.length) {
423
+ const state = (_a = this._stateManager) == null ? void 0 : _a.root;
424
+ const el = state.__element;
425
+ el.call("openNotification", {
426
+ type: "error",
427
+ title: "Nothing to undo"
428
+ });
209
429
  throw new Error("Nothing to undo");
210
430
  }
211
431
  if (!this.isConnected()) {
@@ -233,7 +453,14 @@ class CollabService extends BaseService {
233
453
  return operations;
234
454
  }
235
455
  redo() {
456
+ var _a;
236
457
  if (!this._redoStack.length) {
458
+ const state = (_a = this._stateManager) == null ? void 0 : _a.root;
459
+ const el = state.__element;
460
+ el.call("openNotification", {
461
+ type: "error",
462
+ title: "Nothing to redo"
463
+ });
237
464
  throw new Error("Nothing to redo");
238
465
  }
239
466
  if (!this.isConnected()) {
@@ -278,47 +505,99 @@ class CollabService extends BaseService {
278
505
  this._redoStack.length = 0;
279
506
  }
280
507
  addItem(type, data, opts = {}) {
281
- const { value, ...schema } = data;
282
- const tuples = [
283
- ["update", [type, data.key], value],
284
- ["update", ["schema", type, data.key], schema],
285
- ...opts.additionalChanges || []
286
- ];
287
- return this.updateData(tuples, opts);
508
+ try {
509
+ validateParams.type(type);
510
+ validateParams.data(data, type);
511
+ const { value, ...schema } = data;
512
+ const tuples = [
513
+ ["update", [type, data.key], value],
514
+ ["update", ["schema", type, data.key], schema || {}]
515
+ ];
516
+ const updatedOpts = { ...opts, skipComponentsChangedEvent: true };
517
+ return this.updateData(tuples, updatedOpts);
518
+ } catch (error) {
519
+ throw new Error(`Failed to add item: ${error.message}`, { cause: error });
520
+ }
288
521
  }
289
522
  addMultipleItems(items, opts = {}) {
523
+ var _a;
290
524
  const tuples = [];
291
- items.forEach(([type, data]) => {
525
+ try {
526
+ items.forEach(([type, data]) => {
527
+ validateParams.type(type);
528
+ validateParams.data(data, type);
529
+ const { value, ...schema } = data;
530
+ tuples.push(
531
+ ["update", [type, data.key], value],
532
+ ["update", ["schema", type, data.key], schema]
533
+ );
534
+ });
535
+ this.updateData([...tuples, ...opts.append || []], {
536
+ message: `Created ${tuples.length} items`,
537
+ ...opts
538
+ });
539
+ return tuples;
540
+ } catch (error) {
541
+ const state = (_a = this._stateManager) == null ? void 0 : _a.root;
542
+ const el = state.__element;
543
+ el.call("openNotification", {
544
+ type: "error",
545
+ title: "Failed to add item",
546
+ message: error.message
547
+ });
548
+ throw new Error(`Failed to add item: ${error.message}`, { cause: error });
549
+ }
550
+ }
551
+ updateItem(type, data, opts = {}) {
552
+ var _a;
553
+ try {
554
+ validateParams.type(type);
555
+ validateParams.data(data, type);
292
556
  const { value, ...schema } = data;
293
- tuples.push(
557
+ const tuples = [
294
558
  ["update", [type, data.key], value],
295
559
  ["update", ["schema", type, data.key], schema]
296
- );
297
- });
298
- this.updateData([...tuples, ...opts.additionalChanges || []], {
299
- message: `Created ${tuples.length} items`,
300
- ...opts
301
- });
302
- return tuples;
303
- }
304
- updateItem(type, data, opts = {}) {
305
- const { value, ...schema } = data;
306
- const tuples = [
307
- ["update", [type, data.key], value],
308
- ["update", ["schema", type, data.key], schema]
309
- ];
310
- return this.updateData(tuples, opts);
560
+ ];
561
+ return this.updateData(tuples, opts);
562
+ } catch (error) {
563
+ const state = (_a = this._stateManager) == null ? void 0 : _a.root;
564
+ const el = state.__element;
565
+ el.call("openNotification", {
566
+ type: "error",
567
+ title: "Failed to update item",
568
+ message: error.message
569
+ });
570
+ throw new Error(`Failed to update item: ${error.message}`, {
571
+ cause: error
572
+ });
573
+ }
311
574
  }
312
575
  deleteItem(type, key, opts = {}) {
313
- const tuples = [
314
- ["delete", [type, key]],
315
- ["delete", ["schema", type, key]],
316
- ...opts.additionalChanges || []
317
- ];
318
- return this.updateData(tuples, {
319
- message: `Deleted ${key} from ${type}`,
320
- ...opts
321
- });
576
+ var _a;
577
+ try {
578
+ validateParams.type(type);
579
+ validateParams.key(key, type);
580
+ const tuples = [
581
+ ["delete", [type, key]],
582
+ ["delete", ["schema", type, key]],
583
+ ...opts.append || []
584
+ ];
585
+ return this.updateData(tuples, {
586
+ message: `Deleted ${key} from ${type}`,
587
+ ...opts
588
+ });
589
+ } catch (error) {
590
+ const state = (_a = this._stateManager) == null ? void 0 : _a.root;
591
+ const el = state.__element;
592
+ el.call("openNotification", {
593
+ type: "error",
594
+ title: "Failed to delete item",
595
+ message: error.message
596
+ });
597
+ throw new Error(`Failed to delete item: ${error.message}`, {
598
+ cause: error
599
+ });
600
+ }
322
601
  }
323
602
  /* ---------- socket event helpers ---------- */
324
603
  /**
@@ -331,48 +610,92 @@ class CollabService extends BaseService {
331
610
  var _a;
332
611
  const root = (_a = this._stateManager) == null ? void 0 : _a.root;
333
612
  if (root && typeof root.replace === "function") {
334
- root.replace(
335
- { clients: data },
336
- {
337
- fromSocket: true,
338
- preventUpdate: true
339
- }
340
- );
613
+ root.clients = data;
341
614
  }
615
+ rootBus.emit("clients:updated", data);
342
616
  }
343
- /**
344
- * Handle granular cursor updates coming from the socket.
345
- * Expected payload: { userId, positions?, chosenPos?, ... }
346
- * Only the provided fields are patched into the state tree.
347
- */
348
- _handleCursorEvent(payload = {}) {
349
- const { userId, positions, chosenPos, ...rest } = payload || {};
350
- if (!userId) {
351
- return;
617
+ /* ---------- Dependency bundling events ---------- */
618
+ _handleBundleDoneEvent({
619
+ project,
620
+ ticket,
621
+ dependencies = {},
622
+ schema = {}
623
+ } = {}) {
624
+ var _a;
625
+ console.info("[CollabService] Bundle done", { project, ticket });
626
+ try {
627
+ this._ensureStateManager();
628
+ const { dependencies: schemaDependencies = {} } = schema || {};
629
+ const tuples = [
630
+ ["update", ["dependencies"], dependencies],
631
+ ["update", ["schema", "dependencies"], schemaDependencies]
632
+ ];
633
+ this._stateManager.applyChanges(tuples, {
634
+ fromSocket: true,
635
+ preventFetchDeps: true,
636
+ preventUpdate: ["Iframe"]
637
+ });
638
+ } catch (err) {
639
+ console.error("[CollabService] Failed to update deps after bundle", err);
352
640
  }
353
- const tuples = [];
354
- if (positions) {
355
- tuples.push([
356
- "update",
357
- ["canvas", "clients", userId, "positions"],
358
- positions
359
- ]);
641
+ const root = (_a = this._stateManager) == null ? void 0 : _a.root;
642
+ const el = root == null ? void 0 : root.__element;
643
+ if (el == null ? void 0 : el.call) {
644
+ el.call("openNotification", {
645
+ type: "success",
646
+ title: "Dependencies ready",
647
+ message: `Project ${project} dependencies have been bundled successfully.`
648
+ });
649
+ }
650
+ rootBus.emit("bundle:done", { project, ticket });
651
+ }
652
+ _handleBundleErrorEvent({ project, ticket, error } = {}) {
653
+ var _a;
654
+ console.error("[CollabService] Bundle error", { project, ticket, error });
655
+ const root = (_a = this._stateManager) == null ? void 0 : _a.root;
656
+ const el = root == null ? void 0 : root.__element;
657
+ if (el == null ? void 0 : el.call) {
658
+ el.call("openNotification", {
659
+ type: "error",
660
+ title: "Dependency bundle failed",
661
+ message: error || `An error occurred while bundling dependencies for project ${project}.`
662
+ });
360
663
  }
361
- if (chosenPos) {
362
- tuples.push([
363
- "update",
364
- ["canvas", "clients", userId, "chosenPos"],
365
- chosenPos
366
- ]);
664
+ rootBus.emit("bundle:error", { project, ticket, error });
665
+ }
666
+ /* ---------- Manual checkpoint ---------- */
667
+ _attachSocketLifecycleListeners() {
668
+ var _a;
669
+ const socket = (_a = this._client) == null ? void 0 : _a.socket;
670
+ if (!socket) {
671
+ return;
367
672
  }
368
- if (Object.keys(rest).length) {
369
- tuples.push(["update", ["canvas", "clients", userId], rest]);
673
+ socket.on("connect", this._onSocketConnect);
674
+ socket.on("disconnect", this._onSocketDisconnect);
675
+ socket.on("connect_error", this._onSocketError);
676
+ }
677
+ _detachSocketLifecycleListeners() {
678
+ var _a;
679
+ const socket = (_a = this._client) == null ? void 0 : _a.socket;
680
+ if (!socket) {
681
+ return;
370
682
  }
371
- if (tuples.length) {
372
- this._stateManager.applyChanges(tuples, { fromSocket: true });
683
+ socket.off("connect", this._onSocketConnect);
684
+ socket.off("disconnect", this._onSocketDisconnect);
685
+ socket.off("connect_error", this._onSocketError);
686
+ }
687
+ _onSocketConnect() {
688
+ this._connected = true;
689
+ }
690
+ _onSocketDisconnect(reason) {
691
+ this._connected = false;
692
+ if (reason && reason !== "io client disconnect") {
693
+ console.warn("[CollabService] Socket disconnected", reason);
373
694
  }
374
695
  }
375
- /* ---------- Manual checkpoint ---------- */
696
+ _onSocketError(error) {
697
+ console.warn("[CollabService] Socket connection error", error);
698
+ }
376
699
  /**
377
700
  * Manually request a checkpoint / commit of buffered operations on the server.
378
701
  * Resolves with the new version number once the backend confirms via the