From 625f4abc3b90a4873e2e7b4038b6299a2b0d8722 Mon Sep 17 00:00:00 2001 From: David Sheldrick Date: Wed, 17 Apr 2024 20:38:31 +0100 Subject: [PATCH] [fix] allow loading files (#3517) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I messed up the schema validator for loading files. ### Change Type - [x] `sdk` — Changes the tldraw SDK - [ ] `dotcom` — Changes the tldraw.com web app - [ ] `docs` — Changes to the documentation, examples, or templates. - [ ] `vs code` — Changes to the vscode plugin - [ ] `internal` — Does not affect user-facing stuff - [x] `bugfix` — Bug fix - [ ] `feature` — New feature - [ ] `improvement` — Improving existing features - [ ] `chore` — Updating dependencies, other boring stuff - [ ] `galaxy brain` — Architectural changes - [ ] `tests` — Changes to any test code - [ ] `tools` — Changes to infrastructure, CI, internal scripts, debugging tools, etc. - [ ] `dunno` — I don't know --- packages/tldraw/src/lib/utils/tldr/file.ts | 2 +- packages/validate/api-report.md | 6 ++- packages/validate/api/api.json | 32 +++++++++++++ packages/validate/src/lib/validation.ts | 54 +++++++++++++++++----- 4 files changed, 80 insertions(+), 14 deletions(-) diff --git a/packages/tldraw/src/lib/utils/tldr/file.ts b/packages/tldraw/src/lib/utils/tldr/file.ts index bee4c2ac2..8302d53df 100644 --- a/packages/tldraw/src/lib/utils/tldr/file.ts +++ b/packages/tldraw/src/lib/utils/tldr/file.ts @@ -62,7 +62,7 @@ const schemaV2 = T.object({ const tldrawFileValidator: T.Validator = T.object({ tldrawFileFormatVersion: T.nonZeroInteger, - schema: T.union('schemaVersion', { + schema: T.numberUnion('schemaVersion', { 1: schemaV1, 2: schemaV2, }), diff --git a/packages/validate/api-report.md b/packages/validate/api-report.md index 584047196..dbf57dcbb 100644 --- a/packages/validate/api-report.md +++ b/packages/validate/api-report.md @@ -83,6 +83,9 @@ function nullable(validator: Validatable): Validator; // @public const number: Validator; +// @internal (undocumented) +function numberUnion>(key: Key, config: Config): UnionValidator; + // @public function object(config: { readonly [K in keyof Shape]: Validatable; @@ -134,6 +137,7 @@ declare namespace T { jsonDict, dict, union, + numberUnion, model, setEnum, optional, @@ -178,7 +182,7 @@ function union, UnknownValue = never> extends Validator | UnknownValue> { - constructor(key: Key, config: Config, unknownValueValidation: (value: object, variant: string) => UnknownValue); + constructor(key: Key, config: Config, unknownValueValidation: (value: object, variant: string) => UnknownValue, useNumberKeys: boolean); // (undocumented) validateUnknownVariants(unknownValueValidation: (value: object, variant: string) => Unknown): UnionValidator; } diff --git a/packages/validate/api/api.json b/packages/validate/api/api.json index 1bdd27588..0aedb8cb6 100644 --- a/packages/validate/api/api.json +++ b/packages/validate/api/api.json @@ -3027,6 +3027,14 @@ "kind": "Content", "text": "(value: object, variant: string) => UnknownValue" }, + { + "kind": "Content", + "text": ", useNumberKeys: " + }, + { + "kind": "Content", + "text": "boolean" + }, { "kind": "Content", "text": ");" @@ -3059,6 +3067,14 @@ "endIndex": 6 }, "isOptional": false + }, + { + "parameterName": "useNumberKeys", + "parameterTypeTokenRange": { + "startIndex": 7, + "endIndex": 8 + }, + "isOptional": false } ] }, @@ -4260,6 +4276,14 @@ "kind": "Content", "text": "(value: object, variant: string) => UnknownValue" }, + { + "kind": "Content", + "text": ", useNumberKeys: " + }, + { + "kind": "Content", + "text": "boolean" + }, { "kind": "Content", "text": ");" @@ -4292,6 +4316,14 @@ "endIndex": 6 }, "isOptional": false + }, + { + "parameterName": "useNumberKeys", + "parameterTypeTokenRange": { + "startIndex": 7, + "endIndex": 8 + }, + "isOptional": false } ] }, diff --git a/packages/validate/src/lib/validation.ts b/packages/validate/src/lib/validation.ts index b9d4d21f3..145746437 100644 --- a/packages/validate/src/lib/validation.ts +++ b/packages/validate/src/lib/validation.ts @@ -394,7 +394,8 @@ export class UnionValidator< constructor( private readonly key: Key, private readonly config: Config, - private readonly unknownValueValidation: (value: object, variant: string) => UnknownValue + private readonly unknownValueValidation: (value: object, variant: string) => UnknownValue, + private readonly useNumberKeys: boolean ) { super( (input) => { @@ -442,11 +443,13 @@ export class UnionValidator< matchingSchema: Validatable | undefined variant: string } { - const variant = getOwnProperty(object, this.key) as keyof Config | undefined - if (typeof variant !== 'string') { + const variant = getOwnProperty(object, this.key) as string & keyof Config + if (!this.useNumberKeys && typeof variant !== 'string') { throw new ValidationError( `Expected a string for key "${this.key}", got ${typeToString(variant)}` ) + } else if (this.useNumberKeys && !Number.isFinite(Number(variant))) { + throw new ValidationError(`Expected a number for key "${this.key}", got "${variant as any}"`) } const matchingSchema = hasOwnProperty(this.config, variant) ? this.config[variant] : undefined @@ -456,7 +459,7 @@ export class UnionValidator< validateUnknownVariants( unknownValueValidation: (value: object, variant: string) => Unknown ): UnionValidator { - return new UnionValidator(this.key, this.config, unknownValueValidation) + return new UnionValidator(this.key, this.config, unknownValueValidation, this.useNumberKeys) } } @@ -829,14 +832,41 @@ export function union { - return new UnionValidator(key, config, (unknownValue, unknownVariant) => { - throw new ValidationError( - `Expected one of ${Object.keys(config) - .map((key) => JSON.stringify(key)) - .join(' or ')}, got ${JSON.stringify(unknownVariant)}`, - [key] - ) - }) + return new UnionValidator( + key, + config, + (unknownValue, unknownVariant) => { + throw new ValidationError( + `Expected one of ${Object.keys(config) + .map((key) => JSON.stringify(key)) + .join(' or ')}, got ${JSON.stringify(unknownVariant)}`, + [key] + ) + }, + false + ) +} + +/** + * @internal + */ +export function numberUnion>( + key: Key, + config: Config +): UnionValidator { + return new UnionValidator( + key, + config, + (unknownValue, unknownVariant) => { + throw new ValidationError( + `Expected one of ${Object.keys(config) + .map((key) => JSON.stringify(key)) + .join(' or ')}, got ${JSON.stringify(unknownVariant)}`, + [key] + ) + }, + true + ) } /**