@tldraw/tlschema 3.16.0-canary.d04b7fc312b4 → 3.16.0-canary.d354cc4340c1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist-cjs/index.js CHANGED
@@ -177,7 +177,7 @@ var import_TLVerticalAlignStyle = require("./styles/TLVerticalAlignStyle");
177
177
  var import_translations = require("./translations/translations");
178
178
  (0, import_utils.registerTldrawLibraryVersion)(
179
179
  "@tldraw/tlschema",
180
- "3.16.0-canary.d04b7fc312b4",
180
+ "3.16.0-canary.d354cc4340c1",
181
181
  "cjs"
182
182
  );
183
183
  //# sourceMappingURL=index.js.map
@@ -28,7 +28,8 @@ const Versions = (0, import_store.createMigrationIds)("com.tldraw.store", {
28
28
  RemoveCodeAndIconShapeTypes: 1,
29
29
  AddInstancePresenceType: 2,
30
30
  RemoveTLUserAndPresenceAndAddPointer: 3,
31
- RemoveUserDocument: 4
31
+ RemoveUserDocument: 4,
32
+ FixIndexKeys: 5
32
33
  });
33
34
  const storeMigrations = (0, import_store.createMigrationSequence)({
34
35
  sequenceId: "com.tldraw.store",
@@ -74,7 +75,38 @@ const storeMigrations = (0, import_store.createMigrationSequence)({
74
75
  }
75
76
  }
76
77
  }
78
+ },
79
+ {
80
+ id: Versions.FixIndexKeys,
81
+ scope: "record",
82
+ up: (record) => {
83
+ if (["shape", "page"].includes(record.typeName) && "index" in record) {
84
+ const recordWithIndex = record;
85
+ if (recordWithIndex.index.endsWith("0") && recordWithIndex.index !== "a0") {
86
+ recordWithIndex.index = recordWithIndex.index.slice(0, -1) + getNRandomBase62Digits(3);
87
+ }
88
+ if (record.typeName === "shape" && recordWithIndex.type === "line") {
89
+ const lineShape = recordWithIndex;
90
+ for (const [_, point] of (0, import_utils.objectMapEntries)(lineShape.props.points)) {
91
+ if (point.index.endsWith("0") && point.index !== "a0") {
92
+ point.index = point.index.slice(0, -1) + getNRandomBase62Digits(3);
93
+ }
94
+ }
95
+ }
96
+ }
97
+ },
98
+ down: () => {
99
+ }
77
100
  }
78
101
  ]
79
102
  });
103
+ const BASE_62_DIGITS_WITHOUT_ZERO = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
104
+ const getRandomBase62Digit = () => {
105
+ return BASE_62_DIGITS_WITHOUT_ZERO.charAt(
106
+ Math.floor(Math.random() * BASE_62_DIGITS_WITHOUT_ZERO.length)
107
+ );
108
+ };
109
+ const getNRandomBase62Digits = (n) => {
110
+ return Array.from({ length: n }, getRandomBase62Digit).join("");
111
+ };
80
112
  //# sourceMappingURL=store-migrations.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/store-migrations.ts"],
