@knapsack/design-token-utils 5.0.0--canary.6593.4738696.0 → 5.0.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 (92) hide show
  1. package/.lintstagedrc.mjs +2 -2
  2. package/CHANGELOG.md +164 -0
  3. package/dist/collections/token-collection-utils.d.ts +13 -2
  4. package/dist/collections/token-collection-utils.d.ts.map +1 -1
  5. package/dist/collections/token-collection-utils.js +26 -2
  6. package/dist/collections/token-collection-utils.js.map +1 -1
  7. package/dist/converters/asset-converters/android/android-asset-converters.d.ts +1 -1
  8. package/dist/converters/asset-converters/android/android-asset-converters.d.ts.map +1 -1
  9. package/dist/converters/asset-converters/convert-token-data-to-files.js +12 -13
  10. package/dist/converters/asset-converters/convert-token-data-to-files.js.map +1 -1
  11. package/dist/converters/asset-converters/ios/ios-asset-converters.d.ts +1 -1
  12. package/dist/converters/asset-converters/ios/ios-asset-converters.d.ts.map +1 -1
  13. package/dist/converters/asset-converters/ios/ios-asset-converters.js +1 -2
  14. package/dist/converters/asset-converters/ios/ios-asset-converters.js.map +1 -1
  15. package/dist/converters/asset-converters/web/web-asset-converter-utils.d.ts +1 -1
  16. package/dist/converters/asset-converters/web/web-asset-converter-utils.d.ts.map +1 -1
  17. package/dist/converters/asset-converters/web/web-asset-converters.d.ts +1 -1
  18. package/dist/converters/asset-converters/web/web-asset-converters.d.ts.map +1 -1
  19. package/dist/converters/asset-converters/web/web-asset-converters.js.map +1 -1
  20. package/dist/converters/combined-converters.d.ts +1 -1
  21. package/dist/converters/combined-converters.d.ts.map +1 -1
  22. package/dist/converters/ks-figma-vars-to-spec/ks-figma-vars-to-spec.d.ts +4 -4
  23. package/dist/converters/ks-figma-vars-to-spec/ks-figma-vars-to-spec.d.ts.map +1 -1
  24. package/dist/converters/ks-figma-vars-to-spec/ks-figma-vars-to-spec.js +31 -2
  25. package/dist/converters/ks-figma-vars-to-spec/ks-figma-vars-to-spec.js.map +1 -1
  26. package/dist/converters/ks-figma-vars-to-spec/utils.d.ts +1 -1
  27. package/dist/converters/ks-figma-vars-to-spec/utils.d.ts.map +1 -1
  28. package/dist/converters/ks-figma-vars-to-spec/utils.js +1 -2
  29. package/dist/converters/ks-figma-vars-to-spec/utils.js.map +1 -1
  30. package/dist/converters/sd-src-to-ks-data/sd-src-to-ks-data.d.ts +1 -1
  31. package/dist/converters/sd-src-to-ks-data/sd-src-to-ks-data.d.ts.map +1 -1
  32. package/dist/converters/sd-src-to-ks-data/sd-src-to-ks-data.js +1 -2
  33. package/dist/converters/sd-src-to-ks-data/sd-src-to-ks-data.js.map +1 -1
  34. package/dist/converters/sd-src-to-spec-src/convert-sd-to-token-src.d.ts +1 -1
  35. package/dist/converters/sd-src-to-spec-src/convert-sd-to-token-src.d.ts.map +1 -1
  36. package/dist/converters/spec-src-to-sd-src/spec-src-to-sd-src.d.ts +1 -1
  37. package/dist/converters/spec-src-to-sd-src/spec-src-to-sd-src.d.ts.map +1 -1
  38. package/dist/converters/spec-src-to-studio-src/spec-src-to-studio-src.d.ts +2 -2
  39. package/dist/converters/spec-src-to-studio-src/spec-src-to-studio-src.d.ts.map +1 -1
  40. package/dist/converters/spec-src-to-studio-src/spec-src-to-studio-src.js.map +1 -1
  41. package/dist/converters/spec-src-to-token-data/convert-src-to-token-data.d.ts +1 -1
  42. package/dist/converters/spec-src-to-token-data/convert-src-to-token-data.d.ts.map +1 -1
  43. package/dist/converters/spec-src-to-token-data/convert-src-to-token-data.js +102 -55
  44. package/dist/converters/spec-src-to-token-data/convert-src-to-token-data.js.map +1 -1
  45. package/dist/converters/spec-src-to-token-data/utils.d.ts +1 -1
  46. package/dist/converters/spec-src-to-token-data/utils.d.ts.map +1 -1
  47. package/dist/converters/studio-src-to-sd-src/token-studio-to-sd-src.d.ts +3 -3
  48. package/dist/converters/studio-src-to-sd-src/token-studio-to-sd-src.d.ts.map +1 -1
  49. package/dist/converters/studio-src-to-spec-src/studio-src-to-spec-src.d.ts +3 -3
  50. package/dist/converters/studio-src-to-spec-src/studio-src-to-spec-src.d.ts.map +1 -1
  51. package/dist/converters/studio-src-to-spec-src/test-fixtures/set-override-basic.d.ts +1 -1
  52. package/dist/converters/studio-src-to-spec-src/test-fixtures/set-override-basic.d.ts.map +1 -1
  53. package/dist/converters/studio-src-to-spec-src/test-fixtures/sibling-refs.d.ts +1 -1
  54. package/dist/converters/studio-src-to-spec-src/test-fixtures/sibling-refs.d.ts.map +1 -1
  55. package/dist/formats/format-utils.d.ts +1 -1
  56. package/dist/formats/format-utils.d.ts.map +1 -1
  57. package/dist/formats/ks-figma-vars/ks-figma-vars.format.d.ts +1 -1
  58. package/dist/formats/ks-figma-vars/ks-figma-vars.format.d.ts.map +1 -1
  59. package/dist/formats/style-dictionary-format/style-dictionary-format.d.ts +2 -2
  60. package/dist/formats/style-dictionary-format/style-dictionary-format.d.ts.map +1 -1
  61. package/dist/formats/style-dictionary-format/style-dictionary-format.js.map +1 -1
  62. package/dist/formats/token-spec-format/token-spec-format.d.ts +2 -2
  63. package/dist/formats/token-spec-format/token-spec-format.d.ts.map +1 -1
  64. package/dist/formats/tokens-studio-format/tokens-studio-format.d.ts +2 -2
  65. package/dist/formats/tokens-studio-format/tokens-studio-format.d.ts.map +1 -1
  66. package/dist/formats/tokens-studio-format/tokens-studio-types.d.ts.map +1 -1
  67. package/dist/formats/tokens-studio-format/tokens-studio-types.js.map +1 -1
  68. package/dist/tests-fixtures/index.d.ts +1 -1
  69. package/dist/tests-fixtures/index.d.ts.map +1 -1
  70. package/dist/tests-fixtures/index.js.map +1 -1
  71. package/dist/tests-fixtures/token-fixtures.d.ts +1 -1
  72. package/dist/tests-fixtures/token-fixtures.d.ts.map +1 -1
  73. package/dist/token-src-updater/token-src-updater.d.ts +1 -1
  74. package/dist/token-src-updater/token-src-updater.d.ts.map +1 -1
  75. package/dist/token-src-updater/token-src-updater.js +561 -576
  76. package/dist/token-src-updater/token-src-updater.js.map +1 -1
  77. package/dist/token-src-value-info/token-src-value-info.d.ts +1 -1
  78. package/dist/token-src-value-info/token-src-value-info.d.ts.map +1 -1
  79. package/dist/token-types.d.ts +3 -3
  80. package/dist/token-types.d.ts.map +1 -1
  81. package/dist/types.d.ts +1 -1
  82. package/dist/types.d.ts.map +1 -1
  83. package/dist/types.js +1 -2
  84. package/dist/types.js.map +1 -1
  85. package/dist/utils.d.ts +3 -3
  86. package/dist/utils.d.ts.map +1 -1
  87. package/dist/utils.js +7 -5
  88. package/dist/utils.js.map +1 -1
  89. package/package.json +17 -20
  90. package/vitest.config.ts +7 -0
  91. package/.eslintrc.cjs +0 -8
  92. package/ava.config.cjs +0 -3
