@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
@@ -24,18 +24,54 @@ var import_BaseService = require("./BaseService.js");
24
24
  var import_CollabClient = require("../utils/CollabClient.js");
25
25
  var import_RootStateManager = require("../state/RootStateManager.js");
26
26
  var import_rootEventBus = require("../state/rootEventBus.js");
27
+ var import_validation = require("../utils/validation.js");
28
+ var import_utils = require("@domql/utils");
29
+ var import_changePreprocessor = require("../utils/changePreprocessor.js");
30
+ const FUNCTION_META_KEYS = ["node", "__ref", "__element", "parent", "parse"];
31
+ function stringifyFunctionsForTransport(value, seen = /* @__PURE__ */ new WeakMap()) {
32
+ if (value === null || typeof value !== "object") {
33
+ return typeof value === "function" ? value.toString() : value;
34
+ }
35
+ if (seen.has(value)) {
36
+ return seen.get(value);
37
+ }
38
+ const clone = Array.isArray(value) ? [] : {};
39
+ seen.set(value, clone);
40
+ if (Array.isArray(value)) {
41
+ for (let i = 0; i < value.length; i++) {
42
+ clone[i] = stringifyFunctionsForTransport(value[i], seen);
43
+ }
44
+ return clone;
45
+ }
46
+ const keys = Object.keys(value);
47
+ for (let i = 0; i < keys.length; i++) {
48
+ const key = keys[i];
49
+ if (!FUNCTION_META_KEYS.includes(key)) {
50
+ clone[key] = stringifyFunctionsForTransport(value[key], seen);
51
+ }
52
+ }
53
+ return clone;
54
+ }
27
55
  class CollabService extends import_BaseService.BaseService {
28
56
  constructor(config) {
29
57
  super(config);
30
58
  this._client = null;
31
59
  this._stateManager = null;
32
60
  this._connected = false;
61
+ this._connecting = false;
62
+ this._connectPromise = null;
63
+ this._connectionMeta = null;
64
+ this._pendingConnectReject = null;
33
65
  this._undoStack = [];
34
66
  this._redoStack = [];
35
67
  this._isUndoRedo = false;
36
68
  this._pendingOps = [];
69
+ this._onSocketConnect = this._onSocketConnect.bind(this);
70
+ this._onSocketDisconnect = this._onSocketDisconnect.bind(this);
71
+ this._onSocketError = this._onSocketError.bind(this);
37
72
  }
38
73
  init({ context }) {
74
+ super.init({ context });
39
75
  if (context == null ? void 0 : context.state) {
40
76
  try {
41
77
  this._stateManager = new import_RootStateManager.RootStateManager(context.state);
@@ -63,90 +99,240 @@ class CollabService extends import_BaseService.BaseService {
63
99
  * caller can react accordingly.
64
100
  */
65
101
  _ensureStateManager() {
66
- var _a;
102
+ var _a, _b;
67
103
  if (!this._stateManager) {
68
104
  if (!((_a = this._context) == null ? void 0 : _a.state)) {
69
105
  throw new Error("[CollabService] Cannot operate without root state");
70
106
  }
71
107
  this._stateManager = new import_RootStateManager.RootStateManager(this._context.state);
72
108
  }
109
+ const root = (_b = this._stateManager) == null ? void 0 : _b.root;
110
+ if (root && !root.__element) {
111
+ root.__element = {
112
+ /**
113
+ * Very small subset of the DOMQL `call` API that we rely on inside the
114
+ * CollabService for browser notifications and data helpers.
115
+ * In a Node.js test context we simply log or return fallbacks.
116
+ */
117
+ call: (method, ...args) => {
118
+ switch (method) {
119
+ case "openNotification": {
120
+ const [payload = {}] = args;
121
+ const { type = "info", title = "", message = "" } = payload;
122
+ const logger = type === "error" ? console.error : console.log;
123
+ logger(`[Notification] ${title}${message ? ` \u2013 ${message}` : ""}`);
124
+ return;
125
+ }
126
+ case "deepStringifyFunctions": {
127
+ return (0, import_utils.deepStringifyFunctions)(...args);
128
+ }
129
+ default:
130
+ return {};
131
+ }
132
+ }
133
+ };
134
+ }
73
135
  }
74
136
  /* ---------- Connection Management ---------- */
75
137
  async connect(options = {}) {
76
- var _a, _b, _c, _d, _e;
77
- this._ensureStateManager();
78
- const {
79
- authToken: jwt,
80
- projectId,
81
- branch = "main",
82
- pro
83
- } = {
84
- ...this._context,
85
- ...options
86
- };
87
- console.log(jwt, projectId, branch, pro);
88
- if (!projectId) {
89
- throw new Error("projectId is required for CollabService connection");
90
- }
91
- if (this._client) {
92
- await this.disconnect();
138
+ if (this._connectPromise) {
139
+ return this._connectPromise;
93
140
  }
94
- try {
141
+ this._connectPromise = (async () => {
142
+ var _a;
143
+ this._connecting = true;
144
+ this._connected = false;
145
+ this._ensureStateManager();
146
+ const mergedOptions = {
147
+ ...this._context,
148
+ ...options
149
+ };
150
+ let { authToken: jwt } = mergedOptions;
151
+ const { projectId, branch = "main", pro } = mergedOptions;
152
+ if (!jwt && this._tokenManager) {
153
+ try {
154
+ jwt = await this._tokenManager.ensureValidToken();
155
+ } catch (error) {
156
+ console.warn(
157
+ "[CollabService] Failed to obtain auth token from token manager",
158
+ error
159
+ );
160
+ }
161
+ if (!jwt && typeof this._tokenManager.getAccessToken === "function") {
162
+ jwt = this._tokenManager.getAccessToken();
163
+ }
164
+ }
165
+ if (!jwt) {
166
+ throw new Error("[CollabService] Cannot connect without auth token");
167
+ }
168
+ this._context = {
169
+ ...this._context,
170
+ authToken: jwt,
171
+ projectId,
172
+ branch,
173
+ pro
174
+ };
175
+ if (!projectId) {
176
+ const state = (_a = this._stateManager) == null ? void 0 : _a.root;
177
+ const el = state.__element;
178
+ el.call("openNotification", {
179
+ type: "error",
180
+ title: "projectId is required",
181
+ message: "projectId is required for CollabService connection"
182
+ });
183
+ throw new Error("projectId is required for CollabService connection");
184
+ }
185
+ if (this._client) {
186
+ await this.disconnect();
187
+ }
95
188
  this._client = new import_CollabClient.CollabClient({
96
189
  jwt,
97
190
  projectId,
98
191
  branch,
99
192
  live: Boolean(pro)
100
193
  });
101
- await new Promise((resolve) => {
102
- var _a2, _b2;
103
- if ((_a2 = this._client.socket) == null ? void 0 : _a2.connected) {
104
- resolve();
105
- } else {
106
- (_b2 = this._client.socket) == null ? void 0 : _b2.once("connect", resolve);
107
- }
108
- });
109
- console.log("[CollabService] socket connected");
110
- (_a = this._client.socket) == null ? void 0 : _a.on("ops", ({ changes }) => {
194
+ const { socket } = this._client;
195
+ try {
196
+ await new Promise((resolve, reject) => {
197
+ if (!socket) {
198
+ reject(new Error("[CollabService] Socket instance missing"));
199
+ return;
200
+ }
201
+ if (socket.connected) {
202
+ resolve();
203
+ return;
204
+ }
205
+ const cleanup = () => {
206
+ socket.off("connect", handleConnect);
207
+ socket.off("connect_error", handleError);
208
+ socket.off("error", handleError);
209
+ socket.off("disconnect", handleDisconnect);
210
+ if (this._pendingConnectReject === handleError) {
211
+ this._pendingConnectReject = null;
212
+ }
213
+ };
214
+ const handleConnect = () => {
215
+ cleanup();
216
+ resolve();
217
+ };
218
+ const handleError = (error) => {
219
+ cleanup();
220
+ reject(
221
+ error instanceof Error ? error : new Error(String(error || "Unknown connection error"))
222
+ );
223
+ };
224
+ const handleDisconnect = (reason) => {
225
+ handleError(
226
+ reason instanceof Error ? reason : new Error(
227
+ `[CollabService] Socket disconnected before connect: ${reason || "unknown"}`
228
+ )
229
+ );
230
+ };
231
+ this._pendingConnectReject = handleError;
232
+ socket.once("connect", handleConnect);
233
+ socket.once("connect_error", handleError);
234
+ socket.once("error", handleError);
235
+ socket.once("disconnect", handleDisconnect);
236
+ });
237
+ } catch (error) {
238
+ socket == null ? void 0 : socket.disconnect();
239
+ this._client = null;
240
+ this._connectionMeta = null;
241
+ throw error;
242
+ }
243
+ this._attachSocketLifecycleListeners();
244
+ if (socket == null ? void 0 : socket.connected) {
245
+ this._onSocketConnect();
246
+ }
247
+ socket == null ? void 0 : socket.on("ops", ({ changes }) => {
111
248
  console.log(`ops event`);
112
- console.log(changes);
113
249
  this._stateManager.applyChanges(changes, { fromSocket: true });
114
250
  });
115
- (_b = this._client.socket) == null ? void 0 : _b.on("commit", ({ version }) => {
116
- this._stateManager.setVersion(version);
251
+ socket == null ? void 0 : socket.on("commit", ({ version }) => {
252
+ if (version) {
253
+ this._stateManager.setVersion(version);
254
+ }
117
255
  import_rootEventBus.rootBus.emit("checkpoint:done", { version, origin: "auto" });
118
256
  });
119
- (_c = this._client.socket) == null ? void 0 : _c.on("clients", this._handleClientsEvent.bind(this));
120
- (_d = this._client.socket) == null ? void 0 : _d.on("presence", this._handleClientsEvent.bind(this));
121
- (_e = this._client.socket) == null ? void 0 : _e.on("cursor", this._handleCursorEvent.bind(this));
257
+ socket == null ? void 0 : socket.on("clients", this._handleClientsEvent.bind(this));
258
+ socket == null ? void 0 : socket.on("bundle:done", this._handleBundleDoneEvent.bind(this));
259
+ socket == null ? void 0 : socket.on("bundle:error", this._handleBundleErrorEvent.bind(this));
122
260
  if (this._pendingOps.length) {
123
261
  console.log(
124
262
  `[CollabService] Flushing ${this._pendingOps.length} offline operation batch(es)`
125
263
  );
126
- this._pendingOps.forEach(({ tuples }) => {
127
- this.socket.emit("ops", { changes: tuples, ts: Date.now() });
128
- });
264
+ this._pendingOps.forEach(
265
+ ({ changes, granularChanges, orders, options: opOptions }) => {
266
+ const { message } = opOptions || {};
267
+ const ts = Date.now();
268
+ const payload = {
269
+ changes,
270
+ granularChanges,
271
+ orders,
272
+ ts
273
+ };
274
+ if (message) {
275
+ payload.message = message;
276
+ }
277
+ this.socket.emit("ops", payload);
278
+ }
279
+ );
129
280
  this._pendingOps.length = 0;
130
281
  }
131
- this._connected = true;
132
- console.log("[CollabService] Connected to project:", projectId);
133
- } catch (err) {
134
- console.error("[CollabService] Connection failed:", err);
135
- throw err;
282
+ await this._client.ready;
283
+ this._connectionMeta = {
284
+ projectId,
285
+ branch,
286
+ live: Boolean(pro)
287
+ };
288
+ return this.getConnectionInfo();
289
+ })();
290
+ try {
291
+ return await this._connectPromise;
292
+ } finally {
293
+ this._connecting = false;
294
+ this._connectPromise = null;
136
295
  }
137
296
  }
138
297
  disconnect() {
139
298
  var _a;
140
299
  if ((_a = this._client) == null ? void 0 : _a.socket) {
141
- this._client.socket.disconnect();
300
+ if (this._pendingConnectReject) {
301
+ this._pendingConnectReject(
302
+ new Error("[CollabService] Connection attempt aborted")
303
+ );
304
+ this._pendingConnectReject = null;
305
+ }
306
+ this._detachSocketLifecycleListeners();
307
+ if (typeof this._client.dispose === "function") {
308
+ this._client.dispose();
309
+ } else {
310
+ this._client.socket.disconnect();
311
+ }
142
312
  }
143
313
  this._client = null;
144
314
  this._connected = false;
315
+ this._connecting = false;
316
+ this._connectionMeta = null;
317
+ this._pendingConnectReject = null;
145
318
  console.log("[CollabService] Disconnected");
146
319
  }
147
320
  isConnected() {
148
321
  var _a, _b;
149
- return this._connected && ((_b = (_a = this._client) == null ? void 0 : _a.socket) == null ? void 0 : _b.connected);
322
+ return Boolean(this._connected && ((_b = (_a = this._client) == null ? void 0 : _a.socket) == null ? void 0 : _b.connected));
323
+ }
324
+ getConnectionInfo() {
325
+ var _a, _b, _c;
326
+ return {
327
+ connected: this.isConnected(),
328
+ connecting: this._connecting,
329
+ projectId: ((_a = this._connectionMeta) == null ? void 0 : _a.projectId) ?? null,
330
+ branch: ((_b = this._connectionMeta) == null ? void 0 : _b.branch) ?? null,
331
+ live: ((_c = this._connectionMeta) == null ? void 0 : _c.live) ?? null,
332
+ pendingOps: this._pendingOps.length,
333
+ undoStackSize: this.getUndoStackSize(),
334
+ redoStackSize: this.getRedoStackSize()
335
+ };
150
336
  }
151
337
  /* convenient shortcuts */
152
338
  get ydoc() {
@@ -171,20 +357,47 @@ class CollabService extends import_BaseService.BaseService {
171
357
  }
172
358
  /* ---------- data helpers ---------- */
173
359
  updateData(tuples, options = {}) {
174
- var _a;
360
+ var _a, _b;
175
361
  this._ensureStateManager();
176
362
  const { isUndo = false, isRedo = false } = options;
177
363
  if (!isUndo && !isRedo && !this._isUndoRedo) {
178
364
  this._trackForUndo(tuples, options);
179
365
  }
366
+ const root = (_a = this._stateManager) == null ? void 0 : _a.root;
367
+ const { granularChanges: processedTuples, orders } = (0, import_changePreprocessor.preprocessChanges)(
368
+ root,
369
+ tuples,
370
+ options
371
+ );
372
+ if (options.append && options.append.length) {
373
+ processedTuples.push(...options.append);
374
+ }
180
375
  this._stateManager.applyChanges(tuples, { ...options });
376
+ const stringifiedGranularTuples = stringifyFunctionsForTransport(processedTuples);
377
+ const stringifiedTuples = stringifyFunctionsForTransport(tuples);
378
+ const { message } = options;
181
379
  if (!this.isConnected()) {
182
380
  console.warn("[CollabService] Not connected, queuing real-time update");
183
- this._pendingOps.push({ tuples, options });
381
+ this._pendingOps.push({
382
+ changes: stringifiedTuples,
383
+ granularChanges: stringifiedGranularTuples,
384
+ orders,
385
+ options
386
+ });
184
387
  return;
185
388
  }
186
- if ((_a = this.socket) == null ? void 0 : _a.connected) {
187
- this.socket.emit("ops", { changes: tuples, ts: Date.now() });
389
+ if ((_b = this.socket) == null ? void 0 : _b.connected) {
390
+ const ts = Date.now();
391
+ const payload = {
392
+ changes: stringifiedTuples,
393
+ granularChanges: stringifiedGranularTuples,
394
+ orders,
395
+ ts
396
+ };
397
+ if (message) {
398
+ payload.message = message;
399
+ }
400
+ this.socket.emit("ops", payload);
188
401
  }
189
402
  return { success: true };
190
403
  }
@@ -227,7 +440,14 @@ class CollabService extends import_BaseService.BaseService {
227
440
  }
228
441
  }
229
442
  undo() {
443
+ var _a;
230
444
  if (!this._undoStack.length) {
445
+ const state = (_a = this._stateManager) == null ? void 0 : _a.root;
446
+ const el = state.__element;
447
+ el.call("openNotification", {
448
+ type: "error",
449
+ title: "Nothing to undo"
450
+ });
231
451
  throw new Error("Nothing to undo");
232
452
  }
233
453
  if (!this.isConnected()) {
@@ -255,7 +475,14 @@ class CollabService extends import_BaseService.BaseService {
255
475
  return operations;
256
476
  }
257
477
  redo() {
478
+ var _a;
258
479
  if (!this._redoStack.length) {
480
+ const state = (_a = this._stateManager) == null ? void 0 : _a.root;
481
+ const el = state.__element;
482
+ el.call("openNotification", {
483
+ type: "error",
484
+ title: "Nothing to redo"
485
+ });
259
486
  throw new Error("Nothing to redo");
260
487
  }
261
488
  if (!this.isConnected()) {
@@ -300,47 +527,99 @@ class CollabService extends import_BaseService.BaseService {
300
527
  this._redoStack.length = 0;
301
528
  }
302
529
  addItem(type, data, opts = {}) {
303
- const { value, ...schema } = data;
304
- const tuples = [
305
- ["update", [type, data.key], value],
306
- ["update", ["schema", type, data.key], schema],
307
- ...opts.additionalChanges || []
308
- ];
309
- return this.updateData(tuples, opts);
530
+ try {
531
+ import_validation.validateParams.type(type);
532
+ import_validation.validateParams.data(data, type);
533
+ const { value, ...schema } = data;
534
+ const tuples = [
535
+ ["update", [type, data.key], value],
536
+ ["update", ["schema", type, data.key], schema || {}]
537
+ ];
538
+ const updatedOpts = { ...opts, skipComponentsChangedEvent: true };
539
+ return this.updateData(tuples, updatedOpts);
540
+ } catch (error) {
541
+ throw new Error(`Failed to add item: ${error.message}`, { cause: error });
542
+ }
310
543
  }
311
544
  addMultipleItems(items, opts = {}) {
545
+ var _a;
312
546
  const tuples = [];
313
- items.forEach(([type, data]) => {
547
+ try {
548
+ items.forEach(([type, data]) => {
549
+ import_validation.validateParams.type(type);
550
+ import_validation.validateParams.data(data, type);
551
+ const { value, ...schema } = data;
552
+ tuples.push(
553
+ ["update", [type, data.key], value],
554
+ ["update", ["schema", type, data.key], schema]
555
+ );
556
+ });
557
+ this.updateData([...tuples, ...opts.append || []], {
558
+ message: `Created ${tuples.length} items`,
559
+ ...opts
560
+ });
561
+ return tuples;
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 add item",
568
+ message: error.message
569
+ });
570
+ throw new Error(`Failed to add item: ${error.message}`, { cause: error });
571
+ }
572
+ }
573
+ updateItem(type, data, opts = {}) {
574
+ var _a;
575
+ try {
576
+ import_validation.validateParams.type(type);
577
+ import_validation.validateParams.data(data, type);
314
578
  const { value, ...schema } = data;
315
- tuples.push(
579
+ const tuples = [
316
580
  ["update", [type, data.key], value],
317
581
  ["update", ["schema", type, data.key], schema]
318
- );
319
- });
320
- this.updateData([...tuples, ...opts.additionalChanges || []], {
321
- message: `Created ${tuples.length} items`,
322
- ...opts
323
- });
324
- return tuples;
325
- }
326
- updateItem(type, data, opts = {}) {
327
- const { value, ...schema } = data;
328
- const tuples = [
329
- ["update", [type, data.key], value],
330
- ["update", ["schema", type, data.key], schema]
331
- ];
332
- return this.updateData(tuples, opts);
582
+ ];
583
+ return this.updateData(tuples, opts);
584
+ } catch (error) {
585
+ const state = (_a = this._stateManager) == null ? void 0 : _a.root;
586
+ const el = state.__element;
587
+ el.call("openNotification", {
588
+ type: "error",
589
+ title: "Failed to update item",
590
+ message: error.message
591
+ });
592
+ throw new Error(`Failed to update item: ${error.message}`, {
593
+ cause: error
594
+ });
595
+ }
333
596
  }
334
597
  deleteItem(type, key, opts = {}) {
335
- const tuples = [
336
- ["delete", [type, key]],
337
- ["delete", ["schema", type, key]],
338
- ...opts.additionalChanges || []
339
- ];
340
- return this.updateData(tuples, {
341
- message: `Deleted ${key} from ${type}`,
342
- ...opts
343
- });
598
+ var _a;
599
+ try {
600
+ import_validation.validateParams.type(type);
601
+ import_validation.validateParams.key(key, type);
602
+ const tuples = [
603
+ ["delete", [type, key]],
604
+ ["delete", ["schema", type, key]],
605
+ ...opts.append || []
606
+ ];
607
+ return this.updateData(tuples, {
608
+ message: `Deleted ${key} from ${type}`,
609
+ ...opts
610
+ });
611
+ } catch (error) {
612
+ const state = (_a = this._stateManager) == null ? void 0 : _a.root;
613
+ const el = state.__element;
614
+ el.call("openNotification", {
615
+ type: "error",
616
+ title: "Failed to delete item",
617
+ message: error.message
618
+ });
619
+ throw new Error(`Failed to delete item: ${error.message}`, {
620
+ cause: error
621
+ });
622
+ }
344
623
  }
345
624
  /* ---------- socket event helpers ---------- */
346
625
  /**
@@ -353,48 +632,92 @@ class CollabService extends import_BaseService.BaseService {
353
632
  var _a;
354
633
  const root = (_a = this._stateManager) == null ? void 0 : _a.root;
355
634
  if (root && typeof root.replace === "function") {
356
- root.replace(
357
- { clients: data },
358
- {
359
- fromSocket: true,
360
- preventUpdate: true
361
- }
362
- );
635
+ root.clients = data;
363
636
  }
637
+ import_rootEventBus.rootBus.emit("clients:updated", data);
364
638
  }
365
- /**
366
- * Handle granular cursor updates coming from the socket.
367
- * Expected payload: { userId, positions?, chosenPos?, ... }
368
- * Only the provided fields are patched into the state tree.
369
- */
370
- _handleCursorEvent(payload = {}) {
371
- const { userId, positions, chosenPos, ...rest } = payload || {};
372
- if (!userId) {
373
- return;
639
+ /* ---------- Dependency bundling events ---------- */
640
+ _handleBundleDoneEvent({
641
+ project,
642
+ ticket,
643
+ dependencies = {},
644
+ schema = {}
645
+ } = {}) {
646
+ var _a;
647
+ console.info("[CollabService] Bundle done", { project, ticket });
648
+ try {
649
+ this._ensureStateManager();
650
+ const { dependencies: schemaDependencies = {} } = schema || {};
651
+ const tuples = [
652
+ ["update", ["dependencies"], dependencies],
653
+ ["update", ["schema", "dependencies"], schemaDependencies]
654
+ ];
655
+ this._stateManager.applyChanges(tuples, {
656
+ fromSocket: true,
657
+ preventFetchDeps: true,
658
+ preventUpdate: ["Iframe"]
659
+ });
660
+ } catch (err) {
661
+ console.error("[CollabService] Failed to update deps after bundle", err);
374
662
  }
375
- const tuples = [];
376
- if (positions) {
377
- tuples.push([
378
- "update",
379
- ["canvas", "clients", userId, "positions"],
380
- positions
381
- ]);
663
+ const root = (_a = this._stateManager) == null ? void 0 : _a.root;
664
+ const el = root == null ? void 0 : root.__element;
665
+ if (el == null ? void 0 : el.call) {
666
+ el.call("openNotification", {
667
+ type: "success",
668
+ title: "Dependencies ready",
669
+ message: `Project ${project} dependencies have been bundled successfully.`
670
+ });
671
+ }
672
+ import_rootEventBus.rootBus.emit("bundle:done", { project, ticket });
673
+ }
674
+ _handleBundleErrorEvent({ project, ticket, error } = {}) {
675
+ var _a;
676
+ console.error("[CollabService] Bundle error", { project, ticket, error });
677
+ const root = (_a = this._stateManager) == null ? void 0 : _a.root;
678
+ const el = root == null ? void 0 : root.__element;
679
+ if (el == null ? void 0 : el.call) {
680
+ el.call("openNotification", {
681
+ type: "error",
682
+ title: "Dependency bundle failed",
683
+ message: error || `An error occurred while bundling dependencies for project ${project}.`
684
+ });
382
685
  }
383
- if (chosenPos) {
384
- tuples.push([
385
- "update",
386
- ["canvas", "clients", userId, "chosenPos"],
387
- chosenPos
388
- ]);
686
+ import_rootEventBus.rootBus.emit("bundle:error", { project, ticket, error });
687
+ }
688
+ /* ---------- Manual checkpoint ---------- */
689
+ _attachSocketLifecycleListeners() {
690
+ var _a;
691
+ const socket = (_a = this._client) == null ? void 0 : _a.socket;
692
+ if (!socket) {
693
+ return;
389
694
  }
390
- if (Object.keys(rest).length) {
391
- tuples.push(["update", ["canvas", "clients", userId], rest]);
695
+ socket.on("connect", this._onSocketConnect);
696
+ socket.on("disconnect", this._onSocketDisconnect);
697
+ socket.on("connect_error", this._onSocketError);
698
+ }
699
+ _detachSocketLifecycleListeners() {
700
+ var _a;
701
+ const socket = (_a = this._client) == null ? void 0 : _a.socket;
702
+ if (!socket) {
703
+ return;
392
704
  }
393
- if (tuples.length) {
394
- this._stateManager.applyChanges(tuples, { fromSocket: true });
705
+ socket.off("connect", this._onSocketConnect);
706
+ socket.off("disconnect", this._onSocketDisconnect);
707
+ socket.off("connect_error", this._onSocketError);
708
+ }
709
+ _onSocketConnect() {
710
+ this._connected = true;
711
+ }
712
+ _onSocketDisconnect(reason) {
713
+ this._connected = false;
714
+ if (reason && reason !== "io client disconnect") {
715
+ console.warn("[CollabService] Socket disconnected", reason);
395
716
  }
396
717
  }
397
- /* ---------- Manual checkpoint ---------- */
718
+ _onSocketError(error) {
719
+ console.warn("[CollabService] Socket connection error", error);
720
+ }
398
721
  /**
399
722
  * Manually request a checkpoint / commit of buffered operations on the server.
400
723
  * Resolves with the new version number once the backend confirms via the