4
- "sourcesContent": ["import { createMigrationIds, createMigrationSequence } from '@tldraw/store'\nimport { objectMapEntries } from '@tldraw/utils'\nimport { TLShape } from './records/TLShape'\n\nconst Versions = createMigrationIds('com.tldraw.store', {\n\tRemoveCodeAndIconShapeTypes: 1,\n\tAddInstancePresenceType: 2,\n\tRemoveTLUserAndPresenceAndAddPointer: 3,\n\tRemoveUserDocument: 4,\n} as const)\n\nexport { Versions as storeVersions }\n\n/** @public */\nexport const storeMigrations = createMigrationSequence({\n\tsequenceId: 'com.tldraw.store',\n\tretroactive: false,\n\tsequence: [\n\t\t{\n\t\t\tid: Versions.RemoveCodeAndIconShapeTypes,\n\t\t\tscope: 'store',\n\t\t\tup: (store) => {\n\t\t\t\tfor (const [id, record] of objectMapEntries(store)) {\n\t\t\t\t\tif (\n\t\t\t\t\t\trecord.typeName === 'shape' &&\n\t\t\t\t\t\t((record as TLShape).type === 'icon' || (record as TLShape).type === 'code')\n\t\t\t\t\t) {\n\t\t\t\t\t\tdelete store[id]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddInstancePresenceType,\n\t\t\tscope: 'store',\n\t\t\tup(_store) {\n\t\t\t\t// noop\n\t\t\t\t// there used to be a down migration for this but we made down migrations optional\n\t\t\t\t// and we don't use them on store-level migrations so we can just remove it\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t// remove user and presence records and add pointer records\n\t\t\tid: Versions.RemoveTLUserAndPresenceAndAddPointer,\n\t\t\tscope: 'store',\n\t\t\tup: (store) => {\n\t\t\t\tfor (const [id, record] of objectMapEntries(store)) {\n\t\t\t\t\tif (record.typeName.match(/^(user|user_presence)$/)) {\n\t\t\t\t\t\tdelete store[id]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t// remove user document records\n\t\t\tid: Versions.RemoveUserDocument,\n\t\t\tscope: 'store',\n\t\t\tup: (store) => {\n\t\t\t\tfor (const [id, record] of objectMapEntries(store)) {\n\t\t\t\t\tif (record.typeName.match('user_document')) {\n\t\t\t\t\t\tdelete store[id]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t],\n})\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA4D;AAC5D,mBAAiC;AAGjC,MAAM,eAAW,iCAAmB,oBAAoB;AAAA,EACvD,6BAA6B;AAAA,EAC7B,yBAAyB;AAAA,EACzB,sCAAsC;AAAA,EACtC,oBAAoB;AACrB,CAAU;AAKH,MAAM,sBAAkB,sCAAwB;AAAA,EACtD,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,UAAU;AAAA,IACT;AAAA,MACC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,IAAI,CAAC,UAAU;AACd,mBAAW,CAAC,IAAI,MAAM,SAAK,+BAAiB,KAAK,GAAG;AACnD,cACC,OAAO,aAAa,YAClB,OAAmB,SAAS,UAAW,OAAmB,SAAS,SACpE;AACD,mBAAO,MAAM,EAAE;AAAA,UAChB;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,GAAG,QAAQ;AAAA,MAIX;AAAA,IACD;AAAA,IACA;AAAA;AAAA,MAEC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,IAAI,CAAC,UAAU;AACd,mBAAW,CAAC,IAAI,MAAM,SAAK,+BAAiB,KAAK,GAAG;AACnD,cAAI,OAAO,SAAS,MAAM,wBAAwB,GAAG;AACpD,mBAAO,MAAM,EAAE;AAAA,UAChB;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,IACA;AAAA;AAAA,MAEC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,IAAI,CAAC,UAAU;AACd,mBAAW,CAAC,IAAI,MAAM,SAAK,+BAAiB,KAAK,GAAG;AACnD,cAAI,OAAO,SAAS,MAAM,eAAe,GAAG;AAC3C,mBAAO,MAAM,EAAE;AAAA,UAChB;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,EACD;AACD,CAAC;",
4
+ "sourcesContent": ["import { createMigrationIds, createMigrationSequence } from '@tldraw/store'\nimport { IndexKey, objectMapEntries } from '@tldraw/utils'\nimport { TLPage } from './records/TLPage'\nimport { TLShape } from './records/TLShape'\nimport { TLLineShape } from './shapes/TLLineShape'\n\nconst Versions = createMigrationIds('com.tldraw.store', {\n\tRemoveCodeAndIconShapeTypes: 1,\n\tAddInstancePresenceType: 2,\n\tRemoveTLUserAndPresenceAndAddPointer: 3,\n\tRemoveUserDocument: 4,\n\tFixIndexKeys: 5,\n} as const)\n\nexport { Versions as storeVersions }\n\n/** @public */\nexport const storeMigrations = createMigrationSequence({\n\tsequenceId: 'com.tldraw.store',\n\tretroactive: false,\n\tsequence: [\n\t\t{\n\t\t\tid: Versions.RemoveCodeAndIconShapeTypes,\n\t\t\tscope: 'store',\n\t\t\tup: (store) => {\n\t\t\t\tfor (const [id, record] of objectMapEntries(store)) {\n\t\t\t\t\tif (\n\t\t\t\t\t\trecord.typeName === 'shape' &&\n\t\t\t\t\t\t((record as TLShape).type === 'icon' || (record as TLShape).type === 'code')\n\t\t\t\t\t) {\n\t\t\t\t\t\tdelete store[id]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddInstancePresenceType,\n\t\t\tscope: 'store',\n\t\t\tup(_store) {\n\t\t\t\t// noop\n\t\t\t\t// there used to be a down migration for this but we made down migrations optional\n\t\t\t\t// and we don't use them on store-level migrations so we can just remove it\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t// remove user and presence records and add pointer records\n\t\t\tid: Versions.RemoveTLUserAndPresenceAndAddPointer,\n\t\t\tscope: 'store',\n\t\t\tup: (store) => {\n\t\t\t\tfor (const [id, record] of objectMapEntries(store)) {\n\t\t\t\t\tif (record.typeName.match(/^(user|user_presence)$/)) {\n\t\t\t\t\t\tdelete store[id]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t// remove user document records\n\t\t\tid: Versions.RemoveUserDocument,\n\t\t\tscope: 'store',\n\t\t\tup: (store) => {\n\t\t\t\tfor (const [id, record] of objectMapEntries(store)) {\n\t\t\t\t\tif (record.typeName.match('user_document')) {\n\t\t\t\t\t\tdelete store[id]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.FixIndexKeys,\n\t\t\tscope: 'record',\n\t\t\tup: (record) => {\n\t\t\t\tif (['shape', 'page'].includes(record.typeName) && 'index' in record) {\n\t\t\t\t\tconst recordWithIndex = record as TLShape | TLPage\n\t\t\t\t\t// Our newer fractional indexed library (more correctly) validates that indices\n\t\t\t\t\t// do not end with 0. ('a0' being an exception)\n\t\t\t\t\tif (recordWithIndex.index.endsWith('0') && recordWithIndex.index !== 'a0') {\n\t\t\t\t\t\trecordWithIndex.index = (recordWithIndex.index.slice(0, -1) +\n\t\t\t\t\t\t\tgetNRandomBase62Digits(3)) as IndexKey\n\t\t\t\t\t}\n\t\t\t\t\t// Line shapes have 'points' that have indices as well.\n\t\t\t\t\tif (record.typeName === 'shape' && (recordWithIndex as TLShape).type === 'line') {\n\t\t\t\t\t\tconst lineShape = recordWithIndex as TLLineShape\n\t\t\t\t\t\tfor (const [_, point] of objectMapEntries(lineShape.props.points)) {\n\t\t\t\t\t\t\tif (point.index.endsWith('0') && point.index !== 'a0') {\n\t\t\t\t\t\t\t\tpoint.index = (point.index.slice(0, -1) + getNRandomBase62Digits(3)) as IndexKey\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\tdown: () => {\n\t\t\t\t// noop\n\t\t\t\t// Enables tlsync to support older clients so as to not force people to refresh immediately after deploying.\n\t\t\t},\n\t\t},\n\t],\n})\n\nconst BASE_62_DIGITS_WITHOUT_ZERO = '123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'\nconst getRandomBase62Digit = () => {\n\treturn BASE_62_DIGITS_WITHOUT_ZERO.charAt(\n\t\tMath.floor(Math.random() * BASE_62_DIGITS_WITHOUT_ZERO.length)\n\t)\n}\n\nconst getNRandomBase62Digits = (n: number) => {\n\treturn Array.from({ length: n }, getRandomBase62Digit).join('')\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA4D;AAC5D,mBAA2C;AAK3C,MAAM,eAAW,iCAAmB,oBAAoB;AAAA,EACvD,6BAA6B;AAAA,EAC7B,yBAAyB;AAAA,EACzB,sCAAsC;AAAA,EACtC,oBAAoB;AAAA,EACpB,cAAc;AACf,CAAU;AAKH,MAAM,sBAAkB,sCAAwB;AAAA,EACtD,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,UAAU;AAAA,IACT;AAAA,MACC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,IAAI,CAAC,UAAU;AACd,mBAAW,CAAC,IAAI,MAAM,SAAK,+BAAiB,KAAK,GAAG;AACnD,cACC,OAAO,aAAa,YAClB,OAAmB,SAAS,UAAW,OAAmB,SAAS,SACpE;AACD,mBAAO,MAAM,EAAE;AAAA,UAChB;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,GAAG,QAAQ;AAAA,MAIX;AAAA,IACD;AAAA,IACA;AAAA;AAAA,MAEC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,IAAI,CAAC,UAAU;AACd,mBAAW,CAAC,IAAI,MAAM,SAAK,+BAAiB,KAAK,GAAG;AACnD,cAAI,OAAO,SAAS,MAAM,wBAAwB,GAAG;AACpD,mBAAO,MAAM,EAAE;AAAA,UAChB;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,IACA;AAAA;AAAA,MAEC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,IAAI,CAAC,UAAU;AACd,mBAAW,CAAC,IAAI,MAAM,SAAK,+BAAiB,KAAK,GAAG;AACnD,cAAI,OAAO,SAAS,MAAM,eAAe,GAAG;AAC3C,mBAAO,MAAM,EAAE;AAAA,UAChB;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,IAAI,CAAC,WAAW;AACf,YAAI,CAAC,SAAS,MAAM,EAAE,SAAS,OAAO,QAAQ,KAAK,WAAW,QAAQ;AACrE,gBAAM,kBAAkB;AAGxB,cAAI,gBAAgB,MAAM,SAAS,GAAG,KAAK,gBAAgB,UAAU,MAAM;AAC1E,4BAAgB,QAAS,gBAAgB,MAAM,MAAM,GAAG,EAAE,IACzD,uBAAuB,CAAC;AAAA,UAC1B;AAEA,cAAI,OAAO,aAAa,WAAY,gBAA4B,SAAS,QAAQ;AAChF,kBAAM,YAAY;AAClB,uBAAW,CAAC,GAAG,KAAK,SAAK,+BAAiB,UAAU,MAAM,MAAM,GAAG;AAClE,kBAAI,MAAM,MAAM,SAAS,GAAG,KAAK,MAAM,UAAU,MAAM;AACtD,sBAAM,QAAS,MAAM,MAAM,MAAM,GAAG,EAAE,IAAI,uBAAuB,CAAC;AAAA,cACnE;AAAA,YACD;AAAA,UACD;AAAA,QACD;AAAA,MACD;AAAA,MACA,MAAM,MAAM;AAAA,MAGZ;AAAA,IACD;AAAA,EACD;AACD,CAAC;AAED,MAAM,8BAA8B;AACpC,MAAM,uBAAuB,MAAM;AAClC,SAAO,4BAA4B;AAAA,IAClC,KAAK,MAAM,KAAK,OAAO,IAAI,4BAA4B,MAAM;AAAA,EAC9D;AACD;AAEA,MAAM,yBAAyB,CAAC,MAAc;AAC7C,SAAO,MAAM,KAAK,EAAE,QAAQ,EAAE,GAAG,oBAAoB,EAAE,KAAK,EAAE;AAC/D;",
6
6
  "names": []
7
7
  }
@@ -172,7 +172,7 @@ import {
172
172
  } from "./translations/translations.mjs";
173
173
  registerTldrawLibraryVersion(
174
174
  "@tldraw/tlschema",
175
- "3.16.0-canary.d04b7fc312b4",
175
+ "3.16.0-canary.d354cc4340c1",
176
176
  "esm"
177
177
  );
178
178
  export {
@@ -4,7 +4,8 @@ const Versions = createMigrationIds("com.tldraw.store", {
4
4
  RemoveCodeAndIconShapeTypes: 1,
5
5
  AddInstancePresenceType: 2,
6
6
  RemoveTLUserAndPresenceAndAddPointer: 3,
7
- RemoveUserDocument: 4
7
+ RemoveUserDocument: 4,
8
+ FixIndexKeys: 5
8
9
  });
9
10
  const storeMigrations = createMigrationSequence({
10
11
  sequenceId: "com.tldraw.store",
@@ -50,9 +51,40 @@ const storeMigrations = createMigrationSequence({
50
51
  }
51
52
  }
52
53
  }
54
+ },
55
+ {
56
+ id: Versions.FixIndexKeys,
57
+ scope: "record",
58
+ up: (record) => {
59
+ if (["shape", "page"].includes(record.typeName) && "index" in record) {
60
+ const recordWithIndex = record;
61
+ if (recordWithIndex.index.endsWith("0") && recordWithIndex.index !== "a0") {
62
+ recordWithIndex.index = recordWithIndex.index.slice(0, -1) + getNRandomBase62Digits(3);
63
+ }
64
+ if (record.typeName === "shape" && recordWithIndex.type === "line") {
65
+ const lineShape = recordWithIndex;
66
+ for (const [_, point] of objectMapEntries(lineShape.props.points)) {
67
+ if (point.index.endsWith("0") && point.index !== "a0") {
68
+ point.index = point.index.slice(0, -1) + getNRandomBase62Digits(3);
69
+ }
70
+ }
71
+ }
72
+ }
73
+ },
74
+ down: () => {
75
+ }
53
76
  }
54
77
  ]
55
78
  });
