@tldraw/tlschema 3.16.0-canary.f55016ece635 → 3.16.0-canary.f5bf2b535ea7
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 +1 -1
- package/dist-cjs/store-migrations.js +33 -1
- package/dist-cjs/store-migrations.js.map +2 -2
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/store-migrations.mjs +33 -1
- package/dist-esm/store-migrations.mjs.map +2 -2
- package/package.json +5 -5
- package/src/migrations.test.ts +44 -0
- package/src/store-migrations.ts +43 -1
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.
|
|
180
|
+
"3.16.0-canary.f5bf2b535ea7",
|
|
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,
|
|
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
|
}
|
package/dist-esm/index.mjs
CHANGED
|
@@ -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,
|
|
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.
|
|
4
|
+
"version": "3.16.0-canary.f5bf2b535ea7",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tldraw Inc.",
|
|
7
7
|
"email": "hello@tldraw.com"
|
|
@@ -51,10 +51,10 @@
|
|
|
51
51
|
"vitest": "^3.2.4"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@tldraw/state": "3.16.0-canary.
|
|
55
|
-
"@tldraw/store": "3.16.0-canary.
|
|
56
|
-
"@tldraw/utils": "3.16.0-canary.
|
|
57
|
-
"@tldraw/validate": "3.16.0-canary.
|
|
54
|
+
"@tldraw/state": "3.16.0-canary.f5bf2b535ea7",
|
|
55
|
+
"@tldraw/store": "3.16.0-canary.f5bf2b535ea7",
|
|
56
|
+
"@tldraw/utils": "3.16.0-canary.f5bf2b535ea7",
|
|
57
|
+
"@tldraw/validate": "3.16.0-canary.f5bf2b535ea7"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
60
|
"react": "^18.2.0 || ^19.0.0",
|
package/src/migrations.test.ts
CHANGED
|
@@ -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', () => {
|
package/src/store-migrations.ts
CHANGED
|
@@ -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
|
+
}
|