kopia lustrzana https://github.com/Tldraw/Tldraw
merge
commit
d96b69394e
|
@ -92,4 +92,6 @@ apps/docs/content/gen
|
|||
.env*
|
||||
|
||||
.wrangler
|
||||
/vercel.json
|
||||
/vercel.json
|
||||
license-report-prod.html
|
||||
license-report.html
|
||||
|
|
|
@ -111,6 +111,7 @@
|
|||
"action.zoom-to-selection": "Zoom to selection",
|
||||
"assets.files.upload-failed": "Upload failed",
|
||||
"assets.url.failed": "Couldn't load URL preview",
|
||||
"color-style.white": "White",
|
||||
"color-style.black": "Black",
|
||||
"color-style.blue": "Blue",
|
||||
"color-style.green": "Green",
|
||||
|
|
|
@ -96,6 +96,7 @@
|
|||
"jest": "30.0.0-alpha.2",
|
||||
"json5": "^2.2.3",
|
||||
"lazyrepo": "0.0.0-alpha.27",
|
||||
"license-report": "^6.5.0",
|
||||
"lint-staged": ">=10",
|
||||
"prettier": "^3.0.3",
|
||||
"prettier-plugin-organize-imports": "^3.2.3",
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,7 @@
|
|||
import {
|
||||
ANIMATION_MEDIUM_MS,
|
||||
Box,
|
||||
DefaultColorStyle,
|
||||
Editor,
|
||||
HALF_PI,
|
||||
PageRecordType,
|
||||
|
@ -1281,6 +1282,23 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
|
|||
trackEvent('new-page', { source })
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'select-white-color',
|
||||
label: 'color-style.white',
|
||||
kbd: '?t',
|
||||
onSelect(source) {
|
||||
const style = DefaultColorStyle
|
||||
editor.batch(() => {
|
||||
editor.mark('change-color')
|
||||
if (editor.isIn('select')) {
|
||||
editor.setStyleForSelectedShapes(style, 'white', { squashing: false })
|
||||
}
|
||||
editor.setStyleForNextShapes(style, 'white', { squashing: false })
|
||||
editor.updateInstanceState({ isChangingStyle: true }, { ephemeral: true })
|
||||
})
|
||||
trackEvent('set-style', { source, id: style.id, value: 'white' })
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
const actions = makeActions(actionItems)
|
||||
|
|
|
@ -115,6 +115,7 @@ export type TLUiTranslationKey =
|
|||
| 'action.zoom-to-selection'
|
||||
| 'assets.files.upload-failed'
|
||||
| 'assets.url.failed'
|
||||
| 'color-style.white'
|
||||
| 'color-style.black'
|
||||
| 'color-style.blue'
|
||||
| 'color-style.green'
|
||||
|
|
|
@ -115,6 +115,7 @@ export const DEFAULT_TRANSLATION = {
|
|||
'action.zoom-to-selection': 'Zoom to selection',
|
||||
'assets.files.upload-failed': 'Upload failed',
|
||||
'assets.url.failed': "Couldn't load URL preview",
|
||||
'color-style.white': 'White',
|
||||
'color-style.black': 'Black',
|
||||
'color-style.blue': 'Blue',
|
||||
'color-style.green': 'Green',
|
||||
|
|
|
@ -30,8 +30,8 @@ export const arrowShapeMigrations: Migrations;
|
|||
|
||||
// @public (undocumented)
|
||||
export const arrowShapeProps: {
|
||||
labelColor: EnumStyleProp<"black" | "blue" | "green" | "grey" | "light-blue" | "light-green" | "light-red" | "light-violet" | "orange" | "red" | "violet" | "yellow">;
|
||||
color: EnumStyleProp<"black" | "blue" | "green" | "grey" | "light-blue" | "light-green" | "light-red" | "light-violet" | "orange" | "red" | "violet" | "yellow">;
|
||||
labelColor: EnumStyleProp<"black" | "blue" | "green" | "grey" | "light-blue" | "light-green" | "light-red" | "light-violet" | "orange" | "red" | "violet" | "white" | "yellow">;
|
||||
color: EnumStyleProp<"black" | "blue" | "green" | "grey" | "light-blue" | "light-green" | "light-red" | "light-violet" | "orange" | "red" | "violet" | "white" | "yellow">;
|
||||
fill: EnumStyleProp<"none" | "pattern" | "semi" | "solid">;
|
||||
dash: EnumStyleProp<"dashed" | "dotted" | "draw" | "solid">;
|
||||
size: EnumStyleProp<"l" | "m" | "s" | "xl">;
|
||||
|
@ -47,9 +47,9 @@ export const arrowShapeProps: {
|
|||
isPrecise: boolean;
|
||||
} & {}>;
|
||||
point: T.ObjectValidator<{
|
||||
type: "point";
|
||||
x: number;
|
||||
y: number;
|
||||
type: "point";
|
||||
} & {}>;
|
||||
}, never>;
|
||||
end: T.UnionValidator<"type", {
|
||||
|
@ -61,9 +61,9 @@ export const arrowShapeProps: {
|
|||
isPrecise: boolean;
|
||||
} & {}>;
|
||||
point: T.ObjectValidator<{
|
||||
type: "point";
|
||||
x: number;
|
||||
y: number;
|
||||
type: "point";
|
||||
} & {}>;
|
||||
}, never>;
|
||||
bend: T.Validator<number>;
|
||||
|
@ -153,7 +153,7 @@ export function createTLSchema({ shapes, }?: {
|
|||
}): TLSchema;
|
||||
|
||||
// @public (undocumented)
|
||||
export const DefaultColorStyle: EnumStyleProp<"black" | "blue" | "green" | "grey" | "light-blue" | "light-green" | "light-red" | "light-violet" | "orange" | "red" | "violet" | "yellow">;
|
||||
export const DefaultColorStyle: EnumStyleProp<"black" | "blue" | "green" | "grey" | "light-blue" | "light-green" | "light-red" | "light-violet" | "orange" | "red" | "violet" | "white" | "yellow">;
|
||||
|
||||
// @public (undocumented)
|
||||
export const DefaultColorThemePalette: {
|
||||
|
@ -195,7 +195,7 @@ export const drawShapeMigrations: Migrations;
|
|||
|
||||
// @public (undocumented)
|
||||
export const drawShapeProps: {
|
||||
color: EnumStyleProp<"black" | "blue" | "green" | "grey" | "light-blue" | "light-green" | "light-red" | "light-violet" | "orange" | "red" | "violet" | "yellow">;
|
||||
color: EnumStyleProp<"black" | "blue" | "green" | "grey" | "light-blue" | "light-green" | "light-red" | "light-violet" | "orange" | "red" | "violet" | "white" | "yellow">;
|
||||
fill: EnumStyleProp<"none" | "pattern" | "semi" | "solid">;
|
||||
dash: EnumStyleProp<"dashed" | "dotted" | "draw" | "solid">;
|
||||
size: EnumStyleProp<"l" | "m" | "s" | "xl">;
|
||||
|
@ -482,8 +482,8 @@ export const geoShapeMigrations: Migrations;
|
|||
// @public (undocumented)
|
||||
export const geoShapeProps: {
|
||||
geo: EnumStyleProp<"arrow-down" | "arrow-left" | "arrow-right" | "arrow-up" | "check-box" | "cloud" | "diamond" | "ellipse" | "hexagon" | "octagon" | "oval" | "pentagon" | "rectangle" | "rhombus-2" | "rhombus" | "star" | "trapezoid" | "triangle" | "x-box">;
|
||||
labelColor: EnumStyleProp<"black" | "blue" | "green" | "grey" | "light-blue" | "light-green" | "light-red" | "light-violet" | "orange" | "red" | "violet" | "yellow">;
|
||||
color: EnumStyleProp<"black" | "blue" | "green" | "grey" | "light-blue" | "light-green" | "light-red" | "light-violet" | "orange" | "red" | "violet" | "yellow">;
|
||||
labelColor: EnumStyleProp<"black" | "blue" | "green" | "grey" | "light-blue" | "light-green" | "light-red" | "light-violet" | "orange" | "red" | "violet" | "white" | "yellow">;
|
||||
color: EnumStyleProp<"black" | "blue" | "green" | "grey" | "light-blue" | "light-green" | "light-red" | "light-violet" | "orange" | "red" | "violet" | "white" | "yellow">;
|
||||
fill: EnumStyleProp<"none" | "pattern" | "semi" | "solid">;
|
||||
dash: EnumStyleProp<"dashed" | "dotted" | "draw" | "solid">;
|
||||
size: EnumStyleProp<"l" | "m" | "s" | "xl">;
|
||||
|
@ -519,7 +519,7 @@ export const highlightShapeMigrations: Migrations;
|
|||
|
||||
// @public (undocumented)
|
||||
export const highlightShapeProps: {
|
||||
color: EnumStyleProp<"black" | "blue" | "green" | "grey" | "light-blue" | "light-green" | "light-red" | "light-violet" | "orange" | "red" | "violet" | "yellow">;
|
||||
color: EnumStyleProp<"black" | "blue" | "green" | "grey" | "light-blue" | "light-green" | "light-red" | "light-violet" | "orange" | "red" | "violet" | "white" | "yellow">;
|
||||
size: EnumStyleProp<"l" | "m" | "s" | "xl">;
|
||||
segments: T.ArrayOfValidator<{
|
||||
type: "free" | "straight";
|
||||
|
@ -683,7 +683,7 @@ export const lineShapeMigrations: Migrations;
|
|||
|
||||
// @public (undocumented)
|
||||
export const lineShapeProps: {
|
||||
color: EnumStyleProp<"black" | "blue" | "green" | "grey" | "light-blue" | "light-green" | "light-red" | "light-violet" | "orange" | "red" | "violet" | "yellow">;
|
||||
color: EnumStyleProp<"black" | "blue" | "green" | "grey" | "light-blue" | "light-green" | "light-red" | "light-violet" | "orange" | "red" | "violet" | "white" | "yellow">;
|
||||
dash: EnumStyleProp<"dashed" | "dotted" | "draw" | "solid">;
|
||||
size: EnumStyleProp<"l" | "m" | "s" | "xl">;
|
||||
spline: EnumStyleProp<"cubic" | "line">;
|
||||
|
@ -703,7 +703,7 @@ export const noteShapeMigrations: Migrations;
|
|||
|
||||
// @public (undocumented)
|
||||
export const noteShapeProps: {
|
||||
color: EnumStyleProp<"black" | "blue" | "green" | "grey" | "light-blue" | "light-green" | "light-red" | "light-violet" | "orange" | "red" | "violet" | "yellow">;
|
||||
color: EnumStyleProp<"black" | "blue" | "green" | "grey" | "light-blue" | "light-green" | "light-red" | "light-violet" | "orange" | "red" | "violet" | "white" | "yellow">;
|
||||
size: EnumStyleProp<"l" | "m" | "s" | "xl">;
|
||||
fontSizeAdjustment: T.Validator<number | undefined>;
|
||||
font: EnumStyleProp<"draw" | "mono" | "sans" | "serif">;
|
||||
|
@ -782,7 +782,7 @@ export const textShapeMigrations: Migrations;
|
|||
|
||||
// @public (undocumented)
|
||||
export const textShapeProps: {
|
||||
color: EnumStyleProp<"black" | "blue" | "green" | "grey" | "light-blue" | "light-green" | "light-red" | "light-violet" | "orange" | "red" | "violet" | "yellow">;
|
||||
color: EnumStyleProp<"black" | "blue" | "green" | "grey" | "light-blue" | "light-green" | "light-red" | "light-violet" | "orange" | "red" | "violet" | "white" | "yellow">;
|
||||
size: EnumStyleProp<"l" | "m" | "s" | "xl">;
|
||||
font: EnumStyleProp<"draw" | "mono" | "sans" | "serif">;
|
||||
align: EnumStyleProp<"end-legacy" | "end" | "middle-legacy" | "middle" | "start-legacy" | "start">;
|
||||
|
|
|
@ -256,7 +256,7 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<\"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"yellow\">;\n color: import(\"../styles/StyleProp\")."
|
||||
"text": "<\"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"white\" | \"yellow\">;\n color: import(\"../styles/StyleProp\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
|
@ -265,7 +265,7 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<\"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"yellow\">;\n fill: import(\"../styles/StyleProp\")."
|
||||
"text": "<\"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"white\" | \"yellow\">;\n fill: import(\"../styles/StyleProp\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
|
@ -364,7 +364,7 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<{\n x: number;\n y: number;\n type: \"point\";\n } & {}>;\n }, never>;\n end: "
|
||||
"text": "<{\n type: \"point\";\n x: number;\n y: number;\n } & {}>;\n }, never>;\n end: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
|
@ -409,7 +409,7 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<{\n x: number;\n y: number;\n type: \"point\";\n } & {}>;\n }, never>;\n bend: "
|
||||
"text": "<{\n type: \"point\";\n x: number;\n y: number;\n } & {}>;\n }, never>;\n bend: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
|
@ -1377,7 +1377,7 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<\"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"yellow\">"
|
||||
"text": "<\"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"white\" | \"yellow\">"
|
||||
}
|
||||
],
|
||||
"fileUrlPath": "packages/tlschema/src/styles/TLColorStyle.ts",
|
||||
|
@ -1706,7 +1706,7 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<\"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"yellow\">;\n fill: import(\"..\")."
|
||||
"text": "<\"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"white\" | \"yellow\">;\n fill: import(\"..\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
|
@ -2111,7 +2111,7 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<\"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"yellow\">;\n color: import(\"../styles/StyleProp\")."
|
||||
"text": "<\"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"white\" | \"yellow\">;\n color: import(\"../styles/StyleProp\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
|
@ -2120,7 +2120,7 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<\"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"yellow\">;\n fill: import(\"../styles/StyleProp\")."
|
||||
"text": "<\"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"white\" | \"yellow\">;\n fill: import(\"../styles/StyleProp\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
|
@ -2330,7 +2330,7 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<\"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"yellow\">;\n size: import(\"..\")."
|
||||
"text": "<\"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"white\" | \"yellow\">;\n size: import(\"..\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
|
@ -2782,7 +2782,7 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<\"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"yellow\">;\n dash: import(\"../styles/StyleProp\")."
|
||||
"text": "<\"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"white\" | \"yellow\">;\n dash: import(\"../styles/StyleProp\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
|
@ -2891,7 +2891,7 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<\"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"yellow\">;\n size: import(\"..\")."
|
||||
"text": "<\"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"white\" | \"yellow\">;\n size: import(\"..\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
|
@ -3782,7 +3782,7 @@
|
|||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<\"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"yellow\">;\n size: import(\"..\")."
|
||||
"text": "<\"black\" | \"blue\" | \"green\" | \"grey\" | \"light-blue\" | \"light-green\" | \"light-red\" | \"light-violet\" | \"orange\" | \"red\" | \"violet\" | \"white\" | \"yellow\">;\n size: import(\"..\")."
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
|
|
|
@ -15,6 +15,7 @@ const colors = [
|
|||
'light-green',
|
||||
'light-red',
|
||||
'red',
|
||||
'white',
|
||||
] as const
|
||||
|
||||
/** @public */
|
||||
|
@ -208,6 +209,19 @@ export const DefaultColorThemePalette: {
|
|||
p3: 'color(display-p3 0.972 0.8705 0.05)',
|
||||
},
|
||||
},
|
||||
white: {
|
||||
solid: '#FFFFFF',
|
||||
semi: '#f5f5f5',
|
||||
pattern: '#f9f9f9',
|
||||
note: {
|
||||
fill: '#FFFFFF',
|
||||
text: '#000000',
|
||||
},
|
||||
highlight: {
|
||||
srgb: '#ffffff',
|
||||
p3: 'color(display-p3 1 1 1)',
|
||||
},
|
||||
},
|
||||
},
|
||||
darkMode: {
|
||||
id: 'dark',
|
||||
|
@ -371,6 +385,19 @@ export const DefaultColorThemePalette: {
|
|||
p3: 'color(display-p3 0.8078 0.7225 0.0312)',
|
||||
},
|
||||
},
|
||||
white: {
|
||||
solid: '#FFFFFF',
|
||||
semi: '#f5f5f5',
|
||||
pattern: '#f9f9f9',
|
||||
note: {
|
||||
fill: '#FFFFFF',
|
||||
text: '#000000',
|
||||
},
|
||||
highlight: {
|
||||
srgb: '#ffffff',
|
||||
p3: 'color(display-p3 1 1 1)',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
"lint": "yarn run -T tsx ../../scripts/lint.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"fast-check": "^3.16.0",
|
||||
"tldraw": "workspace:*",
|
||||
"typescript": "^5.3.3",
|
||||
"uuid-by-string": "^4.0.0",
|
||||
|
|
|
@ -49,6 +49,43 @@ export type TLPersistentClientSocket<R extends UnknownRecord = UnknownRecord> =
|
|||
const PING_INTERVAL = 5000
|
||||
const MAX_TIME_TO_WAIT_FOR_SERVER_INTERACTION_BEFORE_RESETTING_CONNECTION = PING_INTERVAL * 2
|
||||
|
||||
export function _applyNetworkDiffToStore<R extends UnknownRecord, S extends Store<R> = Store<R>>(
|
||||
diff: NetworkDiff<R>,
|
||||
store: S
|
||||
): RecordsDiff<R> | null {
|
||||
const changes: RecordsDiff<R> = { added: {} as any, updated: {} as any, removed: {} as any }
|
||||
type k = keyof typeof changes.updated
|
||||
let hasChanges = false
|
||||
for (const [id, op] of objectMapEntries(diff)) {
|
||||
if (op[0] === RecordOpType.Put) {
|
||||
const existing = store.get(id as RecordId<any>)
|
||||
if (existing && !isEqual(existing, op[1])) {
|
||||
hasChanges = true
|
||||
changes.updated[id as k] = [existing, op[1]]
|
||||
} else {
|
||||
hasChanges = true
|
||||
changes.added[id as k] = op[1]
|
||||
}
|
||||
} else if (op[0] === RecordOpType.Patch) {
|
||||
const record = store.get(id as RecordId<any>)
|
||||
if (!record) {
|
||||
// the record was removed upstream
|
||||
continue
|
||||
}
|
||||
const patched = applyObjectDiff(record, op[1])
|
||||
hasChanges = true
|
||||
changes.updated[id as k] = [record, patched]
|
||||
} else if (op[0] === RecordOpType.Remove) {
|
||||
if (store.has(id as RecordId<any>)) {
|
||||
hasChanges = true
|
||||
changes.removed[id as k] = store.get(id as RecordId<any>)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hasChanges ? changes : null
|
||||
}
|
||||
|
||||
// Should connect support chunking the response to allow for large payloads?
|
||||
|
||||
/**
|
||||
|
@ -495,36 +532,8 @@ export class TLSyncClient<R extends UnknownRecord, S extends Store<R> = Store<R>
|
|||
*/
|
||||
private applyNetworkDiff(diff: NetworkDiff<R>, runCallbacks: boolean) {
|
||||
this.debug('applyNetworkDiff', diff)
|
||||
const changes: RecordsDiff<R> = { added: {} as any, updated: {} as any, removed: {} as any }
|
||||
type k = keyof typeof changes.updated
|
||||
let hasChanges = false
|
||||
for (const [id, op] of objectMapEntries(diff)) {
|
||||
if (op[0] === RecordOpType.Put) {
|
||||
const existing = this.store.get(id as RecordId<any>)
|
||||
if (existing && !isEqual(existing, op[1])) {
|
||||
hasChanges = true
|
||||
changes.updated[id as k] = [existing, op[1]]
|
||||
} else {
|
||||
hasChanges = true
|
||||
changes.added[id as k] = op[1]
|
||||
}
|
||||
} else if (op[0] === RecordOpType.Patch) {
|
||||
const record = this.store.get(id as RecordId<any>)
|
||||
if (!record) {
|
||||
// the record was removed upstream
|
||||
continue
|
||||
}
|
||||
const patched = applyObjectDiff(record, op[1])
|
||||
hasChanges = true
|
||||
changes.updated[id as k] = [record, patched]
|
||||
} else if (op[0] === RecordOpType.Remove) {
|
||||
if (this.store.has(id as RecordId<any>)) {
|
||||
hasChanges = true
|
||||
changes.removed[id as k] = this.store.get(id as RecordId<any>)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasChanges) {
|
||||
const changes = _applyNetworkDiffToStore(diff, this.store)
|
||||
if (changes !== null) {
|
||||
this.store.applyDiff(changes, runCallbacks)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ import {
|
|||
TLSocketServerSentDataEvent,
|
||||
TLSocketServerSentEvent,
|
||||
} from './protocol'
|
||||
import { squishDataEvents } from './squish'
|
||||
|
||||
/** @public */
|
||||
export type TLRoomSocket<R extends UnknownRecord> = {
|
||||
|
@ -456,7 +457,10 @@ export class TLSyncRoom<R extends UnknownRecord> {
|
|||
session.socket.sendMessage(message)
|
||||
}
|
||||
} else {
|
||||
session.socket.sendMessage({ type: 'data', data: session.outstandingDataMessages })
|
||||
session.socket.sendMessage({
|
||||
type: 'data',
|
||||
data: squishDataEvents(session.outstandingDataMessages),
|
||||
})
|
||||
}
|
||||
session.outstandingDataMessages.length = 0
|
||||
}
|
||||
|
|
|
@ -236,7 +236,11 @@ export function applyObjectDiff<T extends object>(object: T, objectDiff: ObjectD
|
|||
break
|
||||
}
|
||||
case ValueOpType.Patch: {
|
||||
if (object[key as keyof T] && typeof object[key as keyof T] === 'object') {
|
||||
if (
|
||||
object[key as keyof T] &&
|
||||
typeof object[key as keyof T] === 'object' &&
|
||||
!Array.isArray(object[key as keyof T])
|
||||
) {
|
||||
const diff = op[1]
|
||||
const patched = applyObjectDiff(object[key as keyof T] as object, diff)
|
||||
if (patched !== object[key as keyof T]) {
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
import { UnknownRecord } from '@tldraw/store'
|
||||
import { exhaustiveSwitchError, objectMapEntries, structuredClone } from '@tldraw/utils'
|
||||
import {
|
||||
NetworkDiff,
|
||||
ObjectDiff,
|
||||
RecordOp,
|
||||
RecordOpType,
|
||||
ValueOpType,
|
||||
applyObjectDiff,
|
||||
} from './diff'
|
||||
import { TLSocketServerSentDataEvent } from './protocol'
|
||||
|
||||
interface State<R extends UnknownRecord> {
|
||||
lastPatch: (TLSocketServerSentDataEvent<R> & { type: 'patch' }) | null
|
||||
squished: TLSocketServerSentDataEvent<R>[]
|
||||
}
|
||||
|
||||
type Bailed = boolean
|
||||
|
||||
function patchThePatch(lastPatch: ObjectDiff, newPatch: ObjectDiff): Bailed {
|
||||
for (const [newKey, newOp] of Object.entries(newPatch)) {
|
||||
switch (newOp[0]) {
|
||||
case ValueOpType.Put:
|
||||
lastPatch[newKey] = newOp
|
||||
break
|
||||
case ValueOpType.Append:
|
||||
if (lastPatch[newKey] === undefined) {
|
||||
lastPatch[newKey] = newOp
|
||||
} else {
|
||||
const lastOp = lastPatch[newKey]
|
||||
switch (lastOp[0]) {
|
||||
case ValueOpType.Put: {
|
||||
const lastValues = lastOp[1]
|
||||
if (Array.isArray(lastValues)) {
|
||||
const newValues = newOp[1]
|
||||
lastValues.push(...newValues)
|
||||
} else {
|
||||
// we're trying to append to something that was put previously, but
|
||||
// is not an array; bail out
|
||||
return true
|
||||
}
|
||||
break
|
||||
}
|
||||
case ValueOpType.Append: {
|
||||
const lastValues = lastOp[1]
|
||||
const lastOffset = lastOp[2]
|
||||
const newValues = newOp[1]
|
||||
const newOffset = newOp[2]
|
||||
if (newOffset === lastOffset + lastValues.length) {
|
||||
lastValues.push(...newValues)
|
||||
} else {
|
||||
// something weird is going on, bail out
|
||||
return true
|
||||
}
|
||||
break
|
||||
}
|
||||
default:
|
||||
// trying to append to either a deletion or a patch, bail out
|
||||
return true
|
||||
}
|
||||
}
|
||||
break
|
||||
case ValueOpType.Patch:
|
||||
if (lastPatch[newKey] === undefined) {
|
||||
lastPatch[newKey] = newOp
|
||||
} else {
|
||||
// bail out, recursive patching is too hard
|
||||
return true
|
||||
}
|
||||
break
|
||||
case ValueOpType.Delete:
|
||||
// overwrite whatever was there previously, no point if it's going to be removed
|
||||
// todo: check if it was freshly put and don't add if it wasn't?
|
||||
lastPatch[newKey] = newOp
|
||||
break
|
||||
default:
|
||||
exhaustiveSwitchError(newOp[0])
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
function patchTheOp<R extends UnknownRecord>(
|
||||
lastRecordOp: RecordOp<R>,
|
||||
newPatch: ObjectDiff
|
||||
): Bailed {
|
||||
switch (lastRecordOp[0]) {
|
||||
case RecordOpType.Put:
|
||||
// patching a freshly added value is easy, just patch as normal
|
||||
lastRecordOp[1] = applyObjectDiff(lastRecordOp[1], newPatch)
|
||||
break
|
||||
case RecordOpType.Patch: {
|
||||
// both are patches, merge them
|
||||
const bailed = patchThePatch(lastRecordOp[1], newPatch)
|
||||
if (bailed) {
|
||||
return true
|
||||
}
|
||||
break
|
||||
}
|
||||
case RecordOpType.Remove:
|
||||
// we're trying to patch an object that was removed, just disregard the update
|
||||
break
|
||||
default:
|
||||
exhaustiveSwitchError(lastRecordOp[0])
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
function squishInto<R extends UnknownRecord>(
|
||||
lastDiff: NetworkDiff<R>,
|
||||
newDiff: NetworkDiff<R>
|
||||
): Bailed {
|
||||
for (const [newId, newOp] of objectMapEntries(newDiff)) {
|
||||
switch (newOp[0]) {
|
||||
case RecordOpType.Put:
|
||||
// we Put the same record several times, just overwrite whatever came previously
|
||||
lastDiff[newId] = newOp
|
||||
break
|
||||
case RecordOpType.Patch:
|
||||
if (lastDiff[newId] === undefined) {
|
||||
// this is the patch now
|
||||
lastDiff[newId] = newOp
|
||||
} else {
|
||||
// patch the previous RecordOp!
|
||||
const bailed = patchTheOp(lastDiff[newId], newOp[1])
|
||||
if (bailed) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
break
|
||||
case RecordOpType.Remove:
|
||||
// overwrite whatever was there previously
|
||||
// todo: check if it was freshly put and don't add if it wasn't?
|
||||
lastDiff[newId] = newOp
|
||||
break
|
||||
default:
|
||||
exhaustiveSwitchError(newOp[0])
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
export function squishDataEvents<R extends UnknownRecord>(
|
||||
dataEvents: TLSocketServerSentDataEvent<R>[]
|
||||
): TLSocketServerSentDataEvent<R>[] {
|
||||
if (dataEvents.length < 2) {
|
||||
// most common case
|
||||
return dataEvents
|
||||
}
|
||||
|
||||
const state: State<R> = { lastPatch: null, squished: [] }
|
||||
|
||||
for (const e of dataEvents) {
|
||||
switch (e.type) {
|
||||
case 'push_result':
|
||||
if (state.lastPatch !== null) {
|
||||
state.squished.push(state.lastPatch)
|
||||
state.lastPatch = null
|
||||
}
|
||||
state.squished.push(e)
|
||||
break
|
||||
case 'patch':
|
||||
if (state.lastPatch !== null) {
|
||||
// this structuredClone is necessary to avoid modifying the original list of events
|
||||
// (otherwise objects can get reused on put and then modified on patch)
|
||||
const bailed = squishInto(state.lastPatch.diff, structuredClone(e.diff))
|
||||
if (bailed) {
|
||||
// this is unfortunate, but some patches were too hard to patch, give up
|
||||
// and return the original list
|
||||
return dataEvents
|
||||
}
|
||||
|
||||
state.lastPatch.serverClock = e.serverClock
|
||||
} else {
|
||||
state.lastPatch = structuredClone(e)
|
||||
}
|
||||
break
|
||||
default:
|
||||
exhaustiveSwitchError(e, 'type')
|
||||
}
|
||||
}
|
||||
|
||||
if (state.lastPatch !== null) {
|
||||
state.squished.push(state.lastPatch)
|
||||
}
|
||||
|
||||
return state.squished
|
||||
}
|
|
@ -0,0 +1,574 @@
|
|||
import { createRecordType, IdOf, RecordId, Store, StoreSchema, UnknownRecord } from '@tldraw/store'
|
||||
import { assert, structuredClone } from '@tldraw/utils'
|
||||
import fc, { Arbitrary } from 'fast-check'
|
||||
import { NetworkDiff, ObjectDiff, RecordOpType, ValueOpType } from '../lib/diff'
|
||||
import { TLSocketServerSentDataEvent } from '../lib/protocol'
|
||||
import { squishDataEvents } from '../lib/squish'
|
||||
import { _applyNetworkDiffToStore } from '../lib/TLSyncClient'
|
||||
|
||||
test('basic squishing', () => {
|
||||
const capture = [
|
||||
{
|
||||
type: 'patch',
|
||||
diff: {
|
||||
'instance_presence:nlyxdltolNVL0VONRr9Bz': [
|
||||
RecordOpType.Patch,
|
||||
{
|
||||
brush: [
|
||||
'put',
|
||||
{
|
||||
x: 610.02734375,
|
||||
y: 71.4609375,
|
||||
w: 929.58203125,
|
||||
h: 500.14453125,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
serverClock: 9237,
|
||||
},
|
||||
{
|
||||
type: 'patch',
|
||||
diff: {
|
||||
'instance_presence:nlyxdltolNVL0VONRr9Bz': [
|
||||
RecordOpType.Patch,
|
||||
{
|
||||
lastActivityTimestamp: ['put', 1710188679590],
|
||||
cursor: [
|
||||
'put',
|
||||
{
|
||||
x: 1526.07421875,
|
||||
y: 565.66796875,
|
||||
rotation: 0,
|
||||
type: 'default',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
serverClock: 9238,
|
||||
},
|
||||
{
|
||||
type: 'patch',
|
||||
diff: {
|
||||
'instance_presence:nlyxdltolNVL0VONRr9Bz': [
|
||||
RecordOpType.Patch,
|
||||
{
|
||||
brush: [
|
||||
'put',
|
||||
{
|
||||
x: 610.02734375,
|
||||
y: 71.4609375,
|
||||
w: 916.046875,
|
||||
h: 494.20703125,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
serverClock: 9239,
|
||||
},
|
||||
{
|
||||
type: 'patch',
|
||||
diff: {
|
||||
'instance_presence:nlyxdltolNVL0VONRr9Bz': [
|
||||
RecordOpType.Patch,
|
||||
{
|
||||
lastActivityTimestamp: ['put', 1710188679599],
|
||||
cursor: [
|
||||
'put',
|
||||
{
|
||||
x: 1519.26171875,
|
||||
y: 563.71875,
|
||||
rotation: 0,
|
||||
type: 'default',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
serverClock: 9240,
|
||||
},
|
||||
{
|
||||
type: 'patch',
|
||||
diff: {
|
||||
'instance_presence:nlyxdltolNVL0VONRr9Bz': [
|
||||
RecordOpType.Patch,
|
||||
{
|
||||
brush: [
|
||||
'put',
|
||||
{
|
||||
x: 610.02734375,
|
||||
y: 71.4609375,
|
||||
w: 909.234375,
|
||||
h: 492.2578125,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
serverClock: 9241,
|
||||
},
|
||||
{
|
||||
type: 'patch',
|
||||
diff: {
|
||||
'instance_presence:nlyxdltolNVL0VONRr9Bz': [
|
||||
RecordOpType.Patch,
|
||||
{
|
||||
lastActivityTimestamp: ['put', 1710188679608],
|
||||
cursor: [
|
||||
'put',
|
||||
{
|
||||
x: 1512.41015625,
|
||||
y: 562.23046875,
|
||||
rotation: 0,
|
||||
type: 'default',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
serverClock: 9242,
|
||||
},
|
||||
{
|
||||
type: 'patch',
|
||||
diff: {
|
||||
'instance_presence:nlyxdltolNVL0VONRr9Bz': [
|
||||
RecordOpType.Patch,
|
||||
{
|
||||
brush: [
|
||||
'put',
|
||||
{
|
||||
x: 610.02734375,
|
||||
y: 71.4609375,
|
||||
w: 902.3828125,
|
||||
h: 490.76953125,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
serverClock: 9243,
|
||||
},
|
||||
{
|
||||
type: 'patch',
|
||||
diff: {
|
||||
'instance_presence:nlyxdltolNVL0VONRr9Bz': [
|
||||
RecordOpType.Patch,
|
||||
{
|
||||
lastActivityTimestamp: ['put', 1710188679617],
|
||||
cursor: [
|
||||
'put',
|
||||
{
|
||||
x: 1506.71484375,
|
||||
y: 561.29296875,
|
||||
rotation: 0,
|
||||
type: 'default',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
serverClock: 9244,
|
||||
},
|
||||
{
|
||||
type: 'patch',
|
||||
diff: {
|
||||
'instance_presence:nlyxdltolNVL0VONRr9Bz': [
|
||||
RecordOpType.Patch,
|
||||
{
|
||||
brush: [
|
||||
'put',
|
||||
{
|
||||
x: 610.02734375,
|
||||
y: 71.4609375,
|
||||
w: 896.6875,
|
||||
h: 489.83203125,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
serverClock: 9245,
|
||||
},
|
||||
{
|
||||
type: 'patch',
|
||||
diff: {
|
||||
'instance_presence:nlyxdltolNVL0VONRr9Bz': [
|
||||
RecordOpType.Patch,
|
||||
{
|
||||
lastActivityTimestamp: ['put', 1710188679625],
|
||||
cursor: [
|
||||
'put',
|
||||
{
|
||||
x: 1501.734375,
|
||||
y: 560.88671875,
|
||||
rotation: 0,
|
||||
type: 'default',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
serverClock: 9246,
|
||||
},
|
||||
{
|
||||
type: 'patch',
|
||||
diff: {
|
||||
'instance_presence:nlyxdltolNVL0VONRr9Bz': [
|
||||
RecordOpType.Patch,
|
||||
{
|
||||
brush: [
|
||||
'put',
|
||||
{
|
||||
x: 610.02734375,
|
||||
y: 71.4609375,
|
||||
w: 891.70703125,
|
||||
h: 489.42578125,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
serverClock: 9247,
|
||||
},
|
||||
{
|
||||
type: 'patch',
|
||||
diff: {
|
||||
'instance_presence:nlyxdltolNVL0VONRr9Bz': [
|
||||
RecordOpType.Patch,
|
||||
{
|
||||
lastActivityTimestamp: ['put', 1710188679633],
|
||||
cursor: [
|
||||
'put',
|
||||
{
|
||||
x: 1497.22265625,
|
||||
y: 560.6875,
|
||||
rotation: 0,
|
||||
type: 'default',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
serverClock: 9248,
|
||||
},
|
||||
{
|
||||
type: 'patch',
|
||||
diff: {
|
||||
'instance_presence:nlyxdltolNVL0VONRr9Bz': [
|
||||
RecordOpType.Patch,
|
||||
{
|
||||
brush: [
|
||||
'put',
|
||||
{
|
||||
x: 610.02734375,
|
||||
y: 71.4609375,
|
||||
w: 887.1953125,
|
||||
h: 489.2265625,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
serverClock: 9249,
|
||||
},
|
||||
] as const satisfies TLSocketServerSentDataEvent<UnknownRecord>[]
|
||||
|
||||
const squished = squishDataEvents(capture)
|
||||
const manuallySquished = [
|
||||
{
|
||||
type: 'patch',
|
||||
diff: {
|
||||
'instance_presence:nlyxdltolNVL0VONRr9Bz': [
|
||||
'patch',
|
||||
{
|
||||
lastActivityTimestamp: ['put', 1710188679633],
|
||||
cursor: [
|
||||
'put',
|
||||
{
|
||||
x: 1497.22265625,
|
||||
y: 560.6875,
|
||||
rotation: 0,
|
||||
type: 'default',
|
||||
},
|
||||
],
|
||||
brush: [
|
||||
'put',
|
||||
{
|
||||
x: 610.02734375,
|
||||
y: 71.4609375,
|
||||
w: 887.1953125,
|
||||
h: 489.2265625,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
serverClock: 9249,
|
||||
},
|
||||
]
|
||||
|
||||
// see https://github.com/jestjs/jest/issues/14011 for why the second clone is needed
|
||||
expect(squished).toStrictEqual(structuredClone(manuallySquished))
|
||||
})
|
||||
|
||||
const TEST_RECORD_TYPENAME = 'testRecord' as const
|
||||
|
||||
interface TestRecord extends UnknownRecord {
|
||||
fieldA?: TestRecordValue
|
||||
fieldB?: TestRecordValue
|
||||
fieldC?: TestRecordValue
|
||||
}
|
||||
|
||||
type TestRecordValue =
|
||||
| string
|
||||
| number[]
|
||||
| { fieldA?: TestRecordValue; fieldB?: TestRecordValue; fieldC?: TestRecordValue }
|
||||
|
||||
const TestRecord = createRecordType<TestRecord>(TEST_RECORD_TYPENAME, {
|
||||
validator: {
|
||||
validate(value) {
|
||||
return value as TestRecord
|
||||
},
|
||||
},
|
||||
scope: 'document',
|
||||
})
|
||||
|
||||
class Model {
|
||||
diffs: NetworkDiff<TestRecord>[] = []
|
||||
idMap: IdOf<TestRecord>[]
|
||||
private readonly initialStoreData: Record<IdOf<TestRecord>, TestRecord>
|
||||
|
||||
constructor(public initialStoreContent: TestRecord[]) {
|
||||
this.idMap = initialStoreContent.map((r) => r.id)
|
||||
this.initialStoreData = Object.fromEntries(initialStoreContent.map((r) => [r.id, r]))
|
||||
}
|
||||
|
||||
trueIdx(idx: number) {
|
||||
return idx % this.idMap.length
|
||||
}
|
||||
|
||||
getId(idx: number) {
|
||||
return this.idMap[this.trueIdx(idx)]
|
||||
}
|
||||
|
||||
private getFreshStore(): Store<TestRecord> {
|
||||
return new Store({
|
||||
initialData: this.initialStoreData,
|
||||
schema: StoreSchema.create<TestRecord>({ testRecord: TestRecord }),
|
||||
props: {},
|
||||
})
|
||||
}
|
||||
|
||||
private getStoreWithDiffs(diffs: NetworkDiff<TestRecord>[]) {
|
||||
const store = this.getFreshStore()
|
||||
for (const diff of diffs) {
|
||||
const changes = _applyNetworkDiffToStore(diff, store)
|
||||
if (changes !== null) {
|
||||
store.applyDiff(changes, false)
|
||||
}
|
||||
}
|
||||
return store
|
||||
}
|
||||
|
||||
runTest() {
|
||||
const dataEvents = this.diffs.map((diff, idx) => ({
|
||||
type: 'patch' as const,
|
||||
diff,
|
||||
serverClock: idx,
|
||||
}))
|
||||
const squishedDiffs = squishDataEvents(dataEvents).map((e) => {
|
||||
assert(e.type === 'patch')
|
||||
return e.diff
|
||||
})
|
||||
|
||||
const baseStore = this.getStoreWithDiffs(this.diffs)
|
||||
const squishedStore = this.getStoreWithDiffs(squishedDiffs)
|
||||
|
||||
// see https://github.com/jestjs/jest/issues/14011 for the explanation for that structuredClone
|
||||
expect(squishedStore.serialize()).toEqual(structuredClone(baseStore.serialize()))
|
||||
}
|
||||
|
||||
// offsets are a MAJOR pain because they depend on the entire history of diffs so far, and
|
||||
// the store silently discards append patches if their offsets don't match, so they need
|
||||
// to be correct to exercise the squisher
|
||||
// NOTE: modifies the diff
|
||||
fixOffsets(recordId: IdOf<TestRecord>, fullDiff: ObjectDiff) {
|
||||
const fixed = structuredClone(fullDiff)
|
||||
|
||||
const store = this.getStoreWithDiffs(this.diffs)
|
||||
const record = store.get(recordId)
|
||||
if (record === undefined) {
|
||||
return fixed
|
||||
}
|
||||
|
||||
const fixer = (obj: any, diff: ObjectDiff) => {
|
||||
for (const [k, v] of Object.entries(diff)) {
|
||||
if (v[0] === ValueOpType.Append && Array.isArray(obj[k])) {
|
||||
v[2] = obj[k].length
|
||||
} else if (v[0] === ValueOpType.Patch && typeof obj[k] === 'object') {
|
||||
fixer(obj[k], v[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
fixer(record, fixed)
|
||||
|
||||
return fixed
|
||||
}
|
||||
}
|
||||
|
||||
type Real = 'whatever'
|
||||
|
||||
class RecordPut implements fc.Command<Model, Real> {
|
||||
constructor(readonly record: TestRecord) {}
|
||||
check(_m: Readonly<Model>) {
|
||||
return true
|
||||
}
|
||||
run(m: Model): void {
|
||||
m.diffs.push({ [this.record.id]: [RecordOpType.Put, this.record] })
|
||||
m.idMap.push(this.record.id)
|
||||
|
||||
m.runTest()
|
||||
}
|
||||
toString = () => `Put(${JSON.stringify(this.record)})`
|
||||
}
|
||||
|
||||
class RecordRemove implements fc.Command<Model, Real> {
|
||||
constructor(readonly idx: number) {}
|
||||
check(m: Readonly<Model>) {
|
||||
return m.idMap.length > 0
|
||||
}
|
||||
run(m: Model) {
|
||||
m.diffs.push({ [m.getId(this.idx)]: [RecordOpType.Remove] })
|
||||
m.idMap.splice(m.trueIdx(this.idx), 1)
|
||||
|
||||
m.runTest()
|
||||
}
|
||||
toString = () => `Remove(#${this.idx})`
|
||||
}
|
||||
|
||||
class RecordPatch implements fc.Command<Model, Real> {
|
||||
constructor(
|
||||
readonly idx: number,
|
||||
readonly patch: ObjectDiff
|
||||
) {}
|
||||
check(m: Readonly<Model>) {
|
||||
return m.idMap.length > 0
|
||||
}
|
||||
run(m: Model) {
|
||||
const fixedPatch = m.fixOffsets(m.getId(this.idx), this.patch)
|
||||
m.diffs.push({ [m.getId(this.idx)]: [RecordOpType.Patch, fixedPatch] })
|
||||
|
||||
m.runTest()
|
||||
}
|
||||
toString = () => `Patch(#${this.idx}, ${JSON.stringify(this.patch)})`
|
||||
}
|
||||
|
||||
const { TestRecordValueArb }: { TestRecordValueArb: Arbitrary<TestRecordValue> } = fc.letrec(
|
||||
(tie) => ({
|
||||
TestRecordValueArb: fc.oneof(
|
||||
fc.string(),
|
||||
fc.array(fc.integer()),
|
||||
fc.record(
|
||||
{
|
||||
fieldA: tie('TestRecordValueArb'),
|
||||
fieldB: tie('TestRecordValueArb'),
|
||||
fieldC: tie('TestRecordValueArb'),
|
||||
},
|
||||
{ requiredKeys: ['fieldA'] }
|
||||
)
|
||||
),
|
||||
})
|
||||
)
|
||||
|
||||
const TestRecordKeyArb = fc.oneof(
|
||||
fc.constant('fieldA' as const),
|
||||
fc.constant('fieldB' as const),
|
||||
fc.constant('fieldC' as const)
|
||||
)
|
||||
|
||||
const TestRecordArb = fc.record(
|
||||
{
|
||||
id: fc.oneof(fc.constant('idA'), fc.constant('idB'), fc.constant('idC')) as Arbitrary<
|
||||
RecordId<TestRecord>
|
||||
>,
|
||||
typeName: fc.constant(TEST_RECORD_TYPENAME),
|
||||
fieldA: TestRecordValueArb,
|
||||
fieldB: TestRecordValueArb,
|
||||
fieldC: TestRecordValueArb,
|
||||
},
|
||||
{ requiredKeys: ['id', 'typeName'] }
|
||||
)
|
||||
|
||||
const { ObjectDiffArb }: { ObjectDiffArb: Arbitrary<ObjectDiff> } = fc.letrec((tie) => ({
|
||||
ObjectDiffArb: fc.dictionary(
|
||||
TestRecordKeyArb,
|
||||
fc.oneof(
|
||||
fc.tuple(fc.constant(ValueOpType.Put), TestRecordValueArb),
|
||||
// The offset is -1 because it depends on the length of the array *in the current state*,
|
||||
// so it can't be generated here. Instead, it's patched up in the command
|
||||
fc.tuple(fc.constant(ValueOpType.Append), fc.array(fc.integer()), fc.constant(-1)),
|
||||
fc.tuple(fc.constant(ValueOpType.Patch), tie('ObjectDiffArb')),
|
||||
fc.tuple(fc.constant(ValueOpType.Delete))
|
||||
),
|
||||
{ minKeys: 1, maxKeys: 3 }
|
||||
),
|
||||
}))
|
||||
|
||||
const allCommands = [
|
||||
TestRecordArb.map((r) => new RecordPut(r)),
|
||||
fc.nat(10).map((idx) => new RecordRemove(idx)),
|
||||
fc.tuple(fc.nat(), ObjectDiffArb).map(([idx, diff]) => new RecordPatch(idx, diff)),
|
||||
]
|
||||
|
||||
const initialStoreContentArb: Arbitrary<TestRecord[]> = fc.uniqueArray(TestRecordArb, {
|
||||
selector: (r) => r.id,
|
||||
maxLength: 3,
|
||||
})
|
||||
|
||||
test('fast-checking squish', () => {
|
||||
// If you see this test failing, to reproduce you need both seed and path in fc.assert,
|
||||
// and replayPath in fc.commands. See the next test for an examples
|
||||
fc.assert(
|
||||
fc.property(
|
||||
initialStoreContentArb,
|
||||
fc.commands(allCommands, {}),
|
||||
(initialStoreContent, cmds) => {
|
||||
fc.modelRun(
|
||||
() => ({
|
||||
model: new Model(initialStoreContent),
|
||||
real: 'whatever',
|
||||
}),
|
||||
cmds
|
||||
)
|
||||
}
|
||||
),
|
||||
{
|
||||
verbose: 1,
|
||||
numRuns: 1_000,
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
test('problem: applying a patch to an array', () => {
|
||||
fc.assert(
|
||||
fc.property(
|
||||
initialStoreContentArb,
|
||||
fc.commands(allCommands, {
|
||||
replayPath: 'CDJ:F',
|
||||
}),
|
||||
(initialStoreContent, cmds) => {
|
||||
fc.modelRun(
|
||||
() => ({
|
||||
model: new Model(initialStoreContent),
|
||||
real: 'whatever',
|
||||
}),
|
||||
cmds
|
||||
)
|
||||
}
|
||||
),
|
||||
{ seed: -1883357795, path: '7653:1:2:2:4:3:3:3:3', endOnFailure: true }
|
||||
)
|
||||
})
|
|
@ -0,0 +1,62 @@
|
|||
// For all package.jsons found in the monorepo, generate a license report
|
||||
// by running the `license-report --output=html` script in each package.
|
||||
|
||||
import { execPromise } from '@auto-it/core'
|
||||
import { execSync } from 'child_process'
|
||||
import { writeFileSync } from 'fs'
|
||||
|
||||
// Use `yarn workspace list` to get all the packages in the monorepo
|
||||
async function main() {
|
||||
const devOnly = process.argv.includes('--dev')
|
||||
const prodOnly = process.argv.includes('--prod')
|
||||
|
||||
const htmlTables: { title: string; content: string }[] = []
|
||||
|
||||
const workspaceList = execSync('yarn workspaces list', {
|
||||
encoding: 'utf-8',
|
||||
})
|
||||
const lines = workspaceList.split('\n')
|
||||
lines.pop() // remove // Done
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const location = lines[i].split(': ')[1]
|
||||
try {
|
||||
console.log('running license-report in', location)
|
||||
const report = await execPromise(
|
||||
`yarn license-report --package=${location}/package.json --department.value=tldraw --relatedTo.label=Package --relatedTo.value=${location} --output=html --only=${devOnly ? 'dev' : prodOnly ? 'prod' : 'dev,prod,peer,opt'}`
|
||||
)
|
||||
// Extract the <table> contents from the report
|
||||
const table = report.match(/<tbody>.*<\/tbody>/gs)
|
||||
if (!table) {
|
||||
console.error('Error extracting table from license-report result.')
|
||||
process.exit(1)
|
||||
}
|
||||
htmlTables.push({ title: location, content: table[0] })
|
||||
} catch (e) {
|
||||
console.error(`Error running license-report in ${location}, ${e}`)
|
||||
}
|
||||
}
|
||||
|
||||
const html = `
|
||||
<html>
|
||||
<body>
|
||||
<table><thead><tr><th class="string">department</th><th class="string">related to</th><th class="string">name</th><th class="string">license period</th><th class="string">material / not material</th><th class="string">license type</th><th class="string">link</th><th class="string">remote version</th><th class="string">installed version</th><th class="string">defined version</th><th class="string">author</th></tr></thead><tbody>
|
||||
${htmlTables.reduce((acc, { content }) => {
|
||||
acc += content + '<tr></tr>'
|
||||
return acc
|
||||
}, '')}
|
||||
</tbody></table>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
writeFileSync(
|
||||
prodOnly
|
||||
? 'license-report-prod.html'
|
||||
: devOnly
|
||||
? 'license-report-dev.html'
|
||||
: 'license-report.html',
|
||||
html
|
||||
)
|
||||
}
|
||||
|
||||
main()
|
240
yarn.lock
240
yarn.lock
|
@ -3898,6 +3898,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@kessler/tableify@npm:^1.0.2":
|
||||
version: 1.0.2
|
||||
resolution: "@kessler/tableify@npm:1.0.2"
|
||||
checksum: 705d5218559bcbcde2a3a7908f42f0738f92a4d703d687e4a1f716fd9215e8bee5a326d8181b9f3e783fb3713a7bee65bdbe3cf0ef339cb3e51ec5fa6a2bf467
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@lezer/common@npm:^1.0.0, @lezer/common@npm:^1.0.2, @lezer/common@npm:^1.1.0, @lezer/common@npm:^1.2.0":
|
||||
version: 1.2.1
|
||||
resolution: "@lezer/common@npm:1.2.1"
|
||||
|
@ -6255,6 +6262,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@sindresorhus/is@npm:^5.2.0":
|
||||
version: 5.6.0
|
||||
resolution: "@sindresorhus/is@npm:5.6.0"
|
||||
checksum: b077c325acec98e30f7d86df158aaba2e7af2acb9bb6a00fda4b91578539fbff4ecebe9b934e24fec0e6950de3089d89d79ec02d9062476b20ce185be0e01bd6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@sindresorhus/slugify@npm:^2.2.0":
|
||||
version: 2.2.1
|
||||
resolution: "@sindresorhus/slugify@npm:2.2.1"
|
||||
|
@ -7125,6 +7139,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@szmarczak/http-timer@npm:^5.0.1":
|
||||
version: 5.0.1
|
||||
resolution: "@szmarczak/http-timer@npm:5.0.1"
|
||||
dependencies:
|
||||
defer-to-connect: "npm:^2.0.1"
|
||||
checksum: fc9cb993e808806692e4a3337c90ece0ec00c89f4b67e3652a356b89730da98bc824273a6d67ca84d5f33cd85f317dcd5ce39d8cc0a2f060145a608a7cb8ce92
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@testing-library/dom@npm:^9.0.0":
|
||||
version: 9.3.4
|
||||
resolution: "@testing-library/dom@npm:9.3.4"
|
||||
|
@ -7346,6 +7369,7 @@ __metadata:
|
|||
jest: "npm:30.0.0-alpha.2"
|
||||
json5: "npm:^2.2.3"
|
||||
lazyrepo: "npm:0.0.0-alpha.27"
|
||||
license-report: "npm:^6.5.0"
|
||||
lint-staged: "npm:>=10"
|
||||
prettier: "npm:^3.0.3"
|
||||
prettier-plugin-organize-imports: "npm:^3.2.3"
|
||||
|
@ -7463,6 +7487,7 @@ __metadata:
|
|||
"@tldraw/store": "workspace:*"
|
||||
"@tldraw/tlschema": "workspace:*"
|
||||
"@tldraw/utils": "workspace:*"
|
||||
fast-check: "npm:^3.16.0"
|
||||
lodash.isequal: "npm:^4.5.0"
|
||||
nanoevents: "npm:^7.0.1"
|
||||
nanoid: "npm:4.0.2"
|
||||
|
@ -7853,7 +7878,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/http-cache-semantics@npm:*":
|
||||
"@types/http-cache-semantics@npm:*, @types/http-cache-semantics@npm:^4.0.2":
|
||||
version: 4.0.4
|
||||
resolution: "@types/http-cache-semantics@npm:4.0.4"
|
||||
checksum: a59566cff646025a5de396d6b3f44a39ab6a74f2ed8150692e0f31cc52f3661a68b04afe3166ebe0d566bd3259cb18522f46e949576d5204781cd6452b7fe0c5
|
||||
|
@ -10223,6 +10248,28 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cacheable-lookup@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "cacheable-lookup@npm:7.0.0"
|
||||
checksum: 69ea78cd9f16ad38120372e71ba98b64acecd95bbcbcdad811f857dc192bad81ace021f8def012ce19178583db8d46afd1a00b3e8c88527e978e049edbc23252
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cacheable-request@npm:^10.2.8":
|
||||
version: 10.2.14
|
||||
resolution: "cacheable-request@npm:10.2.14"
|
||||
dependencies:
|
||||
"@types/http-cache-semantics": "npm:^4.0.2"
|
||||
get-stream: "npm:^6.0.1"
|
||||
http-cache-semantics: "npm:^4.1.1"
|
||||
keyv: "npm:^4.5.3"
|
||||
mimic-response: "npm:^4.0.0"
|
||||
normalize-url: "npm:^8.0.0"
|
||||
responselike: "npm:^3.0.0"
|
||||
checksum: 102f454ac68eb66f99a709c5cf65e90ed89f1b9269752578d5a08590b3986c3ea47a5d9dff208fe7b65855a29da129a2f23321b88490106898e0ba70b807c912
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cacheable-request@npm:^7.0.2":
|
||||
version: 7.0.4
|
||||
resolution: "cacheable-request@npm:7.0.4"
|
||||
|
@ -11365,7 +11412,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"defer-to-connect@npm:^2.0.0":
|
||||
"defer-to-connect@npm:^2.0.0, defer-to-connect@npm:^2.0.1":
|
||||
version: 2.0.1
|
||||
resolution: "defer-to-connect@npm:2.0.1"
|
||||
checksum: 8a9b50d2f25446c0bfefb55a48e90afd58f85b21bcf78e9207cd7b804354f6409032a1705c2491686e202e64fc05f147aa5aa45f9aa82627563f045937f5791b
|
||||
|
@ -11938,6 +11985,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eol@npm:^0.9.1":
|
||||
version: 0.9.1
|
||||
resolution: "eol@npm:0.9.1"
|
||||
checksum: 9d3fd93bb2bb5c69c7fe8dfb97b62213ed95857a2e90f5db3110415993e8a989d87fb011755ce22fdb92ca36fbe4e111b395a6f4ce00b9b51d3f00f19c2acf52
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"err-code@npm:^2.0.2":
|
||||
version: 2.0.3
|
||||
resolution: "err-code@npm:2.0.3"
|
||||
|
@ -13499,6 +13553,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fast-check@npm:^3.16.0":
|
||||
version: 3.16.0
|
||||
resolution: "fast-check@npm:3.16.0"
|
||||
dependencies:
|
||||
pure-rand: "npm:^6.0.0"
|
||||
checksum: 4a14945b885ef2d75c3252a067a4cfa2440a2c0da18341d514be3803fafb616b0ec68806071f29e1267a85c7a9e4a5e192ae5e592727d8d2e66389f946be472c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3":
|
||||
version: 3.1.3
|
||||
resolution: "fast-deep-equal@npm:3.1.3"
|
||||
|
@ -13791,6 +13854,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"form-data-encoder@npm:^2.1.2":
|
||||
version: 2.1.4
|
||||
resolution: "form-data-encoder@npm:2.1.4"
|
||||
checksum: 3778e7db3c21457296e6fdbc4200642a6c01e8be9297256e845ee275f9ddaecb5f49bfb0364690ad216898c114ec59bf85f01ec823a70670b8067273415d62f6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"form-data@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "form-data@npm:4.0.0"
|
||||
|
@ -14159,6 +14229,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-stdin@npm:^5.0.1":
|
||||
version: 5.0.1
|
||||
resolution: "get-stdin@npm:5.0.1"
|
||||
checksum: f9784638ad3e68a0a8bdc031aedf0fca749843cd134956fbd4f3bbac17c359e0fb9210343fcbed72ee79fb19d8e4c49b7a6e742cc5d44e94ac1405e9371d4b3e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-stream@npm:^5.1.0":
|
||||
version: 5.2.0
|
||||
resolution: "get-stream@npm:5.2.0"
|
||||
|
@ -14168,7 +14245,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-stream@npm:^6.0.0":
|
||||
"get-stream@npm:^6.0.0, get-stream@npm:^6.0.1":
|
||||
version: 6.0.1
|
||||
resolution: "get-stream@npm:6.0.1"
|
||||
checksum: 781266d29725f35c59f1d214aedc92b0ae855800a980800e2923b3fbc4e56b3cb6e462c42e09a1cf1a00c64e056a78fa407cbe06c7c92b7e5cd49b4b85c2a497
|
||||
|
@ -14466,6 +14543,25 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"got@npm:^13.0.0":
|
||||
version: 13.0.0
|
||||
resolution: "got@npm:13.0.0"
|
||||
dependencies:
|
||||
"@sindresorhus/is": "npm:^5.2.0"
|
||||
"@szmarczak/http-timer": "npm:^5.0.1"
|
||||
cacheable-lookup: "npm:^7.0.0"
|
||||
cacheable-request: "npm:^10.2.8"
|
||||
decompress-response: "npm:^6.0.0"
|
||||
form-data-encoder: "npm:^2.1.2"
|
||||
get-stream: "npm:^6.0.1"
|
||||
http2-wrapper: "npm:^2.1.10"
|
||||
lowercase-keys: "npm:^3.0.0"
|
||||
p-cancelable: "npm:^3.0.0"
|
||||
responselike: "npm:^3.0.0"
|
||||
checksum: 35ac9fe37daca3d0a4f90305d8e64626268ef5a42584f5bcb42eea3cb9bbeb691cf9041d5ea72133a7295d1291684789a3148ff89a95f3d3ce3d0ebb6fb2f680
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"gpt-3-encoder@npm:1.1.4":
|
||||
version: 1.1.4
|
||||
resolution: "gpt-3-encoder@npm:1.1.4"
|
||||
|
@ -14999,6 +15095,16 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"http2-wrapper@npm:^2.1.10":
|
||||
version: 2.2.1
|
||||
resolution: "http2-wrapper@npm:2.2.1"
|
||||
dependencies:
|
||||
quick-lru: "npm:^5.1.1"
|
||||
resolve-alpn: "npm:^1.2.0"
|
||||
checksum: e7a5ac6548318e83fc0399cd832cdff6bbf902b165d211cad47a56ee732922e0aa1107246dd884b12532a1c4649d27c4d44f2480911c65202e93c90bde8fa29d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"https-proxy-agent@npm:5, https-proxy-agent@npm:^5.0.0, https-proxy-agent@npm:^5.0.1":
|
||||
version: 5.0.1
|
||||
resolution: "https-proxy-agent@npm:5.0.1"
|
||||
|
@ -16979,6 +17085,25 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"license-report@npm:^6.5.0":
|
||||
version: 6.5.0
|
||||
resolution: "license-report@npm:6.5.0"
|
||||
dependencies:
|
||||
"@kessler/tableify": "npm:^1.0.2"
|
||||
debug: "npm:^4.3.4"
|
||||
eol: "npm:^0.9.1"
|
||||
got: "npm:^13.0.0"
|
||||
rc: "npm:^1.2.8"
|
||||
semver: "npm:^7.5.4"
|
||||
tablemark: "npm:^3.0.0"
|
||||
text-table: "npm:^0.2.0"
|
||||
visit-values: "npm:^2.0.0"
|
||||
bin:
|
||||
license-report: index.js
|
||||
checksum: 6475e00363924d2fac5712f9866272cfb930834ad4469bc4454fc108a24212a51c2dd49911c61000fef2a848527d3eb37a6b380e4b22865c12d9b4c3ade95d11
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lie@npm:3.1.1":
|
||||
version: 3.1.1
|
||||
resolution: "lie@npm:3.1.1"
|
||||
|
@ -17309,6 +17434,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lower-case@npm:^2.0.2":
|
||||
version: 2.0.2
|
||||
resolution: "lower-case@npm:2.0.2"
|
||||
dependencies:
|
||||
tslib: "npm:^2.0.3"
|
||||
checksum: 83a0a5f159ad7614bee8bf976b96275f3954335a84fad2696927f609ddae902802c4f3312d86668722e668bef41400254807e1d3a7f2e8c3eede79691aa1f010
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lowercase-keys@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "lowercase-keys@npm:2.0.0"
|
||||
|
@ -17316,6 +17450,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lowercase-keys@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "lowercase-keys@npm:3.0.0"
|
||||
checksum: 67a3f81409af969bc0c4ca0e76cd7d16adb1e25aa1c197229587eaf8671275c8c067cd421795dbca4c81be0098e4c426a086a05e30de8a9c587b7a13c0c7ccc5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lowlight@npm:^3.0.0":
|
||||
version: 3.1.0
|
||||
resolution: "lowlight@npm:3.1.0"
|
||||
|
@ -18542,6 +18683,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mimic-response@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "mimic-response@npm:4.0.0"
|
||||
checksum: 33b804cc961efe206efdb1fca6a22540decdcfce6c14eb5c0c50e5ae9022267ab22ce8f5568b1f7247ba67500fe20d523d81e0e9f009b321ccd9d472e78d1850
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"min-indent@npm:^1.0.0":
|
||||
version: 1.0.1
|
||||
resolution: "min-indent@npm:1.0.1"
|
||||
|
@ -19039,6 +19187,16 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"no-case@npm:^3.0.4":
|
||||
version: 3.0.4
|
||||
resolution: "no-case@npm:3.0.4"
|
||||
dependencies:
|
||||
lower-case: "npm:^2.0.2"
|
||||
tslib: "npm:^2.0.3"
|
||||
checksum: 0b2ebc113dfcf737d48dde49cfebf3ad2d82a8c3188e7100c6f375e30eafbef9e9124aadc3becef237b042fd5eb0aad2fd78669c20972d045bbe7fea8ba0be5c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-abi@npm:^3.3.0":
|
||||
version: 3.54.0
|
||||
resolution: "node-abi@npm:3.54.0"
|
||||
|
@ -19218,6 +19376,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"normalize-url@npm:^8.0.0":
|
||||
version: 8.0.1
|
||||
resolution: "normalize-url@npm:8.0.1"
|
||||
checksum: ae392037584fc5935b663ae4af475351930a1fc39e107956cfac44f42d5127eec2d77d9b7b12ded4696ca78103bafac5b6206a0ea8673c7bffecbe13544fcc5a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"npm-run-path@npm:^4.0.1":
|
||||
version: 4.0.1
|
||||
resolution: "npm-run-path@npm:4.0.1"
|
||||
|
@ -19575,6 +19740,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"p-cancelable@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "p-cancelable@npm:3.0.0"
|
||||
checksum: a5eab7cf5ac5de83222a014eccdbfde65ecfb22005ee9bc242041f0b4441e07fac7629432c82f48868aa0f8413fe0df6c6067c16f76bf9217cd8dc651923c93d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"p-limit@npm:^1.1.0":
|
||||
version: 1.3.0
|
||||
resolution: "p-limit@npm:1.3.0"
|
||||
|
@ -21397,7 +21569,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"resolve-alpn@npm:^1.0.0":
|
||||
"resolve-alpn@npm:^1.0.0, resolve-alpn@npm:^1.2.0":
|
||||
version: 1.2.1
|
||||
resolution: "resolve-alpn@npm:1.2.1"
|
||||
checksum: 744e87888f0b6fa0b256ab454ca0b9c0b80808715e2ef1f3672773665c92a941f6181194e30ccae4a8cd0adbe0d955d3f133102636d2ee0cca0119fec0bc9aec
|
||||
|
@ -21540,6 +21712,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"responselike@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "responselike@npm:3.0.0"
|
||||
dependencies:
|
||||
lowercase-keys: "npm:^3.0.0"
|
||||
checksum: e0cc9be30df4f415d6d83cdede3c5c887cd4a73e7cc1708bcaab1d50a28d15acb68460ac5b02bcc55a42f3d493729c8856427dcf6e57e6e128ad05cba4cfb95e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"restore-cursor@npm:^3.1.0":
|
||||
version: 3.1.0
|
||||
resolution: "restore-cursor@npm:3.1.0"
|
||||
|
@ -21926,6 +22107,17 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"sentence-case@npm:^3.0.4":
|
||||
version: 3.0.4
|
||||
resolution: "sentence-case@npm:3.0.4"
|
||||
dependencies:
|
||||
no-case: "npm:^3.0.4"
|
||||
tslib: "npm:^2.0.3"
|
||||
upper-case-first: "npm:^2.0.2"
|
||||
checksum: 3cfe6c0143e649132365695706702d7f729f484fa7b25f43435876efe7af2478243eefb052bacbcce10babf9319fd6b5b6bc59b94c80a1c819bcbb40651465d5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"serialize-javascript@npm:6.0.0":
|
||||
version: 6.0.0
|
||||
resolution: "serialize-javascript@npm:6.0.0"
|
||||
|
@ -22299,6 +22491,18 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"split-text-to-chunks@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "split-text-to-chunks@npm:1.0.0"
|
||||
dependencies:
|
||||
get-stdin: "npm:^5.0.1"
|
||||
minimist: "npm:^1.2.0"
|
||||
bin:
|
||||
wordwrap: cli.js
|
||||
checksum: 944a2defff4eccf193216177b284bd8d3da0d83391827d89efa4250d4c0a0729e63fecfc0cf56fa2c50acba2b8017753b332b13d0aebdb9fb5c1c2164a943ce5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"sprintf-js@npm:~1.0.2":
|
||||
version: 1.0.3
|
||||
resolution: "sprintf-js@npm:1.0.3"
|
||||
|
@ -22876,6 +23080,16 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tablemark@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "tablemark@npm:3.0.0"
|
||||
dependencies:
|
||||
sentence-case: "npm:^3.0.4"
|
||||
split-text-to-chunks: "npm:^1.0.0"
|
||||
checksum: 1e819d7e1ad268743543778b5a1aec559113bf91098e220997fddccd207fd779d8ab216ff86d2180bb780841a23ef3f84f59bbc493d41cd5badff1246a04a8de
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tapable@npm:^2.1.1, tapable@npm:^2.2.0":
|
||||
version: 2.2.1
|
||||
resolution: "tapable@npm:2.2.1"
|
||||
|
@ -23461,7 +23675,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tslib@npm:^2, tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.1.0, tslib@npm:^2.2.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.6.1, tslib@npm:^2.6.2":
|
||||
"tslib@npm:^2, tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.2.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.6.1, tslib@npm:^2.6.2":
|
||||
version: 2.6.2
|
||||
resolution: "tslib@npm:2.6.2"
|
||||
checksum: bd26c22d36736513980091a1e356378e8b662ded04204453d353a7f34a4c21ed0afc59b5f90719d4ba756e581a162ecbf93118dc9c6be5acf70aa309188166ca
|
||||
|
@ -24080,6 +24294,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"upper-case-first@npm:^2.0.2":
|
||||
version: 2.0.2
|
||||
resolution: "upper-case-first@npm:2.0.2"
|
||||
dependencies:
|
||||
tslib: "npm:^2.0.3"
|
||||
checksum: 4487db4701effe3b54ced4b3e4aa4d9ab06c548f97244d04aafb642eedf96a76d5a03cf5f38f10f415531d5792d1ac6e1b50f2a76984dc6964ad530f12876409
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"uri-js@npm:^4.2.2":
|
||||
version: 4.4.1
|
||||
resolution: "uri-js@npm:4.4.1"
|
||||
|
@ -24418,6 +24641,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"visit-values@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "visit-values@npm:2.0.0"
|
||||
checksum: 9422c453864c363a93421f94342ce7c53fbd3e52b7dcd1f4df63c45e2ac1a71332047f1153fbdad2d9544867fa835f4029e2dca9415ee58437513fdb64895618
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"vite-node@npm:^0.28.5":
|
||||
version: 0.28.5
|
||||
resolution: "vite-node@npm:0.28.5"
|
||||
|
|
Ładowanie…
Reference in New Issue