sindresorhus / electron-store

Simple data persistence for your Electron app or module - Save and load user preferences, app state, cache, etc
MIT License
4.61k stars 150 forks source link

Upgrade to JSON Schema draft 2019/09 #239

Closed ramtinsoltani closed 4 months ago

ramtinsoltani commented 1 year ago

Any plans to upgrade to JSON Schema draft 2019/09? It unlocks some goodies including unevaluatedProperties which is very important when working with schema compositions.

This is an example where not having unevaluatedProperties support limits the schema:

import { Schema } from 'electron-store';

export interface BasicAuthCredentials {
  type: 'basic',
  username: string,
  password: string
}

export interface BearerTokenCredentials {
  type: 'bearer',
  token: string
}

export interface CredentialsStore {
  credentials: {
    [id: string]: BasicAuthCredentials | BearerTokenCredentials
  }
}

export const CredentialsSchema: Schema<CredentialsStore> = {
  credentials: {
    type: 'object',
    additionalProperties: false,
    patternProperties: {
      '^[a-zA-Z0-9]{10}$': {
        /* We cannot use 'oneOf' while disallowing additional properties
        without the support for 'unevaluatedProperties'. */
        unevaluatedProperties: false,
        type: 'object',
        oneOf: [{
          properties: {
            type: { type: 'string', enum: ['basic'] },
            username: { type: 'string', pattern: '^[^:]{1,255}$' },
            password: { type: 'string', minLength: 1, maxLength: 255 }
          },
          required: ['type', 'username', 'password']
        }, {
          properties: {
            type: { type: 'string', enum: ['bearer'] },
            token: { type: 'string', minLength: 1 }
          },
          required: ['type', 'token']
        }]
      }
    }
  }
};
sindresorhus commented 1 year ago

This package depends on ajv 8, which supports:

JSON Schema draft-06/07/2019-09/2020-12

https://github.com/ajv-validator/ajv

ramtinsoltani commented 1 year ago

@sindresorhus Correct. But in order to use draft-2019-09 the ajv import needs to be updated to:

import Ajv from 'ajv/dist/2019';

according to https://ajv.js.org/json-schema.html#draft-2019-09.

I looked at the code inside conf and it can be updated easily. Though the issue is with package json-schema-typed. It needs to be updated to version 8.0.1 to support the new drafts. If the import is changed to the following at conf/source/types.ts:1 after the update:

import {type JSONSchema as TypedJSONSchema} from 'json-schema-typed/draft-2019-09';

we'll get a bunch of errors:

source/index.ts:110:5 - error TS2322: Type 'Schema<T>' is not assignable to type 'Record<string, JSONSchema<any, TypeValue>>'.
  Type 'ValueSchema' is not assignable to type 'JSONSchema<any, TypeValue>'.
    Type '{ $anchor?: string | undefined; $comment?: string | undefined; $defs?: Record<string, JSONSchema<any, TypeValue>> | undefined; $id?: string | undefined; ... 54 more ...; writeOnly?: boolean | undefined; }' is not assignable to type 'JSONSchema<any, TypeValue>'.
      Type '{ $anchor?: string | undefined; $comment?: string | undefined; $defs?: Record<string, JSONSchema<any, TypeValue>> | undefined; $id?: string | undefined; ... 54 more ...; writeOnly?: boolean | undefined; }' is not assignable to type '{ $anchor?: string | undefined; $comment?: string | undefined; $defs?: Record<string, JSONSchema<any, TypeValue>> | undefined; $dynamicAnchor?: string | undefined; ... 55 more ...; writeOnly?: boolean | undefined; }'.
        Types of property 'additionalItems' are incompatible.
          Type 'import("/Users/ramtin/repos/misc/conf/node_modules/json-schema-typed/draft-2019-09").JSONSchema<any, import("/Users/ramtin/repos/misc/conf/node_modules/json-schema-typed/draft-2019-09").JSONSchema.TypeValue> | undefined' is not assignable to type 'import("/Users/ramtin/repos/misc/conf/node_modules/json-schema-typed/draft-2020-12").JSONSchema<any, import("/Users/ramtin/repos/misc/conf/node_modules/json-schema-typed/draft-2020-12").JSONSchema.TypeValue> | undefined'.
            Type '{ $anchor?: string | undefined; $comment?: string | undefined; $defs?: Record<string, JSONSchema<any, TypeValue>> | undefined; $id?: string | undefined; ... 54 more ...; writeOnly?: boolean | undefined; }' is not assignable to type 'JSONSchema<any, TypeValue> | undefined'.

110     properties: options.schema
        ~~~~~~~~~~

source/index.ts:115:58 - error TS2345: Argument of type 'Schema<T>' is not assignable to parameter of type '{ [s: string]: JSONSchema<any, TypeValue>; } | ArrayLike<JSONSchema<any, TypeValue>>'.
  Property 'length' is missing in type 'Schema<T>' but required in type 'ArrayLike<JSONSchema<any, TypeValue>>'.

115    for (const [key, value] of Object.entries<JSONSchema>(options.schema)) {
                                                             ~~~~~~~~~~~~~~

  node_modules/typescript/lib/lib.es5.d.ts:1542:14
    1542     readonly length: number;
                      ~~~~~~
    'length' is declared here.

source/index.ts:116:16 - error TS2339: Property 'default' does not exist on type 'JSONSchema<any, TypeValue>'.
  Property 'default' does not exist on type 'false'.

116     if (value?.default) {
                   ~~~~~~~

source/index.ts:117:50 - error TS2339: Property 'default' does not exist on type 'JSONSchema<any, TypeValue>'.
  Property 'default' does not exist on type 'false'.

117      this.#defaultValues[key as keyof T] = value.default;

Is there another way to use draft-2019-09?

sindresorhus commented 1 year ago

Hmm. Weird that they would not use the latest draft as the default.

I guess we can just do a major version where we update it to the latest draft.

I assume you would be ok with draft-2020-12?

ramtinsoltani commented 1 year ago

@sindresorhus Yes, I think it makes sense to move on to the latest draft. The only thing to keep in mind is that draft-2019-09 is backward compatible with draft-07, while draft-2020-12 has breaking changes.

sindresorhus commented 1 year ago

Looks like it's not possible to upgrade. We need to upgrade to json-schema-typed v8 to get the 2020 or 2019 types, but the package moved to ESM in v8, which is not yet supported by Electron.

ramtinsoltani commented 1 year ago

@sindresorhus Can't we drop json-schema-typed and use ajv's JSONSchemaType instead? Here's the documentation: https://ajv.js.org/guide/typescript.html#utility-types-for-schemas

sindresorhus commented 1 year ago

json-schema-typed has the benefit that it has extensive doc comments and I believe it's more strictly typed. I'm sure I'll get complaints if I switch to worse types.

ramtinsoltani commented 1 year ago

Not sure what the alternative would be in this case. It doesn't seem like Electron would support ES modules anytime soon (see this thread that's been going on for 3 years!).

julien3 commented 4 months ago

Any plan to upgrade JSON Schema draft now ESM are supported?

sindresorhus commented 4 months ago

https://github.com/sindresorhus/electron-store/releases/tag/v10.0.0