@@ -1,15 +1,3 @@
1
- var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
- if (kind === "m") throw new TypeError("Private method is not writable");
3
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
- return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
- };
7
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
- };
12
- var _TokenSrcUpdater_instances, _TokenSrcUpdater__src, _TokenSrcUpdater_prefix, _TokenSrcUpdater_dataIsStale, _TokenSrcUpdater__data, _TokenSrcUpdater_src_set, _TokenSrcUpdater_assertIdDoesNotHavePrefixes;
13
1
  import { produce } from 'immer';
14
2
  import loSetWith from 'lodash/setWith.js';
15
3
  import loGet from 'lodash/get.js';
@@ -108,539 +96,48 @@ function makeTokenSrc({ tokenId, tokenSrcValueInfo, description: $description, }
108
96
  *
109
97
  */
110
98
  export class TokenSrcUpdater {
99
+ // Actual src
100
+ #_src = {};
101
+ // Hide at runtime
102
+ // eslint-disable-next-line no-unused-private-class-members -- Reserved for future use
103
+ #prefix = undefined;
104
+ #dataIsStale = true;
105
+ #_data = undefined;
106
+ /**
107
+ * This is the group key that all collections are nested under.
108
+ * Convenient prop instead of `this.data.globalConfig?.collectionsParentKey`
109
+ */
110
+ collectionsParentKey;
111
+ /**
112
+ * Set src should also process/set data. Remember: external cannot set
113
+ */
114
+ set #src(newSrc) {
115
+ this.#_src = newSrc;
116
+ this.#dataIsStale = true;
117
+ this.collectionsParentKey =
118
+ newSrc?.$extensions?.['cloud.knapsack']?.global?.collectionsParentKey ||
119
+ '';
120
+ }
111
121
  /**
112
122
  * Getter for #_src. Remember: external CAN get (the whole point)
113
123
  */
114
124
  get src() {
115
- return __classPrivateFieldGet(this, _TokenSrcUpdater__src, "f");
125
+ return this.#_src;
116
126
  }
117
127
  /**
118
128
  * Calculate `TokenDataSummary` from current `src`
119
129
  */
120
130
  get data() {
121
- if (__classPrivateFieldGet(this, _TokenSrcUpdater_dataIsStale, "f") || !__classPrivateFieldGet(this, _TokenSrcUpdater__data, "f")) {
122
- __classPrivateFieldSet(this, _TokenSrcUpdater__data, convertTokenGroupToData(this.src), "f");
123
- __classPrivateFieldSet(this, _TokenSrcUpdater_dataIsStale, false, "f");
131
+ if (this.#dataIsStale || !this.#_data) {
132
+ this.#_data = convertTokenGroupToData(this.src);
133
+ this.#dataIsStale = false;
124
134
  }
125
- return __classPrivateFieldGet(this, _TokenSrcUpdater__data, "f");
135
+ return this.#_data;
126
136
  }
127
137
  constructor({ src, prefix }) {
128
- _TokenSrcUpdater_instances.add(this);
129
- // Actual src
130
- _TokenSrcUpdater__src.set(this, {});
131
- // Hide at runtime
132
- _TokenSrcUpdater_prefix.set(this, undefined);
133
- _TokenSrcUpdater_dataIsStale.set(this, true);
134
- _TokenSrcUpdater__data.set(this, undefined);
135
- /**
136
- * Ensures that `id` does not start with the Collection Parent Key nor
137
- * a Collection ID (if present)
138
- */
139
- _TokenSrcUpdater_assertIdDoesNotHavePrefixes.set(this, ({ id, collectionId, }) => {
140
- const prefix = collectionId
141
- ? `${this.collectionsParentKey}.${collectionId}`
142
- : this.collectionsParentKey;
143
- if (id === null || id === void 0 ? void 0 : id.startsWith(prefix)) {
144
- throw new Error(`ID "${id}" should not start with "${prefix}"`);
145
- }
146
- });
147
- /**
148
- * Collection operations
149
- */
150
- this.collection = {
151
- /**
152
- * Collection -> Create
153
- */
154
- create: ({ collectionId, excludeInitialMode, }) => {
155
- if (this.data.collectionsById[collectionId]) {
156
- throw new Error(`Collection ${collectionId} already exists.`);
157
- }
158
- this.createGroup({
159
- groupId: `${this.collectionsParentKey}.${collectionId}`,
160
- groupData: {
161
- config: {
162
- collectionInfo: {
163
- type: 'collection',
164
- modes: excludeInitialMode ? [] : [{ id: 'default' }],
165
- },
166
- },
167
- },
168
- });
169
- return { collectionId };
170
- },
171
- /**
172
- * Collection -> Rename
173
- */
174
- rename: ({ collectionId, newName, }) => {
175
- this.renameGroup({
176
- originalGroupId: `${this.collectionsParentKey}.${collectionId}`,
177
- destinationGroupId: `${this.collectionsParentKey}.${newName}`,
178
- });
179
- },
180
- /**
181
- * Collection -> Delete
182
- */
183
- delete: ({ collectionId }) => {
184
- if (collectionId.startsWith(`${this.collectionsParentKey}.`)) {
185
- throw new Error(`Collection IDs should not start with ${this.collectionsParentKey} - those are Group IDs.`);
186
- }
187
- this.deleteGroup({
188
- groupId: `${this.collectionsParentKey}.${collectionId}`,
189
- });
190
- },
191
- };
192
- /**
193
- * Collection -> Mode operations
194
- */
195
- this.mode = {
196
- /**
197
- * Collection -> Mode -> Create
198
- */
199
- create: ({ collectionId, modeId, }) => {
200
- var _a;
201
- const { groupId, modes } = this.data.collectionsById[collectionId];
202
- const colGroup = this.data.groupsById[groupId];
203
- this.updateGroup({
204
- groupId,
205
- groupData: {
206
- config: {
207
- ...colGroup.groupConfig,
208
- collectionInfo: {
209
- ...(((_a = colGroup.groupConfig) === null || _a === void 0 ? void 0 : _a.collectionInfo) || {}),
210
- type: 'collection',
211
- modes: [
212
- ...modes,
213
- {
214
- id: modeId,
215
- },
216
- ],
217
- },
218
- },
219
- },
220
- });
221
- },
222
- /**
223
- * Collection -> Mode -> Rename
224
- * @TODO: ensure new Mode ID does not already exist
225
- */
226
- rename: ({ collectionId, modeId, newModeId, }) => {
227
- // Why use produce here instead of just calling rename() on a loop?
228
- // If we can run multiple operations in a single Immer produce(), it prevents
229
- // thrashing the this.src object and thus unnecessary updates.
230
- __classPrivateFieldSet(this, _TokenSrcUpdater_instances, produce(this.src, (draft) => {
231
- var _a;
232
- // Why not just use the processed this.data which is easier to look up on?
233
- // The answer is that the returned object we get here will need to be
234
- // modified within this Immer produce() anyway, so we might as well just
235
- // use the this.src directly.
236
- const originalColData = loGet(draft, [
237
- this.collectionsParentKey,
238
- collectionId,
239
- '$extensions',
240
- 'cloud.knapsack',
241
- ]);
242
- const colInfo = (_a = originalColData === null || originalColData === void 0 ? void 0 : originalColData.group) === null || _a === void 0 ? void 0 : _a.collectionInfo;
243
- if (!colInfo || colInfo.type !== 'collection')
244
- throw new Error('Not a Collection');
245
- const { modes = [] } = colInfo;
246
- const originalMode = modes === null || modes === void 0 ? void 0 : modes.find((m) => m.id === modeId);
247
- if (!originalMode)
248
- throw new Error('Mode not found');
249
- // Find every single token with the mode name and change its modes. Use
250
- // this.data for much easier looping here.
251
- this.data.collectionsById[collectionId].vars.forEach((varObj) => {
252
- if (!varObj.modesById[modeId])
253
- return;
254
- // Get original mode data
255
- const original = loGet(draft, `${varObj.groupId}.${originalMode.id}`);
256
- // If values for mode in var not found, don't do anything
257
- if (!original) {
258
- return;
259
- }
260
- // Set new Mode key and value
261
- loSetWith(draft, ensureTokenPath(`${varObj.groupId}.${newModeId}`), original, Object);
262
- // Now nuke the old Mode within the Var group
263
- loUnset(draft, `${varObj.groupId}.${originalMode.id}`); // remove old mode
264
- });
265
- // Finally update the name of mode within the collection group modes array
266
- originalMode.id = newModeId;
267
- }), "a", _TokenSrcUpdater_src_set);
268
- },
269
- /**
270
- * Collection -> Mode -> Delete
271
- */
272
- delete: ({ collectionId, modeId, }) => {
273
- __classPrivateFieldSet(this, _TokenSrcUpdater_instances, produce(this.src, (draft) => {
274
- var _a;
275
- const originalColData = loGet(draft, [
276
- this.collectionsParentKey,
277
- collectionId,
278
- '$extensions',
279
- 'cloud.knapsack',
280
- ]);
281
- const colInfo = (_a = originalColData === null || originalColData === void 0 ? void 0 : originalColData.group) === null || _a === void 0 ? void 0 : _a.collectionInfo;
282
- if (!colInfo || colInfo.type !== 'collection')
283
- throw new Error('Not a Collection');
284
- const { modes = [] } = colInfo;
285
- const originalMode = modes === null || modes === void 0 ? void 0 : modes.find((m) => m.id === modeId);
286
- if (!originalMode)
287
- throw new Error('Mode not found');
288
- // Find every single token with the mode name and change its modes. Use
289
- // this.data for much easier looping here.
290
- this.data.collectionsById[collectionId].vars.forEach((varObj) => {
291
- if (!varObj.modesById[modeId])
292
- return;
293
- // Nuke the old Mode within the Var group
294
- loUnset(draft, `${varObj.groupId}.${originalMode.id}`); // remove old mode
295
- });
296
- // Finally update the name of mode within the collection group modes array
297
- loRemove(modes, (mode) => mode.id === modeId);
298
- }), "a", _TokenSrcUpdater_src_set);
299
- },
300
- reorder: ({ collectionId, modeId, index, }) => {
301
- __classPrivateFieldSet(this, _TokenSrcUpdater_instances, produce(this.src, (draft) => {
302
- var _a;
303
- const originalColData = loGet(draft, [
304
- this.collectionsParentKey,
305
- collectionId,
306
- '$extensions',
307
- 'cloud.knapsack',
308
- ]);
309
- const colInfo = (_a = originalColData === null || originalColData === void 0 ? void 0 : originalColData.group) === null || _a === void 0 ? void 0 : _a.collectionInfo;
310
- if (!colInfo || colInfo.type !== 'collection')
311
- throw new Error('Not a Collection');
312
- const { modes = [] } = colInfo;
313
- const originalMode = modes === null || modes === void 0 ? void 0 : modes.find((m) => m.id === modeId);
314
- if (!originalMode)
315
- throw new Error('Mode not found');
316
- const fromIndex = modes.findIndex((m) => m.id === modeId);
317
- const [theModeToMove] = modes.splice(fromIndex, 1);
318
- modes.splice(index, 0, theModeToMove);
319
- }), "a", _TokenSrcUpdater_src_set);
320
- },
321
- };
322
- /**
323
- * Collection -> Collection Group operations
324
- */
325
- this.collectionGroup = {
326
- /**
327
- * Collection -> Collection Group -> Create
328
- */
329
- create: ({ collectionId, groupId, parentGroupId, }) => {
330
- if (parentGroupId) {
331
- __classPrivateFieldGet(this, _TokenSrcUpdater_assertIdDoesNotHavePrefixes, "f").call(this, { id: parentGroupId, collectionId });
332
- }
333
- if (this.data.collectionsById[collectionId].groupIds.indexOf(groupId) !== -1) {
334
- throw new Error(`Group ${groupId} already exists in ${collectionId}.`);
335
- }
336
- this.createGroup({
337
- groupId,
338
- });
339
- return {
340
- groupId,
341
- };
342
- },
343
- /**
344
- * Collection -> Collection Group -> Rename
345
- */
346
- rename: ({ collectionId, collectionGroupId, name, }) => {
347
- assertDotlessId(name);
348
- __classPrivateFieldGet(this, _TokenSrcUpdater_assertIdDoesNotHavePrefixes, "f").call(this, {
349
- id: collectionGroupId,
350
- collectionId,
351
- });
352
- const originalGroupId = `${this.collectionsParentKey}.${collectionId}.${collectionGroupId}`;
353
- const destinationGroupId = originalGroupId
354
- .split('.')
355
- // replace the last segment of the ID with the new name
356
- .map((chunk, i, arr) => (i === arr.length - 1 ? name : chunk))
357
- .join('.');
358
- this.renameGroup({
359
- originalGroupId,
360
- destinationGroupId,
361
- });
362
- },
363
- /**
364
- * Collection -> Collection Group -> Delete
365
- */
366
- delete: ({ collectionId, collectionGroupId, }) => {
367
- __classPrivateFieldGet(this, _TokenSrcUpdater_assertIdDoesNotHavePrefixes, "f").call(this, {
368
- id: collectionGroupId,
369
- collectionId,
370
- });
371
- this.deleteGroup({
372
- groupId: `${this.collectionsParentKey}.${collectionId}.${collectionGroupId}`,
373
- });
374
- },
375
- /**
376
- * Collection -> Collection Group -> Move
377
- */
378
- move: ({ collectionId, collectionGroupId, newParentCollectionId, newParentCollectionGroupId, }) => {
379
- __classPrivateFieldGet(this, _TokenSrcUpdater_assertIdDoesNotHavePrefixes, "f").call(this, {
380
- id: collectionGroupId,
381
- collectionId,
382
- });
383
- __classPrivateFieldGet(this, _TokenSrcUpdater_assertIdDoesNotHavePrefixes, "f").call(this, {
384
- id: newParentCollectionGroupId,
385
- collectionId,
386
- });
387
- const originalGroupId = `${this.collectionsParentKey}.${collectionId}.${collectionGroupId}`;
388
- const finalSegment = originalGroupId.split('.').at(-1);
389
- if (!finalSegment)
390
- throw new Error('Cannot extract final segment.');
391
- const destinationCollectionId = newParentCollectionId !== null && newParentCollectionId !== void 0 ? newParentCollectionId : collectionId;
392
- // Empty string newParentCollectionGroupId means move to root of collection
393
- const destinationCollectionGroupId = newParentCollectionGroupId === ''
394
- ? ''
395
- : `.${newParentCollectionGroupId}`;
396
- const destinationGroupId = `${this.collectionsParentKey}.${destinationCollectionId}${destinationCollectionGroupId}.${finalSegment}`;
397
- this.renameGroup({
398
- originalGroupId,
399
- destinationGroupId,
400
- });
401
- },
402
- /**
403
- * CONVERT vanilla group to collection group
404
- */
405
- convert: ({ currentGroupId, collectionId, newParentCollectionGroupId, }) => {
406
- // Get the vanilla group data
407
- const vanillaGroup = this.data.groupsById[currentGroupId];
408
- // Create var with default value for each mode
409
- try {
410
- // Quick validation that mode we'll be adding later exists
411
- const modeId = this.data.collectionsById[collectionId].modes[0].id;
412
- if (!modeId)
413
- throw new Error('Collection must have at least one mode');
414
- // Make first collection group
415
- this.collectionGroup.create({
416
- collectionId,
417
- groupId: `collections.${collectionId}.${newParentCollectionGroupId}.${vanillaGroup.id}`,
418
- });
419
- // Recursively dive through token group. NOTE: Only a function definition,
420
- // actually called below.
421
- const dive = (group) => {
422
- // Now loop all children, creating var for each
423
- group.children.tokenIds.forEach((tokenId) => {
424
- this.var.create({
425
- collectionId,
426
- groupId: newParentCollectionGroupId,
427
- varId: tokenId,
428
- varType: this.data.tokensById[tokenId].type,
429
- });
430
- const tokenData = this.data.tokensById[tokenId];
431
- const tokenSrcValueInfo = getTokenSrcInfo(tokenData);
432
- // Set the default value for the var for *first* mode
433
- this.var.setModeValue({
434
- collectionId,
435
- varId: `${newParentCollectionGroupId}.${tokenId}`,
436
- modeId,
437
- tokenSrcValueInfo,
438
- });
439
- });
440
- // Yay recursion!
441
- group.children.groupIds.forEach((subGroupId) => {
442
- this.collectionGroup.create({
443
- collectionId,
444
- groupId: `collections.${collectionId}.${newParentCollectionGroupId}.${subGroupId}`,
445
- });
446
- dive(this.data.groupsById[subGroupId]);
447
- });
448
- };
449
- // Kick off the call to the above
450
- dive(vanillaGroup);
451
- // Nuke the original vanilla group
452
- this.deleteGroup({
453
- groupId: currentGroupId,
454
- });
455
- }
456
- catch (e) {
457
- if (e instanceof Error) {
458
- // @TODO: roll back any changes from the try above
459
- throw new Error(`Error converting vanilla group to collection group: ${e.message}`);
460
- }
461
- }
462
- },
463
- };
464
- /**
465
- * Var operations
466
- */
467
- this.var = {
468
- /**
469
- * Var -> Create
470
- */
471
- create: ({ collectionId, groupId, varId, varType, }) => {
472
- __classPrivateFieldGet(this, _TokenSrcUpdater_assertIdDoesNotHavePrefixes, "f").call(this, {
473
- id: groupId,
474
- collectionId,
475
- });
476
- const newVarId = groupId ? `${groupId}.${varId}` : varId;
477
- const fullGroupId = `${this.collectionsParentKey}.${collectionId}.${newVarId}`;
478
- // This is pretty fast
479
- const existingVar = loGet(this.src, fullGroupId);
480
- if (existingVar) {
481
- console.log(`Var ${newVarId} already exists in ${collectionId}. Skipping.`);
482
- return;
483
- }
484
- this.createGroup({
485
- groupId: fullGroupId,
486
- groupData: {
487
- type: varType,
488
- config: {
489
- collectionInfo: {
490
- type: 'var',
491
- },
492
- },
493
- },
494
- });
495
- },
496
- /**
497
- * Var -> Rename
498
- */
499
- rename: ({ collectionId, varId, newVarId, }) => {
500
- this.renameGroup({
501
- originalGroupId: `${this.collectionsParentKey}.${collectionId}.${varId}`,
502
- destinationGroupId: `${this.collectionsParentKey}.${collectionId}.${newVarId}`,
503
- });
504
- },
505
- /**
506
- * Var -> Delete
507
- */
508
- delete: ({ collectionId, varId, }) => {
509
- this.deleteGroup({
510
- groupId: `${this.collectionsParentKey}.${collectionId}.${varId}`,
511
- });
512
- },
513
- /**
514
- * Var -> Move/Rename (moving groups is the same as renaming)
515
- * @TODO: needs work around existing Var groups
516
- */
517
- move: ({ collectionId, varId, newParentGroupId, }) => {
518
- const varGroupPath = `${this.collectionsParentKey}.${collectionId}.${varId}`;
519
- const lastPart = varGroupPath.split('.').at(-1);
520
- const destinationGroupId = `${this.collectionsParentKey}.${collectionId}.${newParentGroupId}.${lastPart}`;
521
- this.renameGroup({
522
- originalGroupId: varGroupPath,
523
- destinationGroupId,
524
- });
525
- },
526
- /**
527
- * Var -> setDescription
528
- */
529
- setDescription: ({ collectionId, varId, description, }) => {
530
- const groupId = `${this.collectionsParentKey}.${collectionId}.${varId}`;
531
- this.updateGroup({
532
- groupId,
533
- groupData: {
534
- description,
535
- },
536
- });
537
- },
538
- /**
539
- * Var -> setModeValue
540
- */
541
- /**
542
- * Set one or many var mode values
543
- */
544
- setModeValue: (modeVals) => {
545
- // Do everything inside a single Immer produce for batch efficiency
546
- __classPrivateFieldSet(this, _TokenSrcUpdater_instances, produce(this.src, (draft) => {
547
- // Force the incoming args to be an array so we can handle one or many
548
- castArray(modeVals).forEach((val) => {
549
- var _a, _b;
550
- const { collectionId, varId, modeId, tokenSrcValueInfo, skipRefExistsCheck, } = val;
551
- const { vars } = this.data.collectionsById[collectionId];
552
- const varValue = vars.find((v) => v.id === varId);
553
- if (!varValue) {
554
- throw new Error(`Var ${varId} does not exist in collection ${collectionId}`);
555
- }
556
- const tokenId = `${this.collectionsParentKey}.${collectionId}.${varId}.${modeId}`;
557
- // Handle the different kinds of tokenSrcValueInfo: var refs and static
558
- switch (tokenSrcValueInfo.kind) {
559
- case 'ref': {
560
- const { isVarRef, referencedTokenId } = tokenSrcValueInfo;
561
- // Note that there are TWO kinds of "refs": referencing another var
562
- // (and all its modes therein) and just referencing a vanilla token.
563
- if (isVarRef) {
564
- if (!skipRefExistsCheck) {
565
- const ref = this.data.groupsById[referencedTokenId];
566
- if (!ref) {
567
- throw new Error(`No ref exists for "${referencedTokenId}"`);
568
- }
569
- if (((_b = (_a = ref.groupConfig) === null || _a === void 0 ? void 0 : _a.collectionInfo) === null || _b === void 0 ? void 0 : _b.type) !== 'var') {
570
- throw new Error(`Referenced token "${referencedTokenId}" is not a var`);
571
- }
572
- }
573
- // This is the fancypants ref to a whole collections var
574
- const tokenSrc = {
575
- $extensions: {
576
- 'cloud.knapsack': {
577
- group: {
578
- collectionInfo: {
579
- type: 'var-ref',
580
- referencedId: referencedTokenId,
581
- },
582
- },
583
- },
584
- },
585
- };
586
- const newTokenSrc = loMerge(loGet(draft, tokenId, {}), tokenSrc);
587
- // @ts-expect-error - force removing key that should not exist
588
- delete newTokenSrc.$value;
589
- loSetWith(draft, ensureTokenPath(tokenId), newTokenSrc, Object);
590
- break;
591
- }
592
- // Cannot call another produce function within a produce
593
- // this.updateToken({
594
- // tokenId,
595
- // tokenSrcValueInfo,
596
- // });
597
- // @TODO: find a way to call this.updateToken() instead of repeating below
598
- // This is the vanilla token ref
599
- const tokenSrc = makeTokenSrc({
600
- tokenId,
601
- tokenSrcValueInfo,
602
- });
603
- const newTokenSrc = loMerge(loGet(draft, tokenId, {}), tokenSrc);
604
- loSetWith(draft, ensureTokenPath(tokenId), newTokenSrc, Object);
605
- break;
606
- }
607
- case 'static': {
608
- // Cannot call another function that uses produce
609
- // this.updateToken({
610
- // tokenId,
611
- // tokenSrcValueInfo,
612
- // });
613
- // @TODO: find a way to call this.updateToken() instead of repeating below
614
- const tokenSrc = makeTokenSrc({
615
- tokenId,
616
- tokenSrcValueInfo,
617
- });
618
- const newTokenSrc = loMerge(loGet(draft, tokenId, {}), tokenSrc);
619
- loSetWith(draft, ensureTokenPath(tokenId), newTokenSrc, Object);
620
- break;
621
- }
622
- default: {
623
- const _exhaustiveCheck = tokenSrcValueInfo;
624
- throw new Error(`Unhandled kind: ${JSON.stringify(tokenSrcValueInfo)}`);
625
- }
626
- }
627
- // Token Mode Values should not have $type since the $type of the Mode is
628
- // set within the Var object above. This just guarantees $type is not set.
629
- loUnset(draft, `${tokenId}.$type`);
630
- });
631
- }), "a", _TokenSrcUpdater_src_set);
632
- },
633
- /**
634
- * Var -> unsetModeValue
635
- */
636
- unsetModeValue: ({ collectionId, varId, modeId, }) => {
637
- const tokenId = `${this.collectionsParentKey}.${collectionId}.${varId}.${modeId}`;
638
- this.deleteToken({ tokenId });
639
- },
640
- };
641
138
  this.collectionsParentKey = '';
642
- __classPrivateFieldSet(this, _TokenSrcUpdater_instances, src, "a", _TokenSrcUpdater_src_set);
643
- __classPrivateFieldSet(this, _TokenSrcUpdater_prefix, prefix, "f");
139
+ this.#src = src;
140
+ this.#prefix = prefix;
644
141
  }
645
142
  /**
646
143
  * Single Token operations
@@ -655,9 +152,9 @@ export class TokenSrcUpdater {
655
152
  description,
656
153
  });
657
154
  // Reminder: mutate draft within produce(). Do NOT return.
658
- __classPrivateFieldSet(this, _TokenSrcUpdater_instances, produce(this.src, (draft) => {
155
+ this.#src = produce(this.src, (draft) => {
659
156
  loSetWith(draft, ensureTokenPath(tokenId), tokenSrc, Object);
660
- }), "a", _TokenSrcUpdater_src_set);
157
+ });
661
158
  }
662
159
  /**
663
160
  * Simple token data updates (value, description, etc)
@@ -669,10 +166,10 @@ export class TokenSrcUpdater {
669
166
  description,
670
167
  });
671
168
  // Reminder: mutate draft within produce(). Do NOT return.
672
- __classPrivateFieldSet(this, _TokenSrcUpdater_instances, produce(this.src, (draft) => {
169
+ this.#src = produce(this.src, (draft) => {
673
170
  const newTokenSrc = loMerge(loGet(draft, tokenId, {}), tokenSrc);
674
171
  loSetWith(draft, ensureTokenPath(tokenId), newTokenSrc, Object);
675
- }), "a", _TokenSrcUpdater_src_set);
172
+ });
676
173
  }
677
174
  /**
678
175
  * Remove token at dot.path. If other things ref it, update those to original
@@ -709,7 +206,6 @@ export class TokenSrcUpdater {
709
206
  // ];
710
207
  // @TODO: probably need to sort this by precedence of reference
711
208
  const tokensWithRefToDeletedTokens = this.data.tokens.filter((token) => {
712
- var _a;
713
209
  switch (token.type) {
714
210
  case 'shadow': {
715
211
  if (token.kind === 'static') {
@@ -763,7 +259,7 @@ export class TokenSrcUpdater {
763
259
  case 'fontWeight':
764
260
  case 'cubicBezier':
765
261
  case 'string': {
766
- return (_a = token === null || token === void 0 ? void 0 : token.references) === null || _a === void 0 ? void 0 : _a.some((refStack) => {
262
+ return token?.references?.some((refStack) => {
767
263
  // First ref in stack is **direct** reference to deleted token
768
264
  const mostImmediateRef = refStack.at(0);
769
265
  // Make TS happy that it's not undefined
@@ -795,7 +291,7 @@ export class TokenSrcUpdater {
795
291
  return x;
796
292
  }
797
293
  // Reminder: mutate draft within produce(). Do NOT return.
798
- __classPrivateFieldSet(this, _TokenSrcUpdater_instances, produce(this.src, (draft) => {
294
+ this.#src = produce(this.src, (draft) => {
799
295
  // Update these tokens to use original token value.
800
296
  tokensWithRefToDeletedTokens.forEach((tokenWithRef) => {
801
297
  switch (tokenWithRef.type) {
@@ -846,7 +342,7 @@ export class TokenSrcUpdater {
846
342
  deletedTokens.forEach((deletedToken) => {
847
343
  loUnset(draft, deletedToken.id);
848
344
  });
849
- }), "a", _TokenSrcUpdater_src_set);
345
+ });
850
346
  // Return deleted tokens for any callee that wants verification
851
347
  return deletedTokens;
852
348
  }
@@ -877,15 +373,14 @@ export class TokenSrcUpdater {
877
373
  : { $type: undefined }),
878
374
  });
879
375
  // Reminder: mutate draft within produce(). Do NOT return.
880
- __classPrivateFieldSet(this, _TokenSrcUpdater_instances, produce(this.src, (draft) => {
376
+ this.#src = produce(this.src, (draft) => {
881
377
  loSetWith(draft, ensureTokenPath(destinationTokenId), tokenProcessed, Object);
882
- }), "a", _TokenSrcUpdater_src_set);
378
+ });
883
379
  const tokensWithRefToRenamedToken = this.data.tokens.filter((token) => {
884
- var _a;
885
- return (_a = token === null || token === void 0 ? void 0 : token.references) === null || _a === void 0 ? void 0 : _a.some((refStack) => refStack.at(0) === originalTokenId);
380
+ return token?.references?.some((refStack) => refStack.at(0) === originalTokenId);
886
381
  });
887
382
  // Reminder: mutate draft within produce(). Do NOT return.
888
- __classPrivateFieldSet(this, _TokenSrcUpdater_instances, produce(this.src, (draft) => {
383
+ this.#src = produce(this.src, (draft) => {
889
384
  tokensWithRefToRenamedToken.forEach((token) => {
890
385
  // @TODO: handle token values other than just strings; `color`, for
891
386
  // instance, is a string.
@@ -895,7 +390,7 @@ export class TokenSrcUpdater {
895
390
  loSetWith(draft, ensureTokenPath(`${token.id}.$value`), newVal, Object);
896
391
  });
897
392
  loUnset(draft, originalTokenId);
898
- }), "a", _TokenSrcUpdater_src_set);
393
+ });
899
394
  // Return original and new token name for any callee that wants verification
900
395
  return {
901
396
  originalTokenId,
@@ -907,19 +402,19 @@ export class TokenSrcUpdater {
907
402
  */
908
403
  createGroup({ groupId, groupData, }) {
909
404
  const srcGroup = {
910
- ...((groupData === null || groupData === void 0 ? void 0 : groupData.description)
405
+ ...(groupData?.description
911
406
  ? { $description: groupData.description }
912
407
  : {}),
913
- $type: (groupData === null || groupData === void 0 ? void 0 : groupData.type) || undefined,
408
+ $type: groupData?.type || undefined,
914
409
  };
915
- if (groupData === null || groupData === void 0 ? void 0 : groupData.config) {
410
+ if (groupData?.config) {
916
411
  srcGroup.$extensions = {
917
412
  'cloud.knapsack': {
918
413
  group: groupData.config,
919
414
  },
920
415
  };
921
416
  }
922
- __classPrivateFieldSet(this, _TokenSrcUpdater_instances, produce(this.src, (draft) => {
417
+ this.#src = produce(this.src, (draft) => {
923
418
  // What existed prior, spread to ensure COPY of object
924
419
  const originalSrcGroup = { ...loGet(draft, groupId, {}) };
925
420
  // Merge new into what existed already, but remove empty key/values
@@ -933,7 +428,7 @@ export class TokenSrcUpdater {
933
428
  // brackets to delimit object nesting. Thus splitting the groupId by dots
934
429
  // into an array means we can support square brackets in path names.
935
430
  ensureTokenPath(groupId), mergedSrcGroup, Object);
936
- }), "a", _TokenSrcUpdater_src_set);
431
+ });
937
432
  }
938
433
  /**
939
434
  * Update token fields, like description and type
@@ -944,21 +439,21 @@ export class TokenSrcUpdater {
944
439
  ...(groupData.description ? { $description: groupData.description } : {}),
945
440
  $type: groupData.type,
946
441
  };
947
- if (groupData === null || groupData === void 0 ? void 0 : groupData.config) {
442
+ if (groupData?.config) {
948
443
  newSrcGroup.$extensions = {
949
444
  'cloud.knapsack': {
950
445
  group: groupData.config,
951
446
  },
952
447
  };
953
448
  }
954
- __classPrivateFieldSet(this, _TokenSrcUpdater_instances, produce(this.src, (draft) => {
449
+ this.#src = produce(this.src, (draft) => {
955
450
  // What existed prior, spread to ensure COPY of object
956
451
  const originalSrcGroup = { ...loGet(draft, groupId, {}) };
957
452
  // Merge new into what existed already, but remove empty key/values
958
453
  const mergedSrcGroup = removeUndefinedProps(loMerge(originalSrcGroup, newSrcGroup));
959
454
  // Set it
960
455
  loSetWith(draft, ensureTokenPath(groupId), mergedSrcGroup, Object);
961
- }), "a", _TokenSrcUpdater_src_set);
456
+ });
962
457
  }
963
458
  /**
964
459
  * Removing a group nukes all tokens within
@@ -972,15 +467,14 @@ export class TokenSrcUpdater {
972
467
  this.deleteToken({
973
468
  tokenId: this.data.tokens.flatMap(({ id }) => id.startsWith(`${groupId}.`) ? id : []),
974
469
  });
975
- __classPrivateFieldSet(this, _TokenSrcUpdater_instances, produce(this.src, (draft) => {
470
+ this.#src = produce(this.src, (draft) => {
976
471
  loUnset(draft, groupId);
977
- }), "a", _TokenSrcUpdater_src_set);
472
+ });
978
473
  }
979
474
  /**
980
475
  * Renaming a group utilizes prior methods
981
476
  */
982
477
  renameGroup({ originalGroupId, destinationGroupId, }) {
983
- var _a;
984
478
  const existingGroup = loGet(this.src, originalGroupId);
985
479
  const destinationGroup = loGet(this.src, destinationGroupId);
986
480
  if (destinationGroup) {
@@ -1016,18 +510,17 @@ export class TokenSrcUpdater {
1016
510
  }
1017
511
  : {}),
1018
512
  ...('$type' in existingGroup ? { type: existingGroup.$type } : {}),
1019
- config: (_a = this.data.groupsById[originalGroupId]) === null || _a === void 0 ? void 0 : _a.groupConfig,
513
+ config: this.data.groupsById[originalGroupId]?.groupConfig,
1020
514
  },
1021
515
  });
