diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..884a2323e --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +**/dist/** diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 000000000..049dbd478 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,22 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module" + }, + "plugins": ["@typescript-eslint"], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + "@typescript-eslint/no-unused-vars": [ + "error", + { "varsIgnorePattern": "^_", "argsIgnorePattern": "^_" } + ], + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/ban-ts-comment": "off" + } +} diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml new file mode 100644 index 000000000..9a8c40d36 --- /dev/null +++ b/.github/workflows/build-test.yml @@ -0,0 +1,77 @@ +name: CI + +on: + pull_request: + branches: + - main + - dev + +permissions: + contents: read + +jobs: + build-test: + runs-on: buildjet-8vcpu-ubuntu-2204 + + services: + postgres: + image: postgres + env: + POSTGRES_PASSWORD: abc123 + # Set health checks to wait until postgres has started + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + strategy: + matrix: + node-version: [20.x] + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v2 + with: + version: 10.12.1 + + - name: Use Node.js ${{ matrix.node-version }} + uses: buildjet/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + cache: 'pnpm' + + - name: Get pnpm store directory + id: pnpm-cache + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT + + - name: Setup pnpm cache + uses: buildjet/cache@v3 + with: + path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build + run: pnpm run build + + - name: Lint + run: pnpm run lint + + # install again for internal dependencies + - name: Install internal dependencies + run: pnpm install --frozen-lockfile + + - name: Test + run: pnpm run test diff --git a/package.json b/package.json index 85a472bce..b55c44c68 100644 --- a/package.json +++ b/package.json @@ -2,10 +2,11 @@ "name": "zenstack-v3", "version": "3.0.0-alpha.1", "description": "ZenStack", - "packageManager": "pnpm@10.0.0", + "packageManager": "pnpm@10.12.1", "scripts": { "build": "pnpm -r build", "watch": "pnpm -r --parallel watch", + "lint": "pnpm -r lint", "test": "pnpm vitest" }, "keywords": [], @@ -13,11 +14,19 @@ "license": "ISC", "devDependencies": { "@swc/core": "^1.10.15", + "@typescript-eslint/eslint-plugin": "~7.3.1", + "@typescript-eslint/parser": "~7.3.1", + "eslint": "~8.57.1", "npm-run-all": "^4.1.5", "tsup": "^8.3.5", "tsx": "^4.19.2", "turbo": "^2.3.3", "typescript": "~5.7.3", "vitest": "^3.1.1" + }, + "pnpm": { + "onlyBuiltDependencies": [ + "better-sqlite3" + ] } } diff --git a/packages/cli/package.json b/packages/cli/package.json index 64f6af111..f5091ab09 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -23,6 +23,7 @@ "scripts": { "build": "tsup-node", "watch": "tsup-node --watch", + "lint": "eslint src --ext ts", "test": "vitest", "pack": "pnpm pack" }, diff --git a/packages/cli/test/ts-schema-gen.test.ts b/packages/cli/test/ts-schema-gen.test.ts index cd2efd414..c8fdad96b 100644 --- a/packages/cli/test/ts-schema-gen.test.ts +++ b/packages/cli/test/ts-schema-gen.test.ts @@ -1,4 +1,4 @@ -import { Expression } from '@zenstackhq/runtime/schema'; +import { ExpressionUtils } from '@zenstackhq/runtime/schema'; import { generateTsSchema } from '@zenstackhq/testtools'; import { describe, expect, it } from 'vitest'; @@ -36,7 +36,7 @@ model Post { id: { type: 'String', id: true, - default: Expression.call('uuid'), + default: ExpressionUtils.call('uuid'), attributes: [ { name: '@id' }, { @@ -56,7 +56,7 @@ model Post { email: { type: 'String', unique: true }, createdAt: { type: 'DateTime', - default: Expression.call('now'), + default: ExpressionUtils.call('now'), attributes: [ { name: '@default', @@ -105,7 +105,7 @@ model Post { id: { type: 'String', id: true, - default: Expression.call('cuid'), + default: ExpressionUtils.call('cuid'), attributes: [ { name: '@id' }, { diff --git a/packages/language/.eslintrc.json b/packages/language/.eslintrc.json deleted file mode 100644 index 8252235cf..000000000 --- a/packages/language/.eslintrc.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "root": true, - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 6, - "sourceType": "module" - }, - "plugins": [ - "@typescript-eslint" - ], - "rules": { - } -} diff --git a/packages/language/package.json b/packages/language/package.json index 13795cea3..6db8b6cef 100644 --- a/packages/language/package.json +++ b/packages/language/package.json @@ -40,9 +40,6 @@ "devDependencies": { "@types/node": "^18.0.0", "@types/pluralize": "^0.0.33", - "@typescript-eslint/eslint-plugin": "~7.3.1", - "@typescript-eslint/parser": "~7.3.1", - "eslint": "~8.57.0", "langium-cli": "~3.3.0", "typescript": "~5.1.6" }, diff --git a/packages/language/src/validators/expression-validator.ts b/packages/language/src/validators/expression-validator.ts index df34eeb8f..39ce761a8 100644 --- a/packages/language/src/validators/expression-validator.ts +++ b/packages/language/src/validators/expression-validator.ts @@ -1,12 +1,10 @@ import { AstUtils, type AstNode, type ValidationAcceptor } from 'langium'; import { BinaryExpr, - DataModelAttribute, Expression, isArrayExpr, isDataModel, isDataModelAttribute, - isDataModelField, isEnum, isLiteralExpr, isMemberAccessExpr, @@ -18,7 +16,6 @@ import { import { findUpAst, - getAttributeArgLiteral, isAuthInvocation, isAuthOrAuthMemberAccess, isDataModelFieldReference, diff --git a/packages/language/src/validators/schema-validator.ts b/packages/language/src/validators/schema-validator.ts index dcd9177b4..57cfa4f34 100644 --- a/packages/language/src/validators/schema-validator.ts +++ b/packages/language/src/validators/schema-validator.ts @@ -1,10 +1,8 @@ import type { LangiumDocuments, ValidationAcceptor } from 'langium'; import { PLUGIN_MODULE_NAME, STD_LIB_MODULE_NAME } from '../constants'; -import { isDataModel, isDataSource, type Model } from '../generated/ast'; +import { isDataSource, type Model } from '../generated/ast'; import { getAllDeclarationsIncludingImports, - getDataModelAndTypeDefs, - hasAttribute, resolveImport, resolveTransitiveImports, } from '../utils'; diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 278c5ae1b..d4890d05c 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -7,6 +7,7 @@ "build": "tsup-node && pnpm test-typecheck", "test-typecheck": "tsc --project tsconfig.test.json", "watch": "tsup-node --watch", + "lint": "eslint src --ext ts", "test": "vitest", "pack": "pnpm pack" }, @@ -85,6 +86,7 @@ "dependencies": { "@paralleldrive/cuid2": "^2.2.2", "decimal.js": "^10.4.3", + "is-plain-object": "^5.0.0", "kysely": "^0.27.5", "nanoid": "^5.0.9", "pg-connection-string": "^2.9.0", @@ -112,8 +114,8 @@ "@types/pg": "^8.0.0", "@types/tmp": "^0.2.6", "@zenstackhq/language": "workspace:*", - "@zenstackhq/testtools": "workspace:*", "@zenstackhq/sdk": "workspace:*", + "@zenstackhq/testtools": "workspace:*", "tmp": "^0.2.3" } } diff --git a/packages/runtime/src/client/client-impl.ts b/packages/runtime/src/client/client-impl.ts index 5a3c77b0c..16fa1761b 100644 --- a/packages/runtime/src/client/client-impl.ts +++ b/packages/runtime/src/client/client-impl.ts @@ -11,7 +11,7 @@ import { } from 'kysely'; import { match } from 'ts-pattern'; import type { GetModels, ProcedureDef, SchemaDef } from '../schema'; -import type { AuthType } from '../schema/schema'; +import type { AuthType } from '../schema/auth'; import type { ClientConstructor, ClientContract } from './contract'; import type { ModelOperations } from './crud-types'; import { AggregateOperationHandler } from './crud/operations/aggregate'; @@ -199,6 +199,7 @@ export class ClientImpl { ); } + // eslint-disable-next-line @typescript-eslint/ban-types return (procOptions[name] as Function).apply(this, [this, ...args]); } diff --git a/packages/runtime/src/client/contract.ts b/packages/runtime/src/client/contract.ts index bb9203474..1e33b8ec1 100644 --- a/packages/runtime/src/client/contract.ts +++ b/packages/runtime/src/client/contract.ts @@ -1,10 +1,8 @@ +/* eslint-disable @typescript-eslint/ban-types */ + +import { type GetModels, type ProcedureDef, type SchemaDef } from '../schema'; import type { Decimal } from 'decimal.js'; -import { - type AuthType, - type GetModels, - type ProcedureDef, - type SchemaDef, -} from '../schema/schema'; +import type { AuthType } from '../schema/auth'; import type { OrUndefinedIf } from '../utils/type-utils'; import type { ModelOperations, ModelResult } from './crud-types'; import type { ClientOptions, HasComputedFields } from './options'; diff --git a/packages/runtime/src/client/crud-types.ts b/packages/runtime/src/client/crud-types.ts index da112e41d..c020648fb 100644 --- a/packages/runtime/src/client/crud-types.ts +++ b/packages/runtime/src/client/crud-types.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/ban-types */ + import type { ExpressionBuilder, OperandExpression, SqlBool } from 'kysely'; import type { Optional } from 'utility-types'; import type { @@ -23,7 +25,7 @@ import type { RelationInfo, ScalarFields, SchemaDef, -} from '../schema/schema'; +} from '../schema'; import type { AtLeast, MapBaseType, diff --git a/packages/runtime/src/client/crud/dialects/base.ts b/packages/runtime/src/client/crud/dialects/base.ts index 72975fc56..ac3939d01 100644 --- a/packages/runtime/src/client/crud/dialects/base.ts +++ b/packages/runtime/src/client/crud/dialects/base.ts @@ -8,14 +8,16 @@ import type { import { sql, type SelectQueryBuilder } from 'kysely'; import invariant from 'tiny-invariant'; import { match, P } from 'ts-pattern'; -import type { GetModels, SchemaDef } from '../../../schema'; import type { BuiltinType, DataSourceProviderType, FieldDef, -} from '../../../schema/schema'; + GetModels, + SchemaDef, +} from '../../../schema'; import { enumerate } from '../../../utils/enumerate'; -import { isPlainObject } from '../../../utils/is-plain-object'; +// @ts-expect-error +import { isPlainObject } from 'is-plain-object'; import type { OrArray } from '../../../utils/type-utils'; import type { BooleanFilter, @@ -54,12 +56,12 @@ export abstract class BaseCrudDialect { } abstract buildRelationSelection( - query: SelectQueryBuilder, + query: SelectQueryBuilder, model: string, relationField: string, parentAlias: string, payload: true | FindArgs, true> - ): SelectQueryBuilder; + ): SelectQueryBuilder; abstract buildSkipTake( query: SelectQueryBuilder, @@ -82,7 +84,7 @@ export abstract class BaseCrudDialect { } let result = this.true(eb); - let _where = flattenCompoundUniqueFilters(this.schema, model, where); + const _where = flattenCompoundUniqueFilters(this.schema, model, where); for (const [key, payload] of Object.entries(_where)) { if (payload === undefined) { @@ -284,7 +286,7 @@ export abstract class BaseCrudDialect { ) .select(() => eb.fn.count(eb.lit(1)).as(filterResultField)); - let conditions: Expression[] = []; + const conditions: Expression[] = []; if ('is' in payload || 'isNot' in payload) { if ('is' in payload) { diff --git a/packages/runtime/src/client/crud/dialects/postgresql.ts b/packages/runtime/src/client/crud/dialects/postgresql.ts index 72d409329..aeb45c304 100644 --- a/packages/runtime/src/client/crud/dialects/postgresql.ts +++ b/packages/runtime/src/client/crud/dialects/postgresql.ts @@ -8,8 +8,12 @@ import { } from 'kysely'; import invariant from 'tiny-invariant'; import { match } from 'ts-pattern'; -import type { SchemaDef } from '../../../schema'; -import type { BuiltinType, FieldDef, GetModels } from '../../../schema/schema'; +import type { + BuiltinType, + FieldDef, + GetModels, + SchemaDef, +} from '../../../schema'; import type { FindArgs } from '../../crud-types'; import { buildFieldRef, @@ -49,12 +53,12 @@ export class PostgresCrudDialect< } override buildRelationSelection( - query: SelectQueryBuilder, + query: SelectQueryBuilder, model: string, relationField: string, parentAlias: string, payload: true | FindArgs, true> - ): SelectQueryBuilder { + ): SelectQueryBuilder { const joinedQuery = this.buildRelationJSON( model, query, @@ -261,7 +265,7 @@ export class PostgresCrudDialect< const objArgs: Array< | string | ExpressionWrapper - | SelectQueryBuilder + | SelectQueryBuilder | RawBuilder > = []; diff --git a/packages/runtime/src/client/crud/dialects/sqlite.ts b/packages/runtime/src/client/crud/dialects/sqlite.ts index 62ea65db2..788dfd616 100644 --- a/packages/runtime/src/client/crud/dialects/sqlite.ts +++ b/packages/runtime/src/client/crud/dialects/sqlite.ts @@ -9,8 +9,7 @@ import { } from 'kysely'; import invariant from 'tiny-invariant'; import { match } from 'ts-pattern'; -import type { SchemaDef } from '../../../schema'; -import type { BuiltinType, GetModels } from '../../../schema/schema'; +import type { BuiltinType, GetModels, SchemaDef } from '../../../schema'; import type { FindArgs } from '../../crud-types'; import { buildFieldRef, @@ -49,12 +48,12 @@ export class SqliteCrudDialect< } override buildRelationSelection( - query: SelectQueryBuilder, + query: SelectQueryBuilder, model: string, relationField: string, parentAlias: string, payload: true | FindArgs, true> - ): SelectQueryBuilder { + ): SelectQueryBuilder { return query.select((eb) => this.buildRelationJSON( model, @@ -184,7 +183,7 @@ export class SqliteCrudDialect< type ArgsType = | Expression | RawBuilder - | SelectQueryBuilder; + | SelectQueryBuilder; const objArgs: ArgsType[] = []; if (payload === true || !payload.select) { diff --git a/packages/runtime/src/client/crud/operations/base.ts b/packages/runtime/src/client/crud/operations/base.ts index a49965728..825e329c7 100644 --- a/packages/runtime/src/client/crud/operations/base.ts +++ b/packages/runtime/src/client/crud/operations/base.ts @@ -16,13 +16,13 @@ import { ulid } from 'ulid'; import * as uuid from 'uuid'; import type { ClientContract } from '../..'; import { PolicyPlugin } from '../../../plugins/policy'; +import type { BuiltinType, Expression, FieldDef } from '../../../schema'; import { - Expression, + ExpressionUtils, type GetModels, type ModelDef, type SchemaDef, } from '../../../schema'; -import type { BuiltinType, FieldDef } from '../../../schema/schema'; import { clone } from '../../../utils/clone'; import { enumerate } from '../../../utils/enumerate'; import { @@ -146,7 +146,11 @@ export abstract class BaseOperationHandler { filter: any ): Promise { const idFields = getIdFields(this.schema, model); - let _filter = flattenCompoundUniqueFilters(this.schema, model, filter); + const _filter = flattenCompoundUniqueFilters( + this.schema, + model, + filter + ); const query = kysely .selectFrom(model) .where((eb) => eb.and(_filter)) @@ -173,7 +177,7 @@ export abstract class BaseOperationHandler { // skip && take let negateOrderBy = false; - let skip = args?.skip; + const skip = args?.skip; let take = args?.take; if (take !== undefined && take < 0) { negateOrderBy = true; @@ -277,7 +281,7 @@ export abstract class BaseOperationHandler { private buildFieldSelection( model: string, - query: SelectQueryBuilder, + query: SelectQueryBuilder, selectOrInclude: Record, parentAlias: string ) { @@ -321,7 +325,7 @@ export abstract class BaseOperationHandler { } private buildCountSelection( - query: SelectQueryBuilder, + query: SelectQueryBuilder, model: string, parentAlias: string, payload: any @@ -331,7 +335,7 @@ export abstract class BaseOperationHandler { ([, field]) => field.relation && field.array ); - let selections = + const selections = payload === true ? { select: toManyRelations.reduce((acc, [field]) => { @@ -411,19 +415,18 @@ export abstract class BaseOperationHandler { private buildSelectAllScalarFields( model: string, - query: SelectQueryBuilder, + query: SelectQueryBuilder, omit?: Record ) { - let result = query; const modelDef = this.requireModel(model); return Object.keys(modelDef.fields) .filter((f) => !isRelationField(this.schema, model, f)) .filter((f) => omit?.[f] !== true) - .reduce((acc, f) => this.selectField(acc, model, model, f), result); + .reduce((acc, f) => this.selectField(acc, model, model, f), query); } private selectField( - query: SelectQueryBuilder, + query: SelectQueryBuilder, model: string, modelAlias: string, field: string @@ -442,7 +445,7 @@ export abstract class BaseOperationHandler { private buildCursorFilter( model: string, - query: SelectQueryBuilder, + query: SelectQueryBuilder, cursor: FindArgs, true>['cursor'], orderBy: FindArgs, true>['orderBy'], negateOrderBy: boolean @@ -459,7 +462,7 @@ export abstract class BaseOperationHandler { const cursorFilter = this.dialect.buildFilter(eb, model, model, cursor); let result = query; - let filters: ExpressionWrapper[] = []; + const filters: ExpressionWrapper[] = []; for (let i = orderByItems.length - 1; i >= 0; i--) { const andFilters: ExpressionWrapper[] = []; @@ -1093,19 +1096,19 @@ export abstract class BaseOperationHandler { } private evalGenerator(defaultValue: Expression) { - if (Expression.isCall(defaultValue)) { + if (ExpressionUtils.isCall(defaultValue)) { return match(defaultValue.function) .with('cuid', () => createId()) .with('uuid', () => defaultValue.args?.[0] && - Expression.isLiteral(defaultValue.args?.[0]) && + ExpressionUtils.isLiteral(defaultValue.args?.[0]) && defaultValue.args[0].value === 7 ? uuid.v7() : uuid.v4() ) .with('nanoid', () => defaultValue.args?.[0] && - Expression.isLiteral(defaultValue.args[0]) && + ExpressionUtils.isLiteral(defaultValue.args[0]) && typeof defaultValue.args[0].value === 'number' ? nanoid(defaultValue.args[0].value) : nanoid() @@ -1113,8 +1116,8 @@ export abstract class BaseOperationHandler { .with('ulid', () => ulid()) .otherwise(() => undefined); } else if ( - Expression.isMember(defaultValue) && - Expression.isCall(defaultValue.receiver) && + ExpressionUtils.isMember(defaultValue) && + ExpressionUtils.isCall(defaultValue.receiver) && defaultValue.receiver.function === 'auth' ) { // `auth()` member access diff --git a/packages/runtime/src/client/crud/validator.ts b/packages/runtime/src/client/crud/validator.ts index 1aa7bc8ba..2a7b13fe8 100644 --- a/packages/runtime/src/client/crud/validator.ts +++ b/packages/runtime/src/client/crud/validator.ts @@ -7,7 +7,7 @@ import type { FieldDef, GetModels, SchemaDef, -} from '../../schema/schema'; +} from '../../schema'; import { NUMERIC_FIELD_TYPES } from '../constants'; import { type AggregateArgs, diff --git a/packages/runtime/src/client/executor/zenstack-driver.ts b/packages/runtime/src/client/executor/zenstack-driver.ts index 078196046..cc60596c7 100644 --- a/packages/runtime/src/client/executor/zenstack-driver.ts +++ b/packages/runtime/src/client/executor/zenstack-driver.ts @@ -130,6 +130,7 @@ export class ZenStackDriver implements Driver { #addLogging(connection: DatabaseConnection): void { const executeQuery = connection.executeQuery; const streamQuery = connection.streamQuery; + // eslint-disable-next-line @typescript-eslint/no-this-alias const dis = this; connection.executeQuery = async ( diff --git a/packages/runtime/src/client/executor/zenstack-query-executor.ts b/packages/runtime/src/client/executor/zenstack-query-executor.ts index 748632148..b0c38bbc7 100644 --- a/packages/runtime/src/client/executor/zenstack-query-executor.ts +++ b/packages/runtime/src/client/executor/zenstack-query-executor.ts @@ -82,7 +82,7 @@ export class ZenStackQueryExecutor< ); // TODO: make sure insert and delete return rows - let oldQueryNode = queryNode; + const oldQueryNode = queryNode; if ( (InsertQueryNode.is(queryNode) || DeleteQueryNode.is(queryNode)) && diff --git a/packages/runtime/src/client/helpers/schema-db-pusher.ts b/packages/runtime/src/client/helpers/schema-db-pusher.ts index 78abe8715..6d6079788 100644 --- a/packages/runtime/src/client/helpers/schema-db-pusher.ts +++ b/packages/runtime/src/client/helpers/schema-db-pusher.ts @@ -7,16 +7,14 @@ import { import invariant from 'tiny-invariant'; import { match } from 'ts-pattern'; import { - Expression, + ExpressionUtils, + type BuiltinType, + type CascadeAction, type FieldDef, + type GetModels, type ModelDef, type SchemaDef, } from '../../schema'; -import type { - BuiltinType, - CascadeAction, - GetModels, -} from '../../schema/schema'; import type { ToKysely } from '../query-builder'; import { requireModel } from '../query-utils'; @@ -151,7 +149,7 @@ export class SchemaDbPusher { 'kind' in fieldDef.default ) { if ( - Expression.isCall(fieldDef.default) && + ExpressionUtils.isCall(fieldDef.default) && fieldDef.default.function === 'now' ) { col = col.defaultTo(sql`CURRENT_TIMESTAMP`); @@ -198,7 +196,7 @@ export class SchemaDbPusher { } const type = fieldDef.type as BuiltinType; - let result = match(type) + const result = match(type) .with('String', () => 'text') .with('Boolean', () => 'boolean') .with('Int', () => 'integer') @@ -224,7 +222,7 @@ export class SchemaDbPusher { private isAutoIncrement(fieldDef: FieldDef) { return ( fieldDef.default && - Expression.isCall(fieldDef.default) && + ExpressionUtils.isCall(fieldDef.default) && fieldDef.default.function === 'autoincrement' ); } diff --git a/packages/runtime/src/client/options.ts b/packages/runtime/src/client/options.ts index 184f6fe11..e340fc6cf 100644 --- a/packages/runtime/src/client/options.ts +++ b/packages/runtime/src/client/options.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/ban-types */ + import type { Expression, ExpressionBuilder, @@ -12,7 +14,7 @@ import type { GetModels, ProcedureDef, SchemaDef, -} from '../schema/schema'; +} from '../schema'; import type { PrependParameter } from '../utils/type-utils'; import type { ClientContract, CRUD, ProcedureFunc } from './contract'; import type { BaseCrudDialect } from './crud/dialects/base'; diff --git a/packages/runtime/src/client/query-builder.ts b/packages/runtime/src/client/query-builder.ts index 9ac908f0e..74ab8ecd5 100644 --- a/packages/runtime/src/client/query-builder.ts +++ b/packages/runtime/src/client/query-builder.ts @@ -9,7 +9,7 @@ import type { GetModels, ScalarFields, SchemaDef, -} from '../schema/schema'; +} from '../schema'; export type ToKyselySchema = { [Model in GetModels]: ToKyselyTable; diff --git a/packages/runtime/src/client/query-utils.ts b/packages/runtime/src/client/query-utils.ts index 7eb45fc1b..e9a0f58cf 100644 --- a/packages/runtime/src/client/query-utils.ts +++ b/packages/runtime/src/client/query-utils.ts @@ -1,5 +1,5 @@ import type { ExpressionBuilder, ExpressionWrapper } from 'kysely'; -import type { FieldDef, GetModels, SchemaDef } from '../schema/schema'; +import type { FieldDef, GetModels, SchemaDef } from '../schema'; import type { OrderBy } from './crud-types'; import { InternalError, QueryError } from './errors'; import type { ClientOptions } from './options'; @@ -207,6 +207,7 @@ export function buildFieldRef( if (!fieldDef.computed) { return eb.ref(modelAlias ? `${modelAlias}.${field}` : field); } else { + // eslint-disable-next-line @typescript-eslint/ban-types let computer: Function | undefined; if ('computedFields' in options) { const computedFields = options.computedFields as Record< diff --git a/packages/runtime/src/client/result-processor.ts b/packages/runtime/src/client/result-processor.ts index f47a64f69..92c2b3d8d 100644 --- a/packages/runtime/src/client/result-processor.ts +++ b/packages/runtime/src/client/result-processor.ts @@ -1,8 +1,7 @@ import Decimal from 'decimal.js'; import invariant from 'tiny-invariant'; import { match } from 'ts-pattern'; -import type { FieldDef, GetModels, SchemaDef } from '../schema'; -import type { BuiltinType } from '../schema/schema'; +import type { BuiltinType, FieldDef, GetModels, SchemaDef } from '../schema'; import { ensureArray, getField } from './query-utils'; export class ResultProcessor { diff --git a/packages/runtime/src/plugins/policy/expression-evaluator.ts b/packages/runtime/src/plugins/policy/expression-evaluator.ts index 2f0c7d4c2..a2d64e161 100644 --- a/packages/runtime/src/plugins/policy/expression-evaluator.ts +++ b/packages/runtime/src/plugins/policy/expression-evaluator.ts @@ -1,10 +1,11 @@ import invariant from 'tiny-invariant'; import { match } from 'ts-pattern'; import { - Expression, + ExpressionUtils, type ArrayExpression, type BinaryExpression, type CallExpression, + type Expression, type FieldExpression, type LiteralExpression, type MemberExpression, @@ -22,25 +23,29 @@ type ExpressionEvaluatorContext = { export class ExpressionEvaluator { evaluate(expression: Expression, context: ExpressionEvaluatorContext): any { const result = match(expression) - .when(Expression.isArray, (expr) => + .when(ExpressionUtils.isArray, (expr) => this.evaluateArray(expr, context) ) - .when(Expression.isBinary, (expr) => + .when(ExpressionUtils.isBinary, (expr) => this.evaluateBinary(expr, context) ) - .when(Expression.isField, (expr) => + .when(ExpressionUtils.isField, (expr) => this.evaluateField(expr, context) ) - .when(Expression.isLiteral, (expr) => this.evaluateLiteral(expr)) - .when(Expression.isMember, (expr) => + .when(ExpressionUtils.isLiteral, (expr) => + this.evaluateLiteral(expr) + ) + .when(ExpressionUtils.isMember, (expr) => this.evaluateMember(expr, context) ) - .when(Expression.isUnary, (expr) => + .when(ExpressionUtils.isUnary, (expr) => this.evaluateUnary(expr, context) ) - .when(Expression.isCall, (expr) => this.evaluateCall(expr, context)) - .when(Expression.isThis, () => context.thisValue) - .when(Expression.isNull, () => null) + .when(ExpressionUtils.isCall, (expr) => + this.evaluateCall(expr, context) + ) + .when(ExpressionUtils.isThis, () => context.thisValue) + .when(ExpressionUtils.isNull, () => null) .exhaustive(); return result ?? null; diff --git a/packages/runtime/src/plugins/policy/expression-transformer.ts b/packages/runtime/src/plugins/policy/expression-transformer.ts index 23d7345c9..78a694e29 100644 --- a/packages/runtime/src/plugins/policy/expression-transformer.ts +++ b/packages/runtime/src/plugins/policy/expression-transformer.ts @@ -29,21 +29,24 @@ import { getRelationForeignKeyFieldPairs, requireField, } from '../../client/query-utils'; +import { + ExpressionUtils, + type ArrayExpression, + type CallExpression, + type Expression, + type FieldExpression, + type SchemaDef, +} from '../../schema'; import type { - ArrayExpression, - CallExpression, - FieldExpression, - SchemaDef, + BinaryExpression, + BinaryOperator, + LiteralExpression, + MemberExpression, + UnaryExpression, + BuiltinType, + FieldDef, + GetModels, } from '../../schema'; -import { - Expression, - type BinaryExpression, - type BinaryOperator, - type LiteralExpression, - type MemberExpression, - type UnaryExpression, -} from '../../schema/expression'; -import type { BuiltinType, FieldDef, GetModels } from '../../schema/schema'; import { ExpressionEvaluator } from './expression-evaluator'; import { conjunction, disjunction, logicalNot, trueNode } from './utils'; @@ -107,7 +110,7 @@ export class ExpressionTransformer { } @expr('literal') - // @ts-ignore + // @ts-expect-error private _literal(expr: LiteralExpression) { return this.transformValue( expr.value, @@ -120,7 +123,7 @@ export class ExpressionTransformer { } @expr('array') - // @ts-ignore + // @ts-expect-error private _array( expr: ArrayExpression, context: ExpressionTransformerContext @@ -131,7 +134,7 @@ export class ExpressionTransformer { } @expr('field') - // @ts-ignore + // @ts-expect-error private _field( expr: FieldExpression, context: ExpressionTransformerContext @@ -281,12 +284,13 @@ export class ExpressionTransformer { } invariant( - Expression.isField(expr.left) || Expression.isMember(expr.left), + ExpressionUtils.isField(expr.left) || + ExpressionUtils.isMember(expr.left), 'left operand must be field or member access' ); let newContextModel: string; - if (Expression.isField(expr.left)) { + if (ExpressionUtils.isField(expr.left)) { const fieldDef = requireField( this.schema, context.model, @@ -294,7 +298,7 @@ export class ExpressionTransformer { ); newContextModel = fieldDef.type; } else { - invariant(Expression.isField(expr.left.receiver)); + invariant(ExpressionUtils.isField(expr.left.receiver)); const fieldDef = requireField( this.schema, context.model, @@ -370,7 +374,7 @@ export class ExpressionTransformer { other = expr.left; } - if (Expression.isNull(other)) { + if (ExpressionUtils.isNull(other)) { return this.transformValue( expr.op === '==' ? !this.auth : !!this.auth, 'Boolean' @@ -445,17 +449,17 @@ export class ExpressionTransformer { arg: Expression, context: ExpressionTransformerContext ): OperandExpression { - if (Expression.isLiteral(arg)) { + if (ExpressionUtils.isLiteral(arg)) { return eb.val(arg.value); } - if (Expression.isField(arg)) { + if (ExpressionUtils.isField(arg)) { return context.thisEntityRaw ? eb.val(context.thisEntityRaw[arg.field]) : eb.ref(arg.field); } - if (Expression.isCall(arg)) { + if (ExpressionUtils.isCall(arg)) { return this.transformCall(arg, context); } @@ -487,7 +491,7 @@ export class ExpressionTransformer { } invariant( - Expression.isField(expr.receiver), + ExpressionUtils.isField(expr.receiver), 'expect receiver to be field expression' ); @@ -711,11 +715,11 @@ export class ExpressionTransformer { } private isAuthCall(value: unknown): value is CallExpression { - return Expression.isCall(value) && value.function === 'auth'; + return ExpressionUtils.isCall(value) && value.function === 'auth'; } private isAuthMember(expr: Expression) { - return Expression.isMember(expr) && this.isAuthCall(expr.receiver); + return ExpressionUtils.isMember(expr) && this.isAuthCall(expr.receiver); } private isNullNode(node: OperationNode) { diff --git a/packages/runtime/src/plugins/policy/policy-handler.ts b/packages/runtime/src/plugins/policy/policy-handler.ts index 8621e20a1..03812d2f6 100644 --- a/packages/runtime/src/plugins/policy/policy-handler.ts +++ b/packages/runtime/src/plugins/policy/policy-handler.ts @@ -38,8 +38,13 @@ import { requireField, requireModel, } from '../../client/query-utils'; -import { Expression, type GetModels, type SchemaDef } from '../../schema'; -import type { BuiltinType } from '../../schema/schema'; +import { + ExpressionUtils, + type BuiltinType, + type Expression, + type GetModels, + type SchemaDef, +} from '../../schema'; import { ColumnCollector } from './column-collector'; import { RejectedByPolicyError } from './errors'; import { ExpressionTransformer } from './expression-transformer'; @@ -278,7 +283,7 @@ export class PolicyHandler< raw: (item as ValueNode).value, }); } else { - let value = this.dialect.transformPrimitive( + const value = this.dialect.transformPrimitive( item, fieldDef.type as BuiltinType ); @@ -326,7 +331,7 @@ export class PolicyHandler< } private isTrueExpr(expr: Expression) { - return Expression.isLiteral(expr) && expr.value === true; + return ExpressionUtils.isLiteral(expr) && expr.value === true; } private async processReadBack( @@ -633,7 +638,7 @@ export class PolicyHandler< const result: Policy[] = []; const extractOperations = (expr: Expression) => { - invariant(Expression.isLiteral(expr), 'expecting a literal'); + invariant(ExpressionUtils.isLiteral(expr), 'expecting a literal'); invariant( typeof expr.value === 'string', 'expecting a string literal' diff --git a/packages/runtime/src/schema/auth.ts b/packages/runtime/src/schema/auth.ts new file mode 100644 index 000000000..7e47a778a --- /dev/null +++ b/packages/runtime/src/schema/auth.ts @@ -0,0 +1,9 @@ +import type { GetModels, SchemaDef } from '.'; +import type { ModelResult } from '../client/crud-types'; + +export type AuthType = + string extends GetModels + ? Record + : Schema['authType'] extends GetModels + ? Partial> + : never; diff --git a/packages/runtime/src/schema/expression.ts b/packages/runtime/src/schema/expression.ts index e2936fb90..cf4908351 100644 --- a/packages/runtime/src/schema/expression.ts +++ b/packages/runtime/src/schema/expression.ts @@ -1,78 +1,19 @@ -export type Expression = - | LiteralExpression - | ArrayExpression - | FieldExpression - | MemberExpression - | CallExpression - | UnaryExpression - | BinaryExpression - | ThisExpression - | NullExpression; - -export type LiteralExpression = { - kind: 'literal'; - value: string | number | boolean; -}; - -export type ArrayExpression = { - kind: 'array'; - items: Expression[]; -}; - -export type FieldExpression = { - kind: 'field'; - field: string; -}; - -export type MemberExpression = { - kind: 'member'; - receiver: Expression; - members: string[]; -}; - -export type UnaryExpression = { - kind: 'unary'; - op: UnaryOperator; - operand: Expression; -}; - -export type BinaryExpression = { - kind: 'binary'; - op: BinaryOperator; - left: Expression; - right: Expression; -}; - -export type CallExpression = { - kind: 'call'; - function: string; - args?: Expression[]; -}; - -export type ThisExpression = { - kind: 'this'; -}; - -export type NullExpression = { - kind: 'null'; -}; - -export type UnaryOperator = '!'; -export type BinaryOperator = - | '&&' - | '||' - | '==' - | '!=' - | '<' - | '<=' - | '>' - | '>=' - | '?' - | '!' - | '^' - | 'in'; - -export const Expression = { +import type { + ArrayExpression, + BinaryExpression, + BinaryOperator, + CallExpression, + Expression, + FieldExpression, + LiteralExpression, + MemberExpression, + NullExpression, + ThisExpression, + UnaryExpression, + UnaryOperator, +} from '.'; + +export const ExpressionUtils = { literal: (value: string | number | boolean): LiteralExpression => { return { kind: 'literal', @@ -145,14 +86,14 @@ export const Expression = { and: (expr: Expression, ...expressions: Expression[]) => { return expressions.reduce( - (acc, exp) => Expression.binary(acc, '&&', exp), + (acc, exp) => ExpressionUtils.binary(acc, '&&', exp), expr ); }, or: (expr: Expression, ...expressions: Expression[]) => { return expressions.reduce( - (acc, exp) => Expression.binary(acc, '||', exp), + (acc, exp) => ExpressionUtils.binary(acc, '||', exp), expr ); }, @@ -167,29 +108,29 @@ export const Expression = { }, isLiteral: (value: unknown): value is LiteralExpression => - Expression.is(value, 'literal'), + ExpressionUtils.is(value, 'literal'), isArray: (value: unknown): value is ArrayExpression => - Expression.is(value, 'array'), + ExpressionUtils.is(value, 'array'), isCall: (value: unknown): value is CallExpression => - Expression.is(value, 'call'), + ExpressionUtils.is(value, 'call'), isNull: (value: unknown): value is NullExpression => - Expression.is(value, 'null'), + ExpressionUtils.is(value, 'null'), isThis: (value: unknown): value is ThisExpression => - Expression.is(value, 'this'), + ExpressionUtils.is(value, 'this'), isUnary: (value: unknown): value is UnaryExpression => - Expression.is(value, 'unary'), + ExpressionUtils.is(value, 'unary'), isBinary: (value: unknown): value is BinaryExpression => - Expression.is(value, 'binary'), + ExpressionUtils.is(value, 'binary'), isField: (value: unknown): value is FieldExpression => - Expression.is(value, 'field'), + ExpressionUtils.is(value, 'field'), isMember: (value: unknown): value is MemberExpression => - Expression.is(value, 'member'), + ExpressionUtils.is(value, 'member'), }; diff --git a/packages/runtime/src/schema/index.ts b/packages/runtime/src/schema/index.ts index 7bcf832ce..4f98939e8 100644 --- a/packages/runtime/src/schema/index.ts +++ b/packages/runtime/src/schema/index.ts @@ -1,16 +1,3 @@ -export * from './expression'; -export type { - CascadeAction, - FieldDef, - FieldType, - GetFields, - GetModels, - ModelDef, - ProcedureDef, - RelationFields, - RelationInfo, - ScalarFields, - SchemaDef, -} from './schema'; - +export type * from '@zenstackhq/sdk/schema'; export type { OperandExpression } from 'kysely'; +export * from './expression'; diff --git a/packages/runtime/src/utils/clone.ts b/packages/runtime/src/utils/clone.ts index 585d3400a..aaf735fad 100644 --- a/packages/runtime/src/utils/clone.ts +++ b/packages/runtime/src/utils/clone.ts @@ -1,4 +1,5 @@ -import { isPlainObject } from './is-plain-object'; +// @ts-expect-error +import { isPlainObject } from 'is-plain-object'; /** * Clones the given object. Only arrays and plain objects are cloned. Other values are returned as is. diff --git a/packages/runtime/src/utils/is-plain-object.ts b/packages/runtime/src/utils/is-plain-object.ts deleted file mode 100644 index f9e50be25..000000000 --- a/packages/runtime/src/utils/is-plain-object.ts +++ /dev/null @@ -1,25 +0,0 @@ -function isObject(o: object) { - return Object.prototype.toString.call(o) === '[object Object]'; -} - -export function isPlainObject(o: object) { - var ctor, prot; - - if (isObject(o) === false) return false; - - // If has modified constructor - ctor = o.constructor; - if (ctor === undefined) return true; - - // If has modified prototype - prot = ctor.prototype; - if (isObject(prot) === false) return false; - - // If constructor does not have an Object-specific method - if (prot.hasOwnProperty('isPrototypeOf') === false) { - return false; - } - - // Most likely a plain Object - return true; -} diff --git a/packages/runtime/src/utils/type-utils.ts b/packages/runtime/src/utils/type-utils.ts index 8948a8faf..a8468d317 100644 --- a/packages/runtime/src/utils/type-utils.ts +++ b/packages/runtime/src/utils/type-utils.ts @@ -41,6 +41,7 @@ export type JsonValue = export type JsonObject = { [key: string]: JsonValue }; export type JsonArray = Array; +// eslint-disable-next-line @typescript-eslint/ban-types export type Simplify = { [Key in keyof T]: T[Key] } & {}; export function call(code: string) { diff --git a/packages/runtime/test/client-api/default-values.test.ts b/packages/runtime/test/client-api/default-values.test.ts index 79bce819f..da57fc846 100644 --- a/packages/runtime/test/client-api/default-values.test.ts +++ b/packages/runtime/test/client-api/default-values.test.ts @@ -4,7 +4,7 @@ import { isValid as isValidUlid } from 'ulid'; import { validate as isValidUuid } from 'uuid'; import { describe, expect, it } from 'vitest'; import { ZenStackClient } from '../../src'; -import { Expression, type SchemaDef } from '../../src/schema'; +import { ExpressionUtils, type SchemaDef } from '../../src/schema'; const schema = { provider: { @@ -20,35 +20,41 @@ const schema = { uuid: { type: 'String', id: true, - default: Expression.call('uuid'), + default: ExpressionUtils.call('uuid'), }, uuid7: { type: 'String', - default: Expression.call('uuid', [Expression.literal(7)]), + default: ExpressionUtils.call('uuid', [ + ExpressionUtils.literal(7), + ]), }, cuid: { type: 'String', - default: Expression.call('cuid'), + default: ExpressionUtils.call('cuid'), }, cuid2: { type: 'String', - default: Expression.call('cuid', [Expression.literal(2)]), + default: ExpressionUtils.call('cuid', [ + ExpressionUtils.literal(2), + ]), }, nanoid: { type: 'String', - default: Expression.call('nanoid'), + default: ExpressionUtils.call('nanoid'), }, nanoid8: { type: 'String', - default: Expression.call('nanoid', [Expression.literal(8)]), + default: ExpressionUtils.call('nanoid', [ + ExpressionUtils.literal(8), + ]), }, ulid: { type: 'String', - default: Expression.call('ulid'), + default: ExpressionUtils.call('ulid'), }, dt: { type: 'DateTime', - default: Expression.call('now'), + default: ExpressionUtils.call('now'), }, }, idFields: ['uuid'], diff --git a/packages/runtime/test/client-api/name-mapping.test.ts b/packages/runtime/test/client-api/name-mapping.test.ts index 2631ca6cb..bd85e05e4 100644 --- a/packages/runtime/test/client-api/name-mapping.test.ts +++ b/packages/runtime/test/client-api/name-mapping.test.ts @@ -1,8 +1,7 @@ import SQLite from 'better-sqlite3'; import { describe, expect, it } from 'vitest'; -import { Expression } from '../../dist/schema'; import { ZenStackClient } from '../../src'; -import type { SchemaDef } from '../../src/schema'; +import { type SchemaDef, ExpressionUtils } from '../../src/schema'; describe('Name mapping tests', () => { const schema = { @@ -18,7 +17,7 @@ describe('Name mapping tests', () => { id: { type: 'String', id: true, - default: Expression.call('uuid'), + default: ExpressionUtils.call('uuid'), }, x: { type: 'Int', diff --git a/packages/runtime/test/test-schema.ts b/packages/runtime/test/test-schema.ts index 1b0550717..790a997fd 100644 --- a/packages/runtime/test/test-schema.ts +++ b/packages/runtime/test/test-schema.ts @@ -1,6 +1,6 @@ import Sqlite from 'better-sqlite3'; -import { Expression } from '../src/schema/expression'; -import type { DataSourceProviderType, SchemaDef } from '../src/schema/schema'; +import type { DataSourceProviderType, SchemaDef } from '../src/schema'; +import { ExpressionUtils } from '../src/schema/expression'; export const schema = { provider: { @@ -16,7 +16,7 @@ export const schema = { id: { type: 'String', id: true, - default: Expression.call('cuid'), + default: ExpressionUtils.call('cuid'), attributes: [ { name: '@id' }, { @@ -47,7 +47,7 @@ export const schema = { }, createdAt: { type: 'DateTime', - default: Expression.call('now'), + default: ExpressionUtils.call('now'), attributes: [ { name: '@default', @@ -102,16 +102,17 @@ export const schema = { args: [ { name: 'operation', - value: Expression.literal('all'), + value: ExpressionUtils.literal('all'), }, { name: 'condition', - value: Expression.binary( - Expression.member(Expression.call('auth'), [ - 'id', - ]), + value: ExpressionUtils.binary( + ExpressionUtils.member( + ExpressionUtils.call('auth'), + ['id'] + ), '==', - Expression.field('id') + ExpressionUtils.field('id') ), }, ], @@ -122,14 +123,14 @@ export const schema = { args: [ { name: 'operation', - value: Expression.literal('read'), + value: ExpressionUtils.literal('read'), }, { name: 'condition', - value: Expression.binary( - Expression.call('auth'), + value: ExpressionUtils.binary( + ExpressionUtils.call('auth'), '!=', - Expression._null() + ExpressionUtils._null() ), }, ], @@ -141,11 +142,11 @@ export const schema = { id: { type: 'String', id: true, - default: Expression.call('cuid'), + default: ExpressionUtils.call('cuid'), }, createdAt: { type: 'DateTime', - default: Expression.call('now'), + default: ExpressionUtils.call('now'), }, updatedAt: { type: 'DateTime', @@ -195,14 +196,14 @@ export const schema = { args: [ { name: 'operation', - value: Expression.literal('all'), + value: ExpressionUtils.literal('all'), }, { name: 'condition', - value: Expression.binary( - Expression.call('auth'), + value: ExpressionUtils.binary( + ExpressionUtils.call('auth'), '==', - Expression._null() + ExpressionUtils._null() ), }, ], @@ -213,16 +214,17 @@ export const schema = { args: [ { name: 'operation', - value: Expression.literal('all'), + value: ExpressionUtils.literal('all'), }, { name: 'condition', - value: Expression.binary( - Expression.member(Expression.call('auth'), [ - 'id', - ]), + value: ExpressionUtils.binary( + ExpressionUtils.member( + ExpressionUtils.call('auth'), + ['id'] + ), '==', - Expression.field('authorId') + ExpressionUtils.field('authorId') ), }, ], @@ -233,11 +235,11 @@ export const schema = { args: [ { name: 'operation', - value: Expression.literal('read'), + value: ExpressionUtils.literal('read'), }, { name: 'condition', - value: Expression.field('published'), + value: ExpressionUtils.field('published'), }, ], }, @@ -248,11 +250,11 @@ export const schema = { id: { type: 'String', id: true, - default: Expression.call('cuid'), + default: ExpressionUtils.call('cuid'), }, createdAt: { type: 'DateTime', - default: Expression.call('now'), + default: ExpressionUtils.call('now'), }, updatedAt: { type: 'DateTime', @@ -288,7 +290,7 @@ export const schema = { id: { type: 'String', id: true, - default: Expression.call('cuid'), + default: ExpressionUtils.call('cuid'), }, bio: { type: 'String' }, age: { type: 'Int', optional: true }, diff --git a/packages/runtime/test/utils.ts b/packages/runtime/test/utils.ts index f3508ab1f..b352be5da 100644 --- a/packages/runtime/test/utils.ts +++ b/packages/runtime/test/utils.ts @@ -9,7 +9,7 @@ import { Client as PGClient, Pool } from 'pg'; import invariant from 'tiny-invariant'; import { ZenStackClient } from '../src/client'; import type { ClientOptions } from '../src/client/options'; -import type { SchemaDef } from '../src/schema/schema'; +import type { SchemaDef } from '../src/schema'; type SqliteSchema = SchemaDef & { provider: { type: 'sqlite' } }; type PostgresSchema = SchemaDef & { provider: { type: 'postgresql' } }; diff --git a/packages/sdk/package.json b/packages/sdk/package.json index e3b5884e9..949a29400 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -6,6 +6,7 @@ "scripts": { "build": "tsup-node", "watch": "tsup-node --watch", + "lint": "eslint src --ext ts", "test": "vitest", "pack": "pnpm pack" }, @@ -25,6 +26,16 @@ "types": "./dist/index.d.cts", "default": "./dist/index.cjs" } + }, + "./schema": { + "import": { + "types": "./dist/schema.d.ts", + "default": "./dist/schema.js" + }, + "require": { + "types": "./dist/schema.d.cts", + "default": "./dist/schema.cjs" + } } }, "dependencies": { @@ -37,6 +48,8 @@ }, "devDependencies": { "@types/node": "^18.0.0", - "@types/tmp": "^0.2.6" + "@types/tmp": "^0.2.6", + "decimal.js": "^10.4.3", + "kysely": "^0.27.6" } } diff --git a/packages/sdk/src/schema/expression.ts b/packages/sdk/src/schema/expression.ts new file mode 100644 index 000000000..dafc30f54 --- /dev/null +++ b/packages/sdk/src/schema/expression.ts @@ -0,0 +1,73 @@ +export type Expression = + | LiteralExpression + | ArrayExpression + | FieldExpression + | MemberExpression + | CallExpression + | UnaryExpression + | BinaryExpression + | ThisExpression + | NullExpression; + +export type LiteralExpression = { + kind: 'literal'; + value: string | number | boolean; +}; + +export type ArrayExpression = { + kind: 'array'; + items: Expression[]; +}; + +export type FieldExpression = { + kind: 'field'; + field: string; +}; + +export type MemberExpression = { + kind: 'member'; + receiver: Expression; + members: string[]; +}; + +export type UnaryExpression = { + kind: 'unary'; + op: UnaryOperator; + operand: Expression; +}; + +export type BinaryExpression = { + kind: 'binary'; + op: BinaryOperator; + left: Expression; + right: Expression; +}; + +export type CallExpression = { + kind: 'call'; + function: string; + args?: Expression[]; +}; + +export type ThisExpression = { + kind: 'this'; +}; + +export type NullExpression = { + kind: 'null'; +}; + +export type UnaryOperator = '!'; +export type BinaryOperator = + | '&&' + | '||' + | '==' + | '!=' + | '<' + | '<=' + | '>' + | '>=' + | '?' + | '!' + | '^' + | 'in'; diff --git a/packages/sdk/src/schema/index.ts b/packages/sdk/src/schema/index.ts new file mode 100644 index 000000000..6863d7bad --- /dev/null +++ b/packages/sdk/src/schema/index.ts @@ -0,0 +1,4 @@ +export type * from './expression'; +export type * from './schema'; + +export type { OperandExpression } from 'kysely'; diff --git a/packages/runtime/src/schema/schema.ts b/packages/sdk/src/schema/schema.ts similarity index 95% rename from packages/runtime/src/schema/schema.ts rename to packages/sdk/src/schema/schema.ts index 2d96d239e..9b62ca209 100644 --- a/packages/runtime/src/schema/schema.ts +++ b/packages/sdk/src/schema/schema.ts @@ -1,5 +1,4 @@ import type Decimal from 'decimal.js'; -import type { ModelResult } from '../client'; import type { Expression } from './expression'; export type DataSourceProviderType = 'sqlite' | 'postgresql'; @@ -29,6 +28,7 @@ export type ModelDef = { | Record> >; idFields: string[]; + // eslint-disable-next-line @typescript-eslint/ban-types computedFields?: Record; }; @@ -255,11 +255,4 @@ export type FieldIsRelationArray< ? FieldIsArray : false; -export type AuthType = - string extends GetModels - ? Record - : Schema['authType'] extends GetModels - ? Partial> - : never; - //#endregion diff --git a/packages/sdk/src/ts-schema-generator.ts b/packages/sdk/src/ts-schema-generator.ts index d5b60df12..a90bb8d95 100644 --- a/packages/sdk/src/ts-schema-generator.ts +++ b/packages/sdk/src/ts-schema-generator.ts @@ -116,7 +116,7 @@ export class TsSchemaGenerator { ts.factory.createImportSpecifier( false, undefined, - ts.factory.createIdentifier('Expression') + ts.factory.createIdentifier('ExpressionUtils') ), ]) ), @@ -457,7 +457,7 @@ export class TsSchemaGenerator { ts.factory.createPropertyAssignment( 'type', ts.factory.createStringLiteral( - field.type.type ?? field.type.reference?.$refText! + field.type.type ?? field.type.reference!.$refText ) ), ]; @@ -530,7 +530,9 @@ export class TsSchemaGenerator { 'default', ts.factory.createCallExpression( - ts.factory.createIdentifier('Expression.call'), + ts.factory.createIdentifier( + 'ExpressionUtils.call' + ), undefined, [ ts.factory.createStringLiteral( @@ -557,13 +559,13 @@ export class TsSchemaGenerator { 'default', ts.factory.createCallExpression( ts.factory.createIdentifier( - 'Expression.member' + 'ExpressionUtils.member' ), undefined, [ ts.factory.createCallExpression( ts.factory.createIdentifier( - 'Expression.call' + 'ExpressionUtils.call' ), undefined, [ts.factory.createStringLiteral('auth')] @@ -635,7 +637,7 @@ export class TsSchemaGenerator { private getDataSourceProvider( model: Model ): - | { type: string; url: string; env: undefined } + | { type: string; env: undefined; url: string } | { type: string; env: string; url: undefined } { const dataSource = model.declarations.find(isDataSource); invariant(dataSource, 'No data source found in the model'); @@ -652,7 +654,6 @@ export class TsSchemaGenerator { 'URL must be a literal or env function' ); - let url: string; if (isLiteralExpr(urlExpr)) { return { type, url: urlExpr.value as string, env: undefined }; } else if (isInvocationExpr(urlExpr)) { @@ -664,9 +665,6 @@ export class TsSchemaGenerator { urlExpr.args.length === 1, 'env function must have one argument' ); - url = `env(${ - (urlExpr.args[0]!.value as LiteralExpr).value as string - })`; return { type, env: (urlExpr.args[0]!.value as LiteralExpr).value as string, @@ -1039,7 +1037,9 @@ export class TsSchemaGenerator { let parsedUrl: URL | undefined; try { parsedUrl = new URL(dsProvider.url); - } catch {} + } catch { + // ignore + } if (parsedUrl) { if (parsedUrl.protocol !== 'file:') { @@ -1144,7 +1144,7 @@ export class TsSchemaGenerator { ts.factory.createPropertyAssignment( 'type', ts.factory.createStringLiteral( - param.type.type ?? param.type.reference?.$refText! + param.type.type ?? param.type.reference!.$refText ) ), ]) @@ -1174,7 +1174,7 @@ export class TsSchemaGenerator { ts.factory.createLiteralTypeNode( ts.factory.createStringLiteral( param.type.type ?? - param.type.reference?.$refText! + param.type.reference!.$refText ) ) ), @@ -1207,7 +1207,7 @@ export class TsSchemaGenerator { 'returnType', ts.factory.createStringLiteral( proc.returnType.type ?? - proc.returnType.reference?.$refText! + proc.returnType.reference!.$refText ) ), ...(proc.mutation @@ -1306,7 +1306,7 @@ export class TsSchemaGenerator { private createThisExpression() { return ts.factory.createCallExpression( - ts.factory.createIdentifier('Expression._this'), + ts.factory.createIdentifier('ExpressionUtils._this'), undefined, [] ); @@ -1331,7 +1331,7 @@ export class TsSchemaGenerator { ]; return ts.factory.createCallExpression( - ts.factory.createIdentifier('Expression.member'), + ts.factory.createIdentifier('ExpressionUtils.member'), undefined, args ); @@ -1339,7 +1339,7 @@ export class TsSchemaGenerator { private createNullExpression() { return ts.factory.createCallExpression( - ts.factory.createIdentifier('Expression._null'), + ts.factory.createIdentifier('ExpressionUtils._null'), undefined, [] ); @@ -1347,7 +1347,7 @@ export class TsSchemaGenerator { private createBinaryExpression(expr: BinaryExpr) { return ts.factory.createCallExpression( - ts.factory.createIdentifier('Expression.binary'), + ts.factory.createIdentifier('ExpressionUtils.binary'), undefined, [ this.createExpression(expr.left), @@ -1359,7 +1359,7 @@ export class TsSchemaGenerator { private createUnaryExpression(expr: UnaryExpr) { return ts.factory.createCallExpression( - ts.factory.createIdentifier('Expression.unary'), + ts.factory.createIdentifier('ExpressionUtils.unary'), undefined, [ this.createLiteralNode(expr.operator), @@ -1370,7 +1370,7 @@ export class TsSchemaGenerator { private createArrayExpression(expr: ArrayExpr): any { return ts.factory.createCallExpression( - ts.factory.createIdentifier('Expression.array'), + ts.factory.createIdentifier('ExpressionUtils.array'), undefined, [ ts.factory.createArrayLiteralExpression( @@ -1383,7 +1383,7 @@ export class TsSchemaGenerator { private createRefExpression(expr: ReferenceExpr): any { if (isDataModelField(expr.target.ref)) { return ts.factory.createCallExpression( - ts.factory.createIdentifier('Expression.field'), + ts.factory.createIdentifier('ExpressionUtils.field'), undefined, [this.createLiteralNode(expr.target.$refText)] ); @@ -1401,7 +1401,7 @@ export class TsSchemaGenerator { private createCallExpression(expr: InvocationExpr) { return ts.factory.createCallExpression( - ts.factory.createIdentifier('Expression.call'), + ts.factory.createIdentifier('ExpressionUtils.call'), undefined, [ ts.factory.createStringLiteral(expr.function.$refText), @@ -1422,21 +1422,21 @@ export class TsSchemaGenerator { return match(type) .with('BooleanLiteral', () => ts.factory.createCallExpression( - ts.factory.createIdentifier('Expression.literal'), + ts.factory.createIdentifier('ExpressionUtils.literal'), undefined, [this.createLiteralNode(value)] ) ) .with('NumberLiteral', () => ts.factory.createCallExpression( - ts.factory.createIdentifier('Expression.literal'), + ts.factory.createIdentifier('ExpressionUtils.literal'), undefined, [ts.factory.createIdentifier(value as string)] ) ) .with('StringLiteral', () => ts.factory.createCallExpression( - ts.factory.createIdentifier('Expression.literal'), + ts.factory.createIdentifier('ExpressionUtils.literal'), undefined, [this.createLiteralNode(value)] ) diff --git a/packages/sdk/tsup.config.ts b/packages/sdk/tsup.config.ts index 5a74a9dd1..4c9aca3e7 100644 --- a/packages/sdk/tsup.config.ts +++ b/packages/sdk/tsup.config.ts @@ -3,6 +3,7 @@ import { defineConfig } from 'tsup'; export default defineConfig({ entry: { index: 'src/index.ts', + schema: 'src/schema/index.ts', }, outDir: 'dist', splitting: false, diff --git a/packages/testtools/package.json b/packages/testtools/package.json index 9996bc595..871b2bca5 100644 --- a/packages/testtools/package.json +++ b/packages/testtools/package.json @@ -6,6 +6,7 @@ "scripts": { "build": "tsup-node", "watch": "tsup-node --watch", + "lint": "eslint src --ext ts", "test": "vitest", "pack": "pnpm pack" }, @@ -30,7 +31,6 @@ "dependencies": { "@types/node": "^18.0.0", "@zenstackhq/language": "workspace:*", - "@zenstackhq/runtime": "workspace:*", "@zenstackhq/sdk": "workspace:*", "glob": "^11.0.2", "tmp": "^0.2.3", diff --git a/packages/testtools/src/schema.ts b/packages/testtools/src/schema.ts index 0eab224e5..5d44daf37 100644 --- a/packages/testtools/src/schema.ts +++ b/packages/testtools/src/schema.ts @@ -1,5 +1,5 @@ -import type { SchemaDef } from '@zenstackhq/runtime/schema'; import { TsSchemaGenerator } from '@zenstackhq/sdk'; +import type { SchemaDef } from '@zenstackhq/sdk/schema'; import { glob } from 'glob'; import { execSync } from 'node:child_process'; import fs from 'node:fs'; @@ -51,11 +51,31 @@ export async function generateTsSchema( const tsPath = path.join(workDir, 'schema.ts'); await generator.generate(zmodelPath, pluginModelFiles, tsPath); - fs.symlinkSync( - path.join(__dirname, '../node_modules'), - path.join(workDir, 'node_modules'), - 'dir' - ); + fs.mkdirSync(path.join(workDir, 'node_modules')); + + // symlink all entries from "node_modules" + const nodeModules = fs.readdirSync(path.join(__dirname, '../node_modules')); + for (const entry of nodeModules) { + if (entry.startsWith('@zenstackhq')) { + continue; + } + fs.symlinkSync( + path.join(__dirname, '../node_modules', entry), + path.join(workDir, 'node_modules', entry), + 'dir' + ); + } + + // in addition, symlink zenstack packages + const zenstackPackages = ['language', 'sdk', 'runtime']; + fs.mkdirSync(path.join(workDir, 'node_modules/@zenstackhq')); + for (const pkg of zenstackPackages) { + fs.symlinkSync( + path.join(__dirname, `../../${pkg}/dist`), + path.join(workDir, `node_modules/@zenstackhq/${pkg}`), + 'dir' + ); + } fs.writeFileSync( path.join(workDir, 'package.json'), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d01c62e4f..716dee3a8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,15 @@ importers: '@swc/core': specifier: ^1.10.15 version: 1.10.15 + '@typescript-eslint/eslint-plugin': + specifier: ~7.3.1 + version: 7.3.1(@typescript-eslint/parser@7.3.1(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(typescript@5.7.3) + '@typescript-eslint/parser': + specifier: ~7.3.1 + version: 7.3.1(eslint@8.57.1)(typescript@5.7.3) + eslint: + specifier: ~8.57.1 + version: 8.57.1 npm-run-all: specifier: ^4.1.5 version: 4.1.5 @@ -112,15 +121,6 @@ importers: '@types/pluralize': specifier: ^0.0.33 version: 0.0.33 - '@typescript-eslint/eslint-plugin': - specifier: ~7.3.1 - version: 7.3.1(@typescript-eslint/parser@7.3.1(eslint@8.57.1)(typescript@5.1.6))(eslint@8.57.1)(typescript@5.1.6) - '@typescript-eslint/parser': - specifier: ~7.3.1 - version: 7.3.1(eslint@8.57.1)(typescript@5.1.6) - eslint: - specifier: ~8.57.0 - version: 8.57.1 langium-cli: specifier: ~3.3.0 version: 3.3.0 @@ -139,6 +139,9 @@ importers: decimal.js: specifier: ^10.4.3 version: 10.4.3 + is-plain-object: + specifier: ^5.0.0 + version: 5.0.0 kysely: specifier: ^0.27.5 version: 0.27.6 @@ -219,6 +222,12 @@ importers: '@types/tmp': specifier: ^0.2.6 version: 0.2.6 + decimal.js: + specifier: ^10.4.3 + version: 10.4.3 + kysely: + specifier: ^0.27.6 + version: 0.27.6 packages/tanstack-query: dependencies: @@ -237,9 +246,6 @@ importers: '@zenstackhq/language': specifier: workspace:* version: link:../language - '@zenstackhq/runtime': - specifier: workspace:* - version: link:../runtime '@zenstackhq/sdk': specifier: workspace:* version: link:../sdk @@ -1833,6 +1839,10 @@ packages: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -3482,13 +3492,13 @@ snapshots: '@types/tmp@0.2.6': {} - '@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.3.1(eslint@8.57.1)(typescript@5.1.6))(eslint@8.57.1)(typescript@5.1.6)': + '@typescript-eslint/eslint-plugin@7.3.1(@typescript-eslint/parser@7.3.1(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(typescript@5.7.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 7.3.1(eslint@8.57.1)(typescript@5.1.6) + '@typescript-eslint/parser': 7.3.1(eslint@8.57.1)(typescript@5.7.3) '@typescript-eslint/scope-manager': 7.3.1 - '@typescript-eslint/type-utils': 7.3.1(eslint@8.57.1)(typescript@5.1.6) - '@typescript-eslint/utils': 7.3.1(eslint@8.57.1)(typescript@5.1.6) + '@typescript-eslint/type-utils': 7.3.1(eslint@8.57.1)(typescript@5.7.3) + '@typescript-eslint/utils': 7.3.1(eslint@8.57.1)(typescript@5.7.3) '@typescript-eslint/visitor-keys': 7.3.1 debug: 4.4.0 eslint: 8.57.1 @@ -3496,22 +3506,22 @@ snapshots: ignore: 5.3.2 natural-compare: 1.4.0 semver: 7.6.3 - ts-api-utils: 1.4.3(typescript@5.1.6) + ts-api-utils: 1.4.3(typescript@5.7.3) optionalDependencies: - typescript: 5.1.6 + typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.3.1(eslint@8.57.1)(typescript@5.1.6)': + '@typescript-eslint/parser@7.3.1(eslint@8.57.1)(typescript@5.7.3)': dependencies: '@typescript-eslint/scope-manager': 7.3.1 '@typescript-eslint/types': 7.3.1 - '@typescript-eslint/typescript-estree': 7.3.1(typescript@5.1.6) + '@typescript-eslint/typescript-estree': 7.3.1(typescript@5.7.3) '@typescript-eslint/visitor-keys': 7.3.1 debug: 4.4.0 eslint: 8.57.1 optionalDependencies: - typescript: 5.1.6 + typescript: 5.7.3 transitivePeerDependencies: - supports-color @@ -3520,21 +3530,21 @@ snapshots: '@typescript-eslint/types': 7.3.1 '@typescript-eslint/visitor-keys': 7.3.1 - '@typescript-eslint/type-utils@7.3.1(eslint@8.57.1)(typescript@5.1.6)': + '@typescript-eslint/type-utils@7.3.1(eslint@8.57.1)(typescript@5.7.3)': dependencies: - '@typescript-eslint/typescript-estree': 7.3.1(typescript@5.1.6) - '@typescript-eslint/utils': 7.3.1(eslint@8.57.1)(typescript@5.1.6) + '@typescript-eslint/typescript-estree': 7.3.1(typescript@5.7.3) + '@typescript-eslint/utils': 7.3.1(eslint@8.57.1)(typescript@5.7.3) debug: 4.4.0 eslint: 8.57.1 - ts-api-utils: 1.4.3(typescript@5.1.6) + ts-api-utils: 1.4.3(typescript@5.7.3) optionalDependencies: - typescript: 5.1.6 + typescript: 5.7.3 transitivePeerDependencies: - supports-color '@typescript-eslint/types@7.3.1': {} - '@typescript-eslint/typescript-estree@7.3.1(typescript@5.1.6)': + '@typescript-eslint/typescript-estree@7.3.1(typescript@5.7.3)': dependencies: '@typescript-eslint/types': 7.3.1 '@typescript-eslint/visitor-keys': 7.3.1 @@ -3543,20 +3553,20 @@ snapshots: is-glob: 4.0.3 minimatch: 9.0.3 semver: 7.6.3 - ts-api-utils: 1.4.3(typescript@5.1.6) + ts-api-utils: 1.4.3(typescript@5.7.3) optionalDependencies: - typescript: 5.1.6 + typescript: 5.7.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@7.3.1(eslint@8.57.1)(typescript@5.1.6)': + '@typescript-eslint/utils@7.3.1(eslint@8.57.1)(typescript@5.7.3)': dependencies: '@eslint-community/eslint-utils': 4.4.1(eslint@8.57.1) '@types/json-schema': 7.0.15 '@types/semver': 7.5.8 '@typescript-eslint/scope-manager': 7.3.1 '@typescript-eslint/types': 7.3.1 - '@typescript-eslint/typescript-estree': 7.3.1(typescript@5.1.6) + '@typescript-eslint/typescript-estree': 7.3.1(typescript@5.7.3) eslint: 8.57.1 semver: 7.6.3 transitivePeerDependencies: @@ -4440,6 +4450,8 @@ snapshots: is-path-inside@3.0.3: {} + is-plain-object@5.0.0: {} + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -5288,9 +5300,9 @@ snapshots: tree-kill@1.2.2: {} - ts-api-utils@1.4.3(typescript@5.1.6): + ts-api-utils@1.4.3(typescript@5.7.3): dependencies: - typescript: 5.1.6 + typescript: 5.7.3 ts-interface-checker@0.1.13: {} diff --git a/samples/blog/zenstack/schema.ts b/samples/blog/zenstack/schema.ts index d4ba702dc..20c5aa80f 100644 --- a/samples/blog/zenstack/schema.ts +++ b/samples/blog/zenstack/schema.ts @@ -3,7 +3,7 @@ // This file is automatically generated by ZenStack CLI and should not be manually updated. // ////////////////////////////////////////////////////////////////////////////////////////////// -import { type SchemaDef, type OperandExpression, Expression } from "@zenstackhq/runtime/schema"; +import { type SchemaDef, type OperandExpression, ExpressionUtils } from "@zenstackhq/runtime/schema"; import path from "node:path"; import url from "node:url"; import { toDialectConfig } from "@zenstackhq/runtime/utils/sqlite-utils"; @@ -20,13 +20,13 @@ export const schema = { id: { type: "String", id: true, - attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: Expression.call("cuid") }] }], - default: Expression.call("cuid") + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }], + default: ExpressionUtils.call("cuid") }, createdAt: { type: "DateTime", - attributes: [{ name: "@default", args: [{ name: "value", value: Expression.call("now") }] }], - default: Expression.call("now") + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }], + default: ExpressionUtils.call("now") }, updatedAt: { type: "DateTime", @@ -49,7 +49,7 @@ export const schema = { }, role: { type: "Role", - attributes: [{ name: "@default", args: [{ name: "value", value: Expression.literal("USER") }] }], + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal("USER") }] }], default: "USER" }, posts: { @@ -79,8 +79,8 @@ export const schema = { id: { type: "String", id: true, - attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: Expression.call("cuid") }] }], - default: Expression.call("cuid") + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }], + default: ExpressionUtils.call("cuid") }, bio: { type: "String", @@ -93,7 +93,7 @@ export const schema = { user: { type: "User", optional: true, - attributes: [{ name: "@relation", args: [{ name: "fields", value: Expression.array([Expression.field("userId")]) }, { name: "references", value: Expression.array([Expression.field("id")]) }] }], + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array([ExpressionUtils.field("userId")]) }, { name: "references", value: ExpressionUtils.array([ExpressionUtils.field("id")]) }] }], relation: { opposite: "profile", fields: ["userId"], references: ["id"] } }, userId: { @@ -117,13 +117,13 @@ export const schema = { id: { type: "String", id: true, - attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: Expression.call("cuid") }] }], - default: Expression.call("cuid") + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("cuid") }] }], + default: ExpressionUtils.call("cuid") }, createdAt: { type: "DateTime", - attributes: [{ name: "@default", args: [{ name: "value", value: Expression.call("now") }] }], - default: Expression.call("now") + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.call("now") }] }], + default: ExpressionUtils.call("now") }, updatedAt: { type: "DateTime", @@ -138,12 +138,12 @@ export const schema = { }, published: { type: "Boolean", - attributes: [{ name: "@default", args: [{ name: "value", value: Expression.literal(false) }] }], + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal(false) }] }], default: false }, author: { type: "User", - attributes: [{ name: "@relation", args: [{ name: "fields", value: Expression.array([Expression.field("authorId")]) }, { name: "references", value: Expression.array([Expression.field("id")]) }] }], + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array([ExpressionUtils.field("authorId")]) }, { name: "references", value: ExpressionUtils.array([ExpressionUtils.field("id")]) }] }], relation: { opposite: "posts", fields: ["authorId"], references: ["id"] } }, authorId: {