79
+ const BASE_62_DIGITS_WITHOUT_ZERO = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
80
+ const getRandomBase62Digit = () => {
81
+ return BASE_62_DIGITS_WITHOUT_ZERO.charAt(
82
+ Math.floor(Math.random() * BASE_62_DIGITS_WITHOUT_ZERO.length)
83
+ );
84
+ };
85
+ const getNRandomBase62Digits = (n) => {
86
+ return Array.from({ length: n }, getRandomBase62Digit).join("");
87
+ };
56
88
  export {
57
89
  storeMigrations,
58
90
  Versions as storeVersions
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/store-migrations.ts"],
4
- "sourcesContent": ["import { createMigrationIds, createMigrationSequence } from '@tldraw/store'\nimport { objectMapEntries } from '@tldraw/utils'\nimport { TLShape } from './records/TLShape'\n\nconst Versions = createMigrationIds('com.tldraw.store', {\n\tRemoveCodeAndIconShapeTypes: 1,\n\tAddInstancePresenceType: 2,\n\tRemoveTLUserAndPresenceAndAddPointer: 3,\n\tRemoveUserDocument: 4,\n} as const)\n\nexport { Versions as storeVersions }\n\n/** @public */\nexport const storeMigrations = createMigrationSequence({\n\tsequenceId: 'com.tldraw.store',\n\tretroactive: false,\n\tsequence: [\n\t\t{\n\t\t\tid: Versions.RemoveCodeAndIconShapeTypes,\n\t\t\tscope: 'store',\n\t\t\tup: (store) => {\n\t\t\t\tfor (const [id, record] of objectMapEntries(store)) {\n\t\t\t\t\tif (\n\t\t\t\t\t\trecord.typeName === 'shape' &&\n\t\t\t\t\t\t((record as TLShape).type === 'icon' || (record as TLShape).type === 'code')\n\t\t\t\t\t) {\n\t\t\t\t\t\tdelete store[id]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddInstancePresenceType,\n\t\t\tscope: 'store',\n\t\t\tup(_store) {\n\t\t\t\t// noop\n\t\t\t\t// there used to be a down migration for this but we made down migrations optional\n\t\t\t\t// and we don't use them on store-level migrations so we can just remove it\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t// remove user and presence records and add pointer records\n\t\t\tid: Versions.RemoveTLUserAndPresenceAndAddPointer,\n\t\t\tscope: 'store',\n\t\t\tup: (store) => {\n\t\t\t\tfor (const [id, record] of objectMapEntries(store)) {\n\t\t\t\t\tif (record.typeName.match(/^(user|user_presence)$/)) {\n\t\t\t\t\t\tdelete store[id]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t// remove user document records\n\t\t\tid: Versions.RemoveUserDocument,\n\t\t\tscope: 'store',\n\t\t\tup: (store) => {\n\t\t\t\tfor (const [id, record] of objectMapEntries(store)) {\n\t\t\t\t\tif (record.typeName.match('user_document')) {\n\t\t\t\t\t\tdelete store[id]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t],\n})\n"],
5
- "mappings": "AAAA,SAAS,oBAAoB,+BAA+B;AAC5D,SAAS,wBAAwB;AAGjC,MAAM,WAAW,mBAAmB,oBAAoB;AAAA,EACvD,6BAA6B;AAAA,EAC7B,yBAAyB;AAAA,EACzB,sCAAsC;AAAA,EACtC,oBAAoB;AACrB,CAAU;AAKH,MAAM,kBAAkB,wBAAwB;AAAA,EACtD,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,UAAU;AAAA,IACT;AAAA,MACC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,IAAI,CAAC,UAAU;AACd,mBAAW,CAAC,IAAI,MAAM,KAAK,iBAAiB,KAAK,GAAG;AACnD,cACC,OAAO,aAAa,YAClB,OAAmB,SAAS,UAAW,OAAmB,SAAS,SACpE;AACD,mBAAO,MAAM,EAAE;AAAA,UAChB;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,GAAG,QAAQ;AAAA,MAIX;AAAA,IACD;AAAA,IACA;AAAA;AAAA,MAEC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,IAAI,CAAC,UAAU;AACd,mBAAW,CAAC,IAAI,MAAM,KAAK,iBAAiB,KAAK,GAAG;AACnD,cAAI,OAAO,SAAS,MAAM,wBAAwB,GAAG;AACpD,mBAAO,MAAM,EAAE;AAAA,UAChB;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,IACA;AAAA;AAAA,MAEC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,IAAI,CAAC,UAAU;AACd,mBAAW,CAAC,IAAI,MAAM,KAAK,iBAAiB,KAAK,GAAG;AACnD,cAAI,OAAO,SAAS,MAAM,eAAe,GAAG;AAC3C,mBAAO,MAAM,EAAE;AAAA,UAChB;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,EACD;AACD,CAAC;",
4
+ "sourcesContent": ["import { createMigrationIds, createMigrationSequence } from '@tldraw/store'\nimport { IndexKey, objectMapEntries } from '@tldraw/utils'\nimport { TLPage } from './records/TLPage'\nimport { TLShape } from './records/TLShape'\nimport { TLLineShape } from './shapes/TLLineShape'\n\nconst Versions = createMigrationIds('com.tldraw.store', {\n\tRemoveCodeAndIconShapeTypes: 1,\n\tAddInstancePresenceType: 2,\n\tRemoveTLUserAndPresenceAndAddPointer: 3,\n\tRemoveUserDocument: 4,\n\tFixIndexKeys: 5,\n} as const)\n\nexport { Versions as storeVersions }\n\n/** @public */\nexport const storeMigrations = createMigrationSequence({\n\tsequenceId: 'com.tldraw.store',\n\tretroactive: false,\n\tsequence: [\n\t\t{\n\t\t\tid: Versions.RemoveCodeAndIconShapeTypes,\n\t\t\tscope: 'store',\n\t\t\tup: (store) => {\n\t\t\t\tfor (const [id, record] of objectMapEntries(store)) {\n\t\t\t\t\tif (\n\t\t\t\t\t\trecord.typeName === 'shape' &&\n\t\t\t\t\t\t((record as TLShape).type === 'icon' || (record as TLShape).type === 'code')\n\t\t\t\t\t) {\n\t\t\t\t\t\tdelete store[id]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddInstancePresenceType,\n\t\t\tscope: 'store',\n\t\t\tup(_store) {\n\t\t\t\t// noop\n\t\t\t\t// there used to be a down migration for this but we made down migrations optional\n\t\t\t\t// and we don't use them on store-level migrations so we can just remove it\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t// remove user and presence records and add pointer records\n\t\t\tid: Versions.RemoveTLUserAndPresenceAndAddPointer,\n\t\t\tscope: 'store',\n\t\t\tup: (store) => {\n\t\t\t\tfor (const [id, record] of objectMapEntries(store)) {\n\t\t\t\t\tif (record.typeName.match(/^(user|user_presence)$/)) {\n\t\t\t\t\t\tdelete store[id]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t// remove user document records\n\t\t\tid: Versions.RemoveUserDocument,\n\t\t\tscope: 'store',\n\t\t\tup: (store) => {\n\t\t\t\tfor (const [id, record] of objectMapEntries(store)) {\n\t\t\t\t\tif (record.typeName.match('user_document')) {\n\t\t\t\t\t\tdelete store[id]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.FixIndexKeys,\n\t\t\tscope: 'record',\n\t\t\tup: (record) => {\n\t\t\t\tif (['shape', 'page'].includes(record.typeName) && 'index' in record) {\n\t\t\t\t\tconst recordWithIndex = record as TLShape | TLPage\n\t\t\t\t\t// Our newer fractional indexed library (more correctly) validates that indices\n\t\t\t\t\t// do not end with 0. ('a0' being an exception)\n\t\t\t\t\tif (recordWithIndex.index.endsWith('0') && recordWithIndex.index !== 'a0') {\n\t\t\t\t\t\trecordWithIndex.index = (recordWithIndex.index.slice(0, -1) +\n\t\t\t\t\t\t\tgetNRandomBase62Digits(3)) as IndexKey\n\t\t\t\t\t}\n\t\t\t\t\t// Line shapes have 'points' that have indices as well.\n\t\t\t\t\tif (record.typeName === 'shape' && (recordWithIndex as TLShape).type === 'line') {\n\t\t\t\t\t\tconst lineShape = recordWithIndex as TLLineShape\n\t\t\t\t\t\tfor (const [_, point] of objectMapEntries(lineShape.props.points)) {\n\t\t\t\t\t\t\tif (point.index.endsWith('0') && point.index !== 'a0') {\n\t\t\t\t\t\t\t\tpoint.index = (point.index.slice(0, -1) + getNRandomBase62Digits(3)) as IndexKey\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\tdown: () => {\n\t\t\t\t// noop\n\t\t\t\t// Enables tlsync to support older clients so as to not force people to refresh immediately after deploying.\n\t\t\t},\n\t\t},\n\t],\n})\n\nconst BASE_62_DIGITS_WITHOUT_ZERO = '123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'\nconst getRandomBase62Digit = () => {\n\treturn BASE_62_DIGITS_WITHOUT_ZERO.charAt(\n\t\tMath.floor(Math.random() * BASE_62_DIGITS_WITHOUT_ZERO.length)\n\t)\n}\n\nconst getNRandomBase62Digits = (n: number) => {\n\treturn Array.from({ length: n }, getRandomBase62Digit).join('')\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB,+BAA+B;AAC5D,SAAmB,wBAAwB;AAK3C,MAAM,WAAW,mBAAmB,oBAAoB;AAAA,EACvD,6BAA6B;AAAA,EAC7B,yBAAyB;AAAA,EACzB,sCAAsC;AAAA,EACtC,oBAAoB;AAAA,EACpB,cAAc;AACf,CAAU;AAKH,MAAM,kBAAkB,wBAAwB;AAAA,EACtD,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,UAAU;AAAA,IACT;AAAA,MACC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,IAAI,CAAC,UAAU;AACd,mBAAW,CAAC,IAAI,MAAM,KAAK,iBAAiB,KAAK,GAAG;AACnD,cACC,OAAO,aAAa,YAClB,OAAmB,SAAS,UAAW,OAAmB,SAAS,SACpE;AACD,mBAAO,MAAM,EAAE;AAAA,UAChB;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,GAAG,QAAQ;AAAA,MAIX;AAAA,IACD;AAAA,IACA;AAAA;AAAA,MAEC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,IAAI,CAAC,UAAU;AACd,mBAAW,CAAC,IAAI,MAAM,KAAK,iBAAiB,KAAK,GAAG;AACnD,cAAI,OAAO,SAAS,MAAM,wBAAwB,GAAG;AACpD,mBAAO,MAAM,EAAE;AAAA,UAChB;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,IACA;AAAA;AAAA,MAEC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,IAAI,CAAC,UAAU;AACd,mBAAW,CAAC,IAAI,MAAM,KAAK,iBAAiB,KAAK,GAAG;AACnD,cAAI,OAAO,SAAS,MAAM,eAAe,GAAG;AAC3C,mBAAO,MAAM,EAAE;AAAA,UAChB;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,IAAI,CAAC,WAAW;AACf,YAAI,CAAC,SAAS,MAAM,EAAE,SAAS,OAAO,QAAQ,KAAK,WAAW,QAAQ;AACrE,gBAAM,kBAAkB;AAGxB,cAAI,gBAAgB,MAAM,SAAS,GAAG,KAAK,gBAAgB,UAAU,MAAM;AAC1E,4BAAgB,QAAS,gBAAgB,MAAM,MAAM,GAAG,EAAE,IACzD,uBAAuB,CAAC;AAAA,UAC1B;AAEA,cAAI,OAAO,aAAa,WAAY,gBAA4B,SAAS,QAAQ;AAChF,kBAAM,YAAY;AAClB,uBAAW,CAAC,GAAG,KAAK,KAAK,iBAAiB,UAAU,MAAM,MAAM,GAAG;AAClE,kBAAI,MAAM,MAAM,SAAS,GAAG,KAAK,MAAM,UAAU,MAAM;AACtD,sBAAM,QAAS,MAAM,MAAM,MAAM,GAAG,EAAE,IAAI,uBAAuB,CAAC;AAAA,cACnE;AAAA,YACD;AAAA,UACD;AAAA,QACD;AAAA,MACD;AAAA,MACA,MAAM,MAAM;AAAA,MAGZ;AAAA,IACD;AAAA,EACD;AACD,CAAC;AAED,MAAM,8BAA8B;AACpC,MAAM,uBAAuB,MAAM;AAClC,SAAO,4BAA4B;AAAA,IAClC,KAAK,MAAM,KAAK,OAAO,IAAI,4BAA4B,MAAM;AAAA,EAC9D;AACD;AAEA,MAAM,yBAAyB,CAAC,MAAc;AAC7C,SAAO,MAAM,KAAK,EAAE,QAAQ,EAAE,GAAG,oBAAoB,EAAE,KAAK,EAAE;AAC/D;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tldraw/tlschema",
3
3
  "description": "tldraw infinite canvas SDK (schema).",
4
- "version": "3.16.0-canary.d04b7fc312b4",
4
+ "version": "3.16.0-canary.d354cc4340c1",
5
5
  "author": {
6
6
  "name": "tldraw Inc.",
7
7
  "email": "hello@tldraw.com"
@@ -32,33 +32,29 @@
32
32
  "src"
33
33
  ],
34
34
  "scripts": {
35
- "test-ci": "lazy inherit",
36
- "test": "yarn run -T jest",
37
- "test-coverage": "lazy inherit",
35
+ "test-ci": "yarn run -T vitest run --passWithNoTests",
36
+ "test": "yarn run -T vitest --passWithNoTests",
37
+ "test-coverage": "yarn run -T vitest run --coverage --passWithNoTests",
38
38
  "format": "yarn run -T prettier --write --cache \"src/**/*.{ts,tsx,js,jsx,json,md}\"",
39
39
  "build": "yarn run -T tsx ../../internal/scripts/build-package.ts",
40
40
  "build-api": "yarn run -T tsx ../../internal/scripts/build-api.ts",
41
41
  "prepack": "yarn run -T tsx ../../internal/scripts/prepack.ts",
42
42
  "postpack": "../../internal/scripts/postpack.sh",
43
43
  "pack-tarball": "yarn pack",
44
- "lint": "yarn run -T tsx ../../internal/scripts/lint.ts"
44
+ "lint": "yarn run -T tsx ../../internal/scripts/lint.ts",
45
+ "context": "yarn run -T tsx ../../internal/scripts/context.ts"
45
46
  },
46
47
  "devDependencies": {
47
48
  "kleur": "^4.1.5",
48
49
  "lazyrepo": "0.0.0-alpha.27",
49
- "react": "^18.3.1"
50
- },
51
- "jest": {
52
- "preset": "../../internal/config/jest/node/jest-preset.js",
53
- "moduleNameMapper": {
54
- "^~(.*)": "<rootDir>/src/$1"
55
- }
50
+ "react": "^18.3.1",
51
+ "vitest": "^3.2.4"
56
52
  },
57
53
  "dependencies": {
58
- "@tldraw/state": "3.16.0-canary.d04b7fc312b4",
59
- "@tldraw/store": "3.16.0-canary.d04b7fc312b4",
60
- "@tldraw/utils": "3.16.0-canary.d04b7fc312b4",
61
- "@tldraw/validate": "3.16.0-canary.d04b7fc312b4"
54
+ "@tldraw/state": "3.16.0-canary.d354cc4340c1",
55
+ "@tldraw/store": "3.16.0-canary.d354cc4340c1",
56
+ "@tldraw/utils": "3.16.0-canary.d354cc4340c1",
57
+ "@tldraw/validate": "3.16.0-canary.d354cc4340c1"
62
58
  },
63
59
  "peerDependencies": {
64
60
  "react": "^18.2.0 || ^19.0.0",
@@ -1,5 +1,6 @@
1
1
  import { Migration, MigrationId } from '@tldraw/store'
2
2
  import { mockUniqueId, structuredClone } from '@tldraw/utils'
3
+ import { vi } from 'vitest'
3
4
  import { createTLSchema } from '../createTLSchema'
4
5
 
5
6
  let nextNanoId = 0
@@ -9,9 +10,9 @@ export const testSchema = createTLSchema()
9
10
 
10
11
  // mock all migrator fns
11
12
  for (const migration of testSchema.sortedMigrations) {
12
- ;(migration as any).up = jest.fn(migration.up as any)
13
+ ;(migration as any).up = vi.fn(migration.up as any)
13
14
  if (typeof migration.down === 'function') {
14
- ;(migration as any).down = jest.fn(migration.down as any)
15
+ ;(migration as any).down = vi.fn(migration.down as any)
15
16
  }
16
17
  }
17
18
 
@@ -227,6 +227,50 @@ describe('Store removing Icon and Code shapes', () => {
227
227
  })
228
228
  })
229
229
 
230
+ describe('Fixing index keys', () => {
231
+ const { up, down } = getTestMigration(storeVersions.FixIndexKeys)
232
+ test('up works as expected', () => {
233
+ const snapshot = [
234
+ ShapeRecord.create({
235
+ type: 'shape',
236
+ id: 'shape:1',
237
+ parentId: 'page:any',
238
+ index: 'a0',
239
+ } as any),
240
+ ShapeRecord.create({
241
+ type: 'shape',
242
+ id: 'shape:2',
243
+ parentId: 'page:any',
244
+ index: 'a00',
245
+ } as any),
246
+ ShapeRecord.create({
247
+ type: 'shape',
248
+ id: 'shape:3',
249
+ parentId: 'page:any',
250
+ index: 'a111',
251
+ } as any),
252
+ ]
253
+ const fixed = snapshot.map((shape) => up(shape))
254
+ expect(fixed.find((s) => s.id === 'shape:1')?.index).toBe('a0')
255
+ expect(fixed.find((s) => s.id === 'shape:2')?.index).not.toBe('a00')
256
+ expect(fixed.find((s) => s.id === 'shape:2')?.index).toMatch(/^a0[1-9A-Za-z]{3}$/)
257
+ expect(fixed.find((s) => s.id === 'shape:3')?.index).toBe('a111')
258
+ })
259
+
260
+ test('down works as expected', () => {
261
+ const snapshot = [
262
+ ShapeRecord.create({
263
+ type: 'shape',
264
+ id: 'shape:1',
265
+ parentId: 'page:any',
266
+ index: 'a00',
267
+ } as any),
268
+ ]
269
+ const unchanged = snapshot.map((shape) => down(shape))
270
+ expect(unchanged.find((s) => s.id === 'shape:1')?.index).toBe('a00')
271
+ })
272
+ })
273
+
230
274
  describe('Adding export background', () => {
231
275
  const { up } = getTestMigration(instanceVersions.AddTransparentExportBgs)
232
276
  test('up works as expected', () => {
@@ -1,12 +1,15 @@
1
1
  import { createMigrationIds, createMigrationSequence } from '@tldraw/store'
2
- import { objectMapEntries } from '@tldraw/utils'
2
+ import { IndexKey, objectMapEntries } from '@tldraw/utils'
3
+ import { TLPage } from './records/TLPage'
3
4
  import { TLShape } from './records/TLShape'
5
+ import { TLLineShape } from './shapes/TLLineShape'
4
6
 
5
7
  const Versions = createMigrationIds('com.tldraw.store', {
6
8
  RemoveCodeAndIconShapeTypes: 1,
7
9
  AddInstancePresenceType: 2,
8
10
  RemoveTLUserAndPresenceAndAddPointer: 3,
9
11
  RemoveUserDocument: 4,
12
+ FixIndexKeys: 5,
10
13
  } as const)
11
14
 
12
15
  export { Versions as storeVersions }
@@ -63,5 +66,44 @@ export const storeMigrations = createMigrationSequence({
63
66
  }
64
67
  },
65
68
  },
69
+ {
70
+ id: Versions.FixIndexKeys,
71
+ scope: 'record',
72
+ up: (record) => {
73
+ if (['shape', 'page'].includes(record.typeName) && 'index' in record) {
74
+ const recordWithIndex = record as TLShape | TLPage
75
+ // Our newer fractional indexed library (more correctly) validates that indices
76
+ // do not end with 0. ('a0' being an exception)
77
+ if (recordWithIndex.index.endsWith('0') && recordWithIndex.index !== 'a0') {
78
+ recordWithIndex.index = (recordWithIndex.index.slice(0, -1) +
79
+ getNRandomBase62Digits(3)) as IndexKey
80
+ }
81
+ // Line shapes have 'points' that have indices as well.
82
+ if (record.typeName === 'shape' && (recordWithIndex as TLShape).type === 'line') {
83
+ const lineShape = recordWithIndex as TLLineShape
84
+ for (const [_, point] of objectMapEntries(lineShape.props.points)) {
85
+ if (point.index.endsWith('0') && point.index !== 'a0') {
86
+ point.index = (point.index.slice(0, -1) + getNRandomBase62Digits(3)) as IndexKey
87
+ }
88
+ }
89
+ }
90
+ }
91
+ },
92
+ down: () => {
93
+ // noop
94
+ // Enables tlsync to support older clients so as to not force people to refresh immediately after deploying.
95
+ },
96
+ },
66
97
  ],
67
98
  })
99
+
100
+ const BASE_62_DIGITS_WITHOUT_ZERO = '123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
101
+ const getRandomBase62Digit = () => {
102
+ return BASE_62_DIGITS_WITHOUT_ZERO.charAt(
103
+ Math.floor(Math.random() * BASE_62_DIGITS_WITHOUT_ZERO.length)
104
+ )
105
+ }
106
+
107
+ const getNRandomBase62Digits = (n: number) => {
108
+ return Array.from({ length: n }, getRandomBase62Digit).join('')
109
+ }