1022
516
  // Dig through all token values and replace references from the old group ID
1023
517
  // to the new
1024
518
  this.data.groups.forEach((group) => {
1025
- var _a, _b, _c, _d;
1026
519
  // We're only looking for var-refs
1027
- if (((_b = (_a = group === null || group === void 0 ? void 0 : group.groupConfig) === null || _a === void 0 ? void 0 : _a.collectionInfo) === null || _b === void 0 ? void 0 : _b.type) !== 'var-ref')
520
+ if (group?.groupConfig?.collectionInfo?.type !== 'var-ref')
1028
521
  return;
1029
522
  // And the referenced ID does NOT match, skip
1030
- if (((_d = (_c = group === null || group === void 0 ? void 0 : group.groupConfig) === null || _c === void 0 ? void 0 : _c.collectionInfo) === null || _d === void 0 ? void 0 : _d.referencedId) !== originalGroupId)
523
+ if (group?.groupConfig?.collectionInfo?.referencedId !== originalGroupId)
1031
524
  return;
1032
525
  // We are a var-ref and it's referencing the group ID that just changed
1033
526
  // i.e. 'collections.brand.color.primary.modeA'
@@ -1051,24 +544,23 @@ export class TokenSrcUpdater {
1051
544
  // Finally we delete the original group. Note: we're using produce() here
1052
545
  // directly instead of calling this.deleteGroup() due to how Immer holds onto
1053
546
  // the proxy. This apparently keeps it in the same tick.
1054
- __classPrivateFieldSet(this, _TokenSrcUpdater_instances, produce(this.src, (draft) => {
547
+ this.#src = produce(this.src, (draft) => {
1055
548
  loUnset(draft, originalGroupId);
1056
- }), "a", _TokenSrcUpdater_src_set);
549
+ });
1057
550
  }
1058
551
  setCollectionsParentKey({ collectionsParentKey, }) {
1059
- __classPrivateFieldSet(this, _TokenSrcUpdater_instances, produce(this.src, (draft) => {
1060
- var _a, _b, _c;
552
+ this.#src = produce(this.src, (draft) => {
1061
553
  draft.$extensions = {
1062
554
  ...(draft.$extensions || {}),
1063
555
  'cloud.knapsack': {
1064
- ...(((_a = draft.$extensions) === null || _a === void 0 ? void 0 : _a['cloud.knapsack']) || {}),
556
+ ...(draft.$extensions?.['cloud.knapsack'] || {}),
1065
557
  global: {
1066
- ...(((_c = (_b = draft.$extensions) === null || _b === void 0 ? void 0 : _b['cloud.knapsack']) === null || _c === void 0 ? void 0 : _c.global) || {}),
558
+ ...(draft.$extensions?.['cloud.knapsack']?.global || {}),
1067
559
  collectionsParentKey,
1068
560
  },
1069
561
  },
1070
562
  };
1071
- }), "a", _TokenSrcUpdater_src_set);
563
+ });
1072
564
  // Update the instance property to match the new value
1073
565
  this.collectionsParentKey = collectionsParentKey;
1074
566
  if (collectionsParentKey) {
@@ -1077,13 +569,506 @@ export class TokenSrcUpdater {
1077
569
  });
1078
570
  }
1079
571
  }
572
+ /**
573
+ * Ensures that `id` does not start with the Collection Parent Key nor
574
+ * a Collection ID (if present)
575
+ */
576
+ #assertIdDoesNotHavePrefixes = ({ id, collectionId, }) => {
577
+ const prefix = collectionId
578
+ ? `${this.collectionsParentKey}.${collectionId}`
579
+ : this.collectionsParentKey;
580
+ if (id?.startsWith(prefix)) {
581
+ throw new Error(`ID "${id}" should not start with "${prefix}"`);
582
+ }
583
+ };
584
+ /**
585
+ * Collection operations
586
+ */
587
+ collection = {
588
+ /**
589
+ * Collection -> Create
590
+ */
591
+ create: ({ collectionId, excludeInitialMode, }) => {
592
+ if (this.data.collectionsById[collectionId]) {
593
+ throw new Error(`Collection ${collectionId} already exists.`);
594
+ }
595
+ this.createGroup({
596
+ groupId: `${this.collectionsParentKey}.${collectionId}`,
597
+ groupData: {
598
+ config: {
599
+ collectionInfo: {
600
+ type: 'collection',
601
+ modes: excludeInitialMode ? [] : [{ id: 'default' }],
602
+ },
603
+ },
604
+ },
605
+ });
606
+ return { collectionId };
607
+ },
608
+ /**
609
+ * Collection -> Rename
610
+ */
611
+ rename: ({ collectionId, newName, }) => {
612
+ this.renameGroup({
613
+ originalGroupId: `${this.collectionsParentKey}.${collectionId}`,
614
+ destinationGroupId: `${this.collectionsParentKey}.${newName}`,
615
+ });
616
+ },
617
+ /**
618
+ * Collection -> Delete
619
+ */
620
+ delete: ({ collectionId }) => {
621
+ if (collectionId.startsWith(`${this.collectionsParentKey}.`)) {
622
+ throw new Error(`Collection IDs should not start with ${this.collectionsParentKey} - those are Group IDs.`);
623
+ }
624
+ this.deleteGroup({
625
+ groupId: `${this.collectionsParentKey}.${collectionId}`,
626
+ });
627
+ },
628
+ };
629
+ /**
630
+ * Collection -> Mode operations
631
+ */
632
+ mode = {
633
+ /**
634
+ * Collection -> Mode -> Create
635
+ */
636
+ create: ({ collectionId, modeId, }) => {
637
+ const { groupId, modes } = this.data.collectionsById[collectionId];
638
+ const colGroup = this.data.groupsById[groupId];
639
+ this.updateGroup({
640
+ groupId,
641
+ groupData: {
642
+ config: {
643
+ ...colGroup.groupConfig,
644
+ collectionInfo: {
645
+ ...(colGroup.groupConfig?.collectionInfo || {}),
646
+ type: 'collection',
647
+ modes: [
648
+ ...modes,
649
+ {
650
+ id: modeId,
651
+ },
652
+ ],
653
+ },
654
+ },
655
+ },
656
+ });
657
+ },
658
+ /**
659
+ * Collection -> Mode -> Rename
660
+ * @TODO: ensure new Mode ID does not already exist
661
+ */
662
+ rename: ({ collectionId, modeId, newModeId, }) => {
663
+ // Why use produce here instead of just calling rename() on a loop?
664
+ // If we can run multiple operations in a single Immer produce(), it prevents
665
+ // thrashing the this.src object and thus unnecessary updates.
666
+ this.#src = produce(this.src, (draft) => {
667
+ // Why not just use the processed this.data which is easier to look up on?
668
+ // The answer is that the returned object we get here will need to be
669
+ // modified within this Immer produce() anyway, so we might as well just
670
+ // use the this.src directly.
671
+ const originalColData = loGet(draft, [
672
+ this.collectionsParentKey,
673
+ collectionId,
674
+ '$extensions',
675
+ 'cloud.knapsack',
676
+ ]);
677
+ const colInfo = originalColData?.group?.collectionInfo;
678
+ if (!colInfo || colInfo.type !== 'collection')
679
+ throw new Error('Not a Collection');
680
+ const { modes = [] } = colInfo;
681
+ const originalMode = modes?.find((m) => m.id === modeId);
682
+ if (!originalMode)
683
+ throw new Error('Mode not found');
684
+ // Find every single token with the mode name and change its modes. Use
685
+ // this.data for much easier looping here.
686
+ this.data.collectionsById[collectionId].vars.forEach((varObj) => {
687
+ if (!varObj.modesById[modeId])
688
+ return;
689
+ // Get original mode data
690
+ const original = loGet(draft, `${varObj.groupId}.${originalMode.id}`);
691
+ // If values for mode in var not found, don't do anything
692
+ if (!original) {
693
+ return;
694
+ }
695
+ // Set new Mode key and value
696
+ loSetWith(draft, ensureTokenPath(`${varObj.groupId}.${newModeId}`), original, Object);
697
+ // Now nuke the old Mode within the Var group
698
+ loUnset(draft, `${varObj.groupId}.${originalMode.id}`); // remove old mode
699
+ });
700
+ // Finally update the name of mode within the collection group modes array
701
+ originalMode.id = newModeId;
702
+ });
703
+ },
704
+ /**
705
+ * Collection -> Mode -> Delete
706
+ */
707
+ delete: ({ collectionId, modeId, }) => {
708
+ this.#src = produce(this.src, (draft) => {
709
+ const originalColData = loGet(draft, [
710
+ this.collectionsParentKey,
711
+ collectionId,
712
+ '$extensions',
713
+ 'cloud.knapsack',
714
+ ]);
715
+ const colInfo = originalColData?.group?.collectionInfo;
716
+ if (!colInfo || colInfo.type !== 'collection')
717
+ throw new Error('Not a Collection');
718
+ const { modes = [] } = colInfo;
719
+ const originalMode = modes?.find((m) => m.id === modeId);
720
+ if (!originalMode)
721
+ throw new Error('Mode not found');
722
+ // Find every single token with the mode name and change its modes. Use
723
+ // this.data for much easier looping here.
724
+ this.data.collectionsById[collectionId].vars.forEach((varObj) => {
725
+ if (!varObj.modesById[modeId])
726
+ return;
727
+ // Nuke the old Mode within the Var group
728
+ loUnset(draft, `${varObj.groupId}.${originalMode.id}`); // remove old mode
729
+ });
730
+ // Finally update the name of mode within the collection group modes array
731
+ loRemove(modes, (mode) => mode.id === modeId);
732
+ });
733
+ },
734
+ reorder: ({ collectionId, modeId, index, }) => {
735
+ this.#src = produce(this.src, (draft) => {
736
+ const originalColData = loGet(draft, [
737
+ this.collectionsParentKey,
738
+ collectionId,
739
+ '$extensions',
740
+ 'cloud.knapsack',
741
+ ]);
742
+ const colInfo = originalColData?.group?.collectionInfo;
743
+ if (!colInfo || colInfo.type !== 'collection')
744
+ throw new Error('Not a Collection');
745
+ const { modes = [] } = colInfo;
746
+ const originalMode = modes?.find((m) => m.id === modeId);
747
+ if (!originalMode)
748
+ throw new Error('Mode not found');
749
+ const fromIndex = modes.findIndex((m) => m.id === modeId);
750
+ const [theModeToMove] = modes.splice(fromIndex, 1);
751
+ modes.splice(index, 0, theModeToMove);
752
+ });
753
+ },
754
+ };
755
+ /**
756
+ * Collection -> Collection Group operations
757
+ */
758
+ collectionGroup = {
759
+ /**
760
+ * Collection -> Collection Group -> Create
761
+ */
762
+ create: ({ collectionId, groupId, parentGroupId, }) => {
763
+ if (parentGroupId) {
764
+ this.#assertIdDoesNotHavePrefixes({ id: parentGroupId, collectionId });
765
+ }
766
+ if (this.data.collectionsById[collectionId].groupIds.indexOf(groupId) !== -1) {
767
+ throw new Error(`Group ${groupId} already exists in ${collectionId}.`);
768
+ }
769
+ this.createGroup({
770
+ groupId,
771
+ });
772
+ return {
773
+ groupId,
774
+ };
775
+ },
776
+ /**
777
+ * Collection -> Collection Group -> Rename
778
+ */
779
+ rename: ({ collectionId, collectionGroupId, name, }) => {
780
+ assertDotlessId(name);
781
+ this.#assertIdDoesNotHavePrefixes({
782
+ id: collectionGroupId,
783
+ collectionId,
784
+ });
785
+ const originalGroupId = `${this.collectionsParentKey}.${collectionId}.${collectionGroupId}`;
786
+ const destinationGroupId = originalGroupId
787
+ .split('.')
788
+ // replace the last segment of the ID with the new name
789
+ .map((chunk, i, arr) => (i === arr.length - 1 ? name : chunk))
790
+ .join('.');
791
+ this.renameGroup({
792
+ originalGroupId,
793
+ destinationGroupId,
794
+ });
795
+ },
796
+ /**
797
+ * Collection -> Collection Group -> Delete
798
+ */
799
+ delete: ({ collectionId, collectionGroupId, }) => {
800
+ this.#assertIdDoesNotHavePrefixes({
801
+ id: collectionGroupId,
802
+ collectionId,
803
+ });
804
+ this.deleteGroup({
805
+ groupId: `${this.collectionsParentKey}.${collectionId}.${collectionGroupId}`,
806
+ });
807
+ },
808
+ /**
809
+ * Collection -> Collection Group -> Move
810
+ */
811
+ move: ({ collectionId, collectionGroupId, newParentCollectionId, newParentCollectionGroupId, }) => {
812
+ this.#assertIdDoesNotHavePrefixes({
813
+ id: collectionGroupId,
814
+ collectionId,
815
+ });
816
+ this.#assertIdDoesNotHavePrefixes({
817
+ id: newParentCollectionGroupId,
818
+ collectionId,
819
+ });
820
+ const originalGroupId = `${this.collectionsParentKey}.${collectionId}.${collectionGroupId}`;
821
+ const finalSegment = originalGroupId.split('.').at(-1);
822
+ if (!finalSegment)
823
+ throw new Error('Cannot extract final segment.');
824
+ const destinationCollectionId = newParentCollectionId ?? collectionId;
825
+ // Empty string newParentCollectionGroupId means move to root of collection
826
+ const destinationCollectionGroupId = newParentCollectionGroupId === ''
827
+ ? ''
828
+ : `.${newParentCollectionGroupId}`;
829
+ const destinationGroupId = `${this.collectionsParentKey}.${destinationCollectionId}${destinationCollectionGroupId}.${finalSegment}`;
830
+ this.renameGroup({
831
+ originalGroupId,
832
+ destinationGroupId,
833
+ });
834
+ },
835
+ /**
836
+ * CONVERT vanilla group to collection group
837
+ */
838
+ convert: ({ currentGroupId, collectionId, newParentCollectionGroupId, }) => {
839
+ // Get the vanilla group data
840
+ const vanillaGroup = this.data.groupsById[currentGroupId];
841
+ // Create var with default value for each mode
842
+ try {
843
+ // Quick validation that mode we'll be adding later exists
844
+ const modeId = this.data.collectionsById[collectionId].modes[0].id;
845
+ if (!modeId)
846
+ throw new Error('Collection must have at least one mode');
847
+ // Make first collection group
848
+ this.collectionGroup.create({
849
+ collectionId,
850
+ groupId: `collections.${collectionId}.${newParentCollectionGroupId}.${vanillaGroup.id}`,
851
+ });
852
+ // Recursively dive through token group. NOTE: Only a function definition,
853
+ // actually called below.
854
+ const dive = (group) => {
855
+ // Now loop all children, creating var for each
856
+ group.children.tokenIds.forEach((tokenId) => {
857
+ this.var.create({
858
+ collectionId,
859
+ groupId: newParentCollectionGroupId,
860
+ varId: tokenId,
861
+ varType: this.data.tokensById[tokenId].type,
862
+ });
863
+ const tokenData = this.data.tokensById[tokenId];
864
+ const tokenSrcValueInfo = getTokenSrcInfo(tokenData);
865
+ // Set the default value for the var for *first* mode
866
+ this.var.setModeValue({
867
+ collectionId,
868
+ varId: `${newParentCollectionGroupId}.${tokenId}`,
869
+ modeId,
870
+ tokenSrcValueInfo,
871
+ });
872
+ });
873
+ // Yay recursion!
874
+ group.children.groupIds.forEach((subGroupId) => {
875
+ this.collectionGroup.create({
876
+ collectionId,
877
+ groupId: `collections.${collectionId}.${newParentCollectionGroupId}.${subGroupId}`,
878
+ });
879
+ dive(this.data.groupsById[subGroupId]);
880
+ });
881
+ };
882
+ // Kick off the call to the above
883
+ dive(vanillaGroup);
884
+ // Nuke the original vanilla group
885
+ this.deleteGroup({
886
+ groupId: currentGroupId,
887
+ });
888
+ }
889
+ catch (e) {
890
+ if (e instanceof Error) {
891
+ // @TODO: roll back any changes from the try above
892
+ throw new Error(`Error converting vanilla group to collection group: ${e.message}`);
893
+ }
894
+ }
895
+ },
896
+ };
897
+ /**
898
+ * Var operations
899
+ */
900
+ var = {
901
+ /**
902
+ * Var -> Create
903
+ */
904
+ create: ({ collectionId, groupId, varId, varType, }) => {
905
+ this.#assertIdDoesNotHavePrefixes({
906
+ id: groupId,
907
+ collectionId,
908
+ });
909
+ const newVarId = groupId ? `${groupId}.${varId}` : varId;
910
+ const fullGroupId = `${this.collectionsParentKey}.${collectionId}.${newVarId}`;
911
+ // This is pretty fast
912
+ const existingVar = loGet(this.src, fullGroupId);
913
+ if (existingVar) {
914
+ console.log(`Var ${newVarId} already exists in ${collectionId}. Skipping.`);
915
+ return;
916
+ }
917
+ this.createGroup({
918
+ groupId: fullGroupId,
919
+ groupData: {
920
+ type: varType,
921
+ config: {
922
+ collectionInfo: {
923
+ type: 'var',
924
+ },
925
+ },
926
+ },
927
+ });
928
+ },
929
+ /**
930
+ * Var -> Rename
931
+ */
932
+ rename: ({ collectionId, varId, newVarId, }) => {
933
+ this.renameGroup({
934
+ originalGroupId: `${this.collectionsParentKey}.${collectionId}.${varId}`,
935
+ destinationGroupId: `${this.collectionsParentKey}.${collectionId}.${newVarId}`,
936
+ });
937
+ },
938
+ /**
939
+ * Var -> Delete
940
+ */
941
+ delete: ({ collectionId, varId, }) => {
942
+ this.deleteGroup({
943
+ groupId: `${this.collectionsParentKey}.${collectionId}.${varId}`,
944
+ });
945
+ },
946
+ /**
947
+ * Var -> Move/Rename (moving groups is the same as renaming)
948
+ * @TODO: needs work around existing Var groups
949
+ */
950
+ move: ({ collectionId, varId, newParentGroupId, }) => {
951
+ const varGroupPath = `${this.collectionsParentKey}.${collectionId}.${varId}`;
952
+ const lastPart = varGroupPath.split('.').at(-1);
953
+ const destinationGroupId = `${this.collectionsParentKey}.${collectionId}.${newParentGroupId}.${lastPart}`;
954
+ this.renameGroup({
955
+ originalGroupId: varGroupPath,
956
+ destinationGroupId,
957
+ });
958
+ },
959
+ /**
960
+ * Var -> setDescription
961
+ */
962
+ setDescription: ({ collectionId, varId, description, }) => {
963
+ const groupId = `${this.collectionsParentKey}.${collectionId}.${varId}`;
964
+ this.updateGroup({
965
+ groupId,
966
+ groupData: {
967
+ description,
968
+ },
969
+ });
970
+ },
971
+ /**
972
+ * Var -> setModeValue
973
+ */
974
+ /**
975
+ * Set one or many var mode values
976
+ */
977
+ setModeValue: (modeVals) => {
978
+ // Do everything inside a single Immer produce for batch efficiency
979
+ this.#src = produce(this.src, (draft) => {
980
+ // Force the incoming args to be an array so we can handle one or many
981
+ castArray(modeVals).forEach((val) => {
982
+ const { collectionId, varId, modeId, tokenSrcValueInfo, skipRefExistsCheck, } = val;
983
+ const { vars } = this.data.collectionsById[collectionId];
984
+ const varValue = vars.find((v) => v.id === varId);
985
+ if (!varValue) {
986
+ throw new Error(`Var ${varId} does not exist in collection ${collectionId}`);
987
+ }
988
+ const tokenId = `${this.collectionsParentKey}.${collectionId}.${varId}.${modeId}`;
989
+ // Handle the different kinds of tokenSrcValueInfo: var refs and static
990
+ switch (tokenSrcValueInfo.kind) {
991
+ case 'ref': {
992
+ const { isVarRef, referencedTokenId } = tokenSrcValueInfo;
993
+ // Note that there are TWO kinds of "refs": referencing another var
994
+ // (and all its modes therein) and just referencing a vanilla token.
995
+ if (isVarRef) {
996
+ if (!skipRefExistsCheck) {
997
+ const ref = this.data.groupsById[referencedTokenId];
998
+ if (!ref) {
999
+ throw new Error(`No ref exists for "${referencedTokenId}"`);
1000
+ }
1001
+ if (ref.groupConfig?.collectionInfo?.type !== 'var') {
1002
+ throw new Error(`Referenced token "${referencedTokenId}" is not a var`);
1003
+ }
1004
+ }
1005
+ // This is the fancypants ref to a whole collections var
1006
+ const tokenSrc = {
1007
+ $extensions: {
1008
+ 'cloud.knapsack': {
1009
+ group: {
1010
+ collectionInfo: {
1011
+ type: 'var-ref',
1012
+ referencedId: referencedTokenId,
1013
+ },
1014
+ },
1015
+ },
1016
+ },
1017
+ };
1018
+ const newTokenSrc = loMerge(loGet(draft, tokenId, {}), tokenSrc);
1019
+ // @ts-expect-error - force removing key that should not exist
1020
+ delete newTokenSrc.$value;
1021
+ loSetWith(draft, ensureTokenPath(tokenId), newTokenSrc, Object);
1022
+ break;
1023
+ }
1024
+ // Cannot call another produce function within a produce
1025
+ // this.updateToken({
1026
+ // tokenId,
1027
+ // tokenSrcValueInfo,
1028
+ // });
1029
+ // @TODO: find a way to call this.updateToken() instead of repeating below
1030
+ // This is the vanilla token ref
1031
+ const tokenSrc = makeTokenSrc({
1032
+ tokenId,
1033
+ tokenSrcValueInfo,
1034
+ });
1035
+ const newTokenSrc = loMerge(loGet(draft, tokenId, {}), tokenSrc);
1036
+ loSetWith(draft, ensureTokenPath(tokenId), newTokenSrc, Object);
1037
+ break;
1038
+ }
1039
+ case 'static': {
1040
+ // Cannot call another function that uses produce
1041
+ // this.updateToken({
1042
+ // tokenId,
1043
+ // tokenSrcValueInfo,
1044
+ // });
1045
+ // @TODO: find a way to call this.updateToken() instead of repeating below
1046
+ const tokenSrc = makeTokenSrc({
1047
+ tokenId,
1048
+ tokenSrcValueInfo,
1049
+ });
1050
+ const newTokenSrc = loMerge(loGet(draft, tokenId, {}), tokenSrc);
1051
+ loSetWith(draft, ensureTokenPath(tokenId), newTokenSrc, Object);
1052
+ break;
1053
+ }
1054
+ default: {
1055
+ const _exhaustiveCheck = tokenSrcValueInfo;
1056
+ throw new Error(`Unhandled kind: ${JSON.stringify(tokenSrcValueInfo)}`);
1057
+ }
1058
+ }
1059
+ // Token Mode Values should not have $type since the $type of the Mode is
1060
+ // set within the Var object above. This just guarantees $type is not set.
1061
+ loUnset(draft, `${tokenId}.$type`);
1062
+ });
1063
+ });
1064
+ },
1065
+ /**
1066
+ * Var -> unsetModeValue
1067
+ */
1068
+ unsetModeValue: ({ collectionId, varId, modeId, }) => {
1069
+ const tokenId = `${this.collectionsParentKey}.${collectionId}.${varId}.${modeId}`;
1070
+ this.deleteToken({ tokenId });
1071
+ },
1072
+ };
1080
1073
  }
1081
- _TokenSrcUpdater__src = new WeakMap(), _TokenSrcUpdater_prefix = new WeakMap(), _TokenSrcUpdater_dataIsStale = new WeakMap(), _TokenSrcUpdater__data = new WeakMap(), _TokenSrcUpdater_assertIdDoesNotHavePrefixes = new WeakMap(), _TokenSrcUpdater_instances = new WeakSet(), _TokenSrcUpdater_src_set = function _TokenSrcUpdater_src_set(newSrc) {
1082
- var _a, _b, _c;
1083
- __classPrivateFieldSet(this, _TokenSrcUpdater__src, newSrc, "f");
1084
- __classPrivateFieldSet(this, _TokenSrcUpdater_dataIsStale, true, "f");
1085
- this.collectionsParentKey =
1086
- ((_c = (_b = (_a = newSrc === null || newSrc === void 0 ? void 0 : newSrc.$extensions) === null || _a === void 0 ? void 0 : _a['cloud.knapsack']) === null || _b === void 0 ? void 0 : _b.global) === null || _c === void 0 ? void 0 : _c.collectionsParentKey) ||
1087
- '';
1088
- };
1089
1074
  //# sourceMappingURL=token-src-updater.js.map