From 930d9965acdbb5f1635b98bd9528b73805157b9d Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Sun, 8 Mar 2026 23:11:23 -0700 Subject: [PATCH 1/4] fix(policy): support now() default value in access policy evaluation - Fill now() default in evalGenerator so createdAt fields are populated before policy checks, preventing DefaultInsertValueNode from being treated as null during pre-create policy evaluation. - Fix now() SQL function to produce ISO 8601 format matching each dialect's DateTime storage format (SQLite: strftime, MySQL: DATE_FORMAT with trimmed microseconds), ensuring correct comparisons in policy expressions. - Add e2e tests for now() in create, read, update, and delete policies. Co-Authored-By: Claude Opus 4.6 --- .../orm/src/client/crud/operations/base.ts | 1 + packages/orm/src/client/functions.ts | 14 +- tests/e2e/orm/policy/now-function.test.ts | 125 ++++++++++++++++++ 3 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 tests/e2e/orm/policy/now-function.test.ts diff --git a/packages/orm/src/client/crud/operations/base.ts b/packages/orm/src/client/crud/operations/base.ts index e38ca4369..a12f0fe2a 100644 --- a/packages/orm/src/client/crud/operations/base.ts +++ b/packages/orm/src/client/crud/operations/base.ts @@ -1105,6 +1105,7 @@ export abstract class BaseOperationHandler { return this.formatGeneratedValue(generated, defaultValue.args?.[1]); }) .with('ulid', () => this.formatGeneratedValue(ulid(), defaultValue.args?.[0])) + .with('now', () => new Date()) .otherwise(() => undefined); } else if ( ExpressionUtils.isMember(defaultValue) && diff --git a/packages/orm/src/client/functions.ts b/packages/orm/src/client/functions.ts index 25af1e723..058410b31 100644 --- a/packages/orm/src/client/functions.ts +++ b/packages/orm/src/client/functions.ts @@ -109,7 +109,19 @@ export const isEmpty: ZModelFunction = (eb, args, { dialect }: ZModelFuncti return eb(dialect.buildArrayLength(field), '=', sql.lit(0)); }; -export const now: ZModelFunction = () => sql.raw('CURRENT_TIMESTAMP'); +export const now: ZModelFunction = (_eb, _args, context) => + match(context.dialect.provider) + // SQLite stores DateTime as ISO 8601 text ('YYYY-MM-DDTHH:MM:SS.sssZ'), but + // CURRENT_TIMESTAMP returns 'YYYY-MM-DD HH:MM:SS'. Use strftime for ISO format. + .with('sqlite', () => sql.raw("strftime('%Y-%m-%dT%H:%M:%fZ')")) + // MySQL stores DateTime as ISO 8601 text ('YYYY-MM-DDTHH:MM:SS.sss+00:00'). + // Use CONCAT + SUBSTRING to produce matching 3-digit millisecond ISO format. + .with('mysql', () => + sql.raw("CONCAT(SUBSTRING(DATE_FORMAT(UTC_TIMESTAMP(3), '%Y-%m-%dT%H:%i:%s.%f'), 1, 23), '+00:00')"), + ) + // PostgreSQL has native timestamp type that compares correctly. + .with('postgresql', () => sql.raw('CURRENT_TIMESTAMP')) + .exhaustive(); export const currentModel: ZModelFunction = (_eb, args, { model }: ZModelFunctionContext) => { let result = model; diff --git a/tests/e2e/orm/policy/now-function.test.ts b/tests/e2e/orm/policy/now-function.test.ts new file mode 100644 index 000000000..ae5f51e8e --- /dev/null +++ b/tests/e2e/orm/policy/now-function.test.ts @@ -0,0 +1,125 @@ +import { createPolicyTestClient } from '@zenstackhq/testtools'; +import { describe, expect, it } from 'vitest'; + +describe('now() function in policy tests', () => { + it('allows create when createdAt default now() is used in comparison', async () => { + const db = await createPolicyTestClient( + ` +model Post { + id Int @id @default(autoincrement()) + title String + createdAt DateTime? @default(now()) + @@allow('create', createdAt != null) + @@allow('read', true) +} +`, + ); + + // createdAt should be auto-filled with now(), satisfying the <= now() check + await expect(db.post.create({ data: { title: 'hello' } })).resolves.toMatchObject({ title: 'hello' }); + const post = await db.post.findFirst(); + expect(post.createdAt).toBeInstanceOf(Date); + }); + + it('uses now() in update policy to compare against DateTime field', async () => { + const db = await createPolicyTestClient( + ` +model Event { + id Int @id @default(autoincrement()) + name String + scheduledAt DateTime + @@allow('create,read', true) + @@allow('update', scheduledAt > now()) +} +`, + ); + + // create an event in the future - should be updatable + const futureDate = new Date(Date.now() + 60 * 60 * 1000); + await db.event.create({ data: { name: 'future', scheduledAt: futureDate } }); + await expect(db.event.update({ where: { id: 1 }, data: { name: 'updated' } })).resolves.toMatchObject({ + name: 'updated', + }); + + // create an event in the past - should NOT be updatable + const pastDate = new Date(Date.now() - 60 * 60 * 1000); + await db.event.create({ data: { name: 'past', scheduledAt: pastDate } }); + await expect(db.event.update({ where: { id: 2 }, data: { name: 'updated' } })).toBeRejectedNotFound(); + }); + + it('uses now() in read policy to filter DateTime field', async () => { + const db = await createPolicyTestClient( + ` +model Article { + id Int @id @default(autoincrement()) + title String + publishedAt DateTime + @@allow('create', true) + @@allow('read', publishedAt <= now()) +} +`, + ); + + const rawDb = db.$unuseAll(); + const pastDate = new Date(Date.now() - 60 * 60 * 1000); + const futureDate = new Date(Date.now() + 60 * 60 * 1000); + + await rawDb.article.create({ data: { title: 'published', publishedAt: pastDate } }); + await rawDb.article.create({ data: { title: 'scheduled', publishedAt: futureDate } }); + + // only the past article should be readable + const articles = await db.article.findMany(); + expect(articles).toHaveLength(1); + expect(articles[0].title).toBe('published'); + }); + + it('uses now() in delete policy', async () => { + const db = await createPolicyTestClient( + ` +model Task { + id Int @id @default(autoincrement()) + name String + expiresAt DateTime + @@allow('create,read', true) + @@allow('delete', expiresAt < now()) +} +`, + ); + + // create an expired task - should be deletable + const pastDate = new Date(Date.now() - 60 * 60 * 1000); + await db.task.create({ data: { name: 'expired', expiresAt: pastDate } }); + await expect(db.task.delete({ where: { id: 1 } })).resolves.toMatchObject({ name: 'expired' }); + + // create a non-expired task - should NOT be deletable + const futureDate = new Date(Date.now() + 60 * 60 * 1000); + await db.task.create({ data: { name: 'active', expiresAt: futureDate } }); + await expect(db.task.delete({ where: { id: 2 } })).toBeRejectedNotFound(); + }); + + it('combines now() default with auth in create policy', async () => { + const db = await createPolicyTestClient( + ` +type Auth { + id Int + @@auth +} + +model Log { + id Int @id @default(autoincrement()) + message String + createdAt DateTime @default(now()) + @@allow('create', createdAt <= now() && auth() != null) + @@allow('read', true) +} +`, + ); + + // anonymous user - rejected + await expect(db.log.create({ data: { message: 'test' } })).toBeRejectedByPolicy(); + // authenticated user with auto-filled createdAt - allowed + await expect(db.$setAuth({ id: 1 }).log.create({ data: { message: 'test' } })).resolves.toMatchObject({ + message: 'test', + }); + }); +}); From 31776a890a68c027a1b86eae223d24914955da22 Mon Sep 17 00:00:00 2001 From: Yiming Cao Date: Wed, 11 Mar 2026 10:57:21 -0700 Subject: [PATCH 2/4] fix(orm): use IS operator for null comparisons in filters (#2475) Co-authored-by: Claude Opus 4.6 --- .../src/client/crud/dialects/base-dialect.ts | 18 +++++-- tests/e2e/orm/client-api/filter.test.ts | 24 +++++++++ tests/regression/test/issue-2472.test.ts | 53 +++++++++++++++++++ 3 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 tests/regression/test/issue-2472.test.ts diff --git a/packages/orm/src/client/crud/dialects/base-dialect.ts b/packages/orm/src/client/crud/dialects/base-dialect.ts index 1f5102121..e38382f1f 100644 --- a/packages/orm/src/client/crud/dialects/base-dialect.ts +++ b/packages/orm/src/client/crud/dialects/base-dialect.ts @@ -757,11 +757,21 @@ export abstract class BaseCrudDialect { } protected buildJsonEqualityFilter(lhs: Expression, rhs: unknown) { - return this.buildLiteralFilter(lhs, 'Json', rhs); + return this.buildValueFilter(lhs, 'Json', rhs); } - private buildLiteralFilter(lhs: Expression, type: BuiltinType, rhs: unknown) { - return this.eb(lhs, '=', rhs !== null && rhs !== undefined ? this.transformInput(rhs, type, false) : rhs); + private buildValueFilter(lhs: Expression, type: BuiltinType, rhs: unknown) { + if (rhs === undefined) { + // undefined filter is no-op, always true + return this.true(); + } + + if (rhs === null) { + // null comparison + return this.eb(lhs, 'is', null); + } + + return this.eb(lhs, '=', this.transformInput(rhs, type, false)); } private buildStandardFilter( @@ -776,7 +786,7 @@ export abstract class BaseCrudDialect { ) { if (payload === null || !isPlainObject(payload)) { return { - conditions: [this.buildLiteralFilter(lhs, type, payload)], + conditions: [this.buildValueFilter(lhs, type, payload)], consumedKeys: [], }; } diff --git a/tests/e2e/orm/client-api/filter.test.ts b/tests/e2e/orm/client-api/filter.test.ts index d4594ab75..36173ca2d 100644 --- a/tests/e2e/orm/client-api/filter.test.ts +++ b/tests/e2e/orm/client-api/filter.test.ts @@ -414,6 +414,18 @@ describe('Client filter tests ', () => { where: { email: { not: { not: { contains: 'test' } } } }, }), ).toResolveTruthy(); + + // not null (issue #2472) + await expect( + client.user.findMany({ + where: { name: { not: null } }, + }), + ).toResolveWithLength(1); + await expect( + client.user.findFirst({ + where: { name: { not: null } }, + }), + ).resolves.toMatchObject({ id: user1.id }); }); it('supports numeric filters', async () => { @@ -490,6 +502,18 @@ describe('Client filter tests ', () => { where: { age: { not: { not: { equals: null } } } }, }), ).toResolveTruthy(); + + // not null shorthand (issue #2472) + await expect( + client.profile.findMany({ + where: { age: { not: null } }, + }), + ).toResolveWithLength(1); + await expect( + client.profile.findFirst({ + where: { age: { not: null } }, + }), + ).resolves.toMatchObject({ id: '1' }); }); it('supports boolean filters', async () => { diff --git a/tests/regression/test/issue-2472.test.ts b/tests/regression/test/issue-2472.test.ts new file mode 100644 index 000000000..c4389069f --- /dev/null +++ b/tests/regression/test/issue-2472.test.ts @@ -0,0 +1,53 @@ +import { createTestClient } from '@zenstackhq/testtools'; +import { describe, expect, it } from 'vitest'; + +// https://github.com/zenstackhq/zenstack/issues/2472 +// Filtering by `{ not: null }` returns empty array instead of non-null records +describe('Regression for issue 2472', () => { + const schema = ` +model Post { + id Int @id @default(autoincrement()) + title String + published_at DateTime? +} + `; + + it('should filter records where nullable field is not null', async () => { + const db = await createTestClient(schema); + + await db.post.create({ data: { title: 'published', published_at: new Date('2025-01-01') } }); + await db.post.create({ data: { title: 'draft' } }); + + // { not: null } should return only records where the field is NOT NULL + const results = await db.post.findMany({ + where: { + published_at: { + not: null, + }, + }, + }); + + expect(results).toHaveLength(1); + expect(results[0].title).toBe('published'); + }); + + it('should also work with { not: null } on string fields', async () => { + const db = await createTestClient(` +model Item { + id Int @id @default(autoincrement()) + name String + note String? +} + `); + + await db.item.create({ data: { name: 'a', note: 'has note' } }); + await db.item.create({ data: { name: 'b' } }); + + const results = await db.item.findMany({ + where: { note: { not: null } }, + }); + + expect(results).toHaveLength(1); + expect(results[0].name).toBe('a'); + }); +}); From 2c32dc329e65fc114b3d534a707d15573ba9ed9c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 11 Mar 2026 11:40:16 -0700 Subject: [PATCH 3/4] [CI] Bump version 3.4.6 (#2477) Co-authored-by: ymc9 <104139426+ymc9@users.noreply.github.com> --- package.json | 2 +- packages/auth-adapters/better-auth/package.json | 2 +- packages/cli/package.json | 2 +- packages/clients/client-helpers/package.json | 2 +- packages/clients/tanstack-query/package.json | 2 +- packages/common-helpers/package.json | 2 +- packages/config/eslint-config/package.json | 2 +- packages/config/typescript-config/package.json | 2 +- packages/config/vitest-config/package.json | 2 +- packages/create-zenstack/package.json | 2 +- packages/ide/vscode/package.json | 2 +- packages/language/package.json | 2 +- packages/orm/package.json | 2 +- packages/plugins/policy/package.json | 2 +- packages/schema/package.json | 2 +- packages/sdk/package.json | 2 +- packages/server/package.json | 2 +- packages/testtools/package.json | 2 +- packages/zod/package.json | 2 +- samples/orm/package.json | 2 +- tests/e2e/package.json | 2 +- tests/regression/package.json | 2 +- tests/runtimes/bun/package.json | 2 +- tests/runtimes/edge-runtime/package.json | 2 +- 24 files changed, 24 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index 9655ac03d..419fa65b4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zenstack-v3", - "version": "3.4.5", + "version": "3.4.6", "description": "ZenStack", "packageManager": "pnpm@10.23.0", "type": "module", diff --git a/packages/auth-adapters/better-auth/package.json b/packages/auth-adapters/better-auth/package.json index b1656e41c..56e06ce41 100644 --- a/packages/auth-adapters/better-auth/package.json +++ b/packages/auth-adapters/better-auth/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/better-auth", - "version": "3.4.5", + "version": "3.4.6", "description": "ZenStack Better Auth Adapter. This adapter is modified from better-auth's Prisma adapter.", "type": "module", "scripts": { diff --git a/packages/cli/package.json b/packages/cli/package.json index 1391e33a5..b1bdb6b05 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -3,7 +3,7 @@ "publisher": "zenstack", "displayName": "ZenStack CLI", "description": "FullStack database toolkit with built-in access control and automatic API generation.", - "version": "3.4.5", + "version": "3.4.6", "type": "module", "author": { "name": "ZenStack Team" diff --git a/packages/clients/client-helpers/package.json b/packages/clients/client-helpers/package.json index a8da05dbc..384a8915e 100644 --- a/packages/clients/client-helpers/package.json +++ b/packages/clients/client-helpers/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/client-helpers", - "version": "3.4.5", + "version": "3.4.6", "description": "Helpers for implementing clients that consume ZenStack's CRUD service", "type": "module", "scripts": { diff --git a/packages/clients/tanstack-query/package.json b/packages/clients/tanstack-query/package.json index 335e71bcb..f3fa224ac 100644 --- a/packages/clients/tanstack-query/package.json +++ b/packages/clients/tanstack-query/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/tanstack-query", - "version": "3.4.5", + "version": "3.4.6", "description": "TanStack Query Client for consuming ZenStack v3's CRUD service", "type": "module", "scripts": { diff --git a/packages/common-helpers/package.json b/packages/common-helpers/package.json index 4d07aa1c8..35bad22e4 100644 --- a/packages/common-helpers/package.json +++ b/packages/common-helpers/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/common-helpers", - "version": "3.4.5", + "version": "3.4.6", "description": "ZenStack Common Helpers", "type": "module", "scripts": { diff --git a/packages/config/eslint-config/package.json b/packages/config/eslint-config/package.json index 2a6274263..ca574596d 100644 --- a/packages/config/eslint-config/package.json +++ b/packages/config/eslint-config/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/eslint-config", - "version": "3.4.5", + "version": "3.4.6", "type": "module", "private": true, "license": "MIT" diff --git a/packages/config/typescript-config/package.json b/packages/config/typescript-config/package.json index 64977930a..b3277c1d6 100644 --- a/packages/config/typescript-config/package.json +++ b/packages/config/typescript-config/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/typescript-config", - "version": "3.4.5", + "version": "3.4.6", "private": true, "license": "MIT" } diff --git a/packages/config/vitest-config/package.json b/packages/config/vitest-config/package.json index a302ad4a4..7d085f782 100644 --- a/packages/config/vitest-config/package.json +++ b/packages/config/vitest-config/package.json @@ -1,7 +1,7 @@ { "name": "@zenstackhq/vitest-config", "type": "module", - "version": "3.4.5", + "version": "3.4.6", "private": true, "license": "MIT", "exports": { diff --git a/packages/create-zenstack/package.json b/packages/create-zenstack/package.json index b45cc8a10..221d756c6 100644 --- a/packages/create-zenstack/package.json +++ b/packages/create-zenstack/package.json @@ -1,6 +1,6 @@ { "name": "create-zenstack", - "version": "3.4.5", + "version": "3.4.6", "description": "Create a new ZenStack project", "type": "module", "scripts": { diff --git a/packages/ide/vscode/package.json b/packages/ide/vscode/package.json index 41c420fb1..b877a5575 100644 --- a/packages/ide/vscode/package.json +++ b/packages/ide/vscode/package.json @@ -1,7 +1,7 @@ { "name": "zenstack-v3", "publisher": "zenstack", - "version": "3.4.5", + "version": "3.4.6", "displayName": "ZenStack V3 Language Tools", "description": "VSCode extension for ZenStack (v3) ZModel language", "private": true, diff --git a/packages/language/package.json b/packages/language/package.json index 85599be25..4dfba58c5 100644 --- a/packages/language/package.json +++ b/packages/language/package.json @@ -1,7 +1,7 @@ { "name": "@zenstackhq/language", "description": "ZenStack ZModel language specification", - "version": "3.4.5", + "version": "3.4.6", "license": "MIT", "author": "ZenStack Team", "files": [ diff --git a/packages/orm/package.json b/packages/orm/package.json index ca38f2849..ed1a1d826 100644 --- a/packages/orm/package.json +++ b/packages/orm/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/orm", - "version": "3.4.5", + "version": "3.4.6", "description": "ZenStack ORM", "type": "module", "scripts": { diff --git a/packages/plugins/policy/package.json b/packages/plugins/policy/package.json index 0f9a0c8b5..712aa1a9f 100644 --- a/packages/plugins/policy/package.json +++ b/packages/plugins/policy/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/plugin-policy", - "version": "3.4.5", + "version": "3.4.6", "description": "ZenStack Policy Plugin", "type": "module", "scripts": { diff --git a/packages/schema/package.json b/packages/schema/package.json index 16c77054f..eeee4da61 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/schema", - "version": "3.4.5", + "version": "3.4.6", "description": "ZenStack Runtime Schema", "type": "module", "scripts": { diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 6dcdb9948..5275702ce 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/sdk", - "version": "3.4.5", + "version": "3.4.6", "description": "ZenStack SDK", "type": "module", "scripts": { diff --git a/packages/server/package.json b/packages/server/package.json index aed1b34e9..1f2acd1ce 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/server", - "version": "3.4.5", + "version": "3.4.6", "description": "ZenStack automatic CRUD API handlers and server adapters", "type": "module", "scripts": { diff --git a/packages/testtools/package.json b/packages/testtools/package.json index 83ec62bd0..db5387253 100644 --- a/packages/testtools/package.json +++ b/packages/testtools/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/testtools", - "version": "3.4.5", + "version": "3.4.6", "description": "ZenStack Test Tools", "type": "module", "scripts": { diff --git a/packages/zod/package.json b/packages/zod/package.json index 5da1cb6af..8b9cf703b 100644 --- a/packages/zod/package.json +++ b/packages/zod/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/zod", - "version": "3.4.5", + "version": "3.4.6", "description": "ZenStack Zod integration", "type": "module", "scripts": { diff --git a/samples/orm/package.json b/samples/orm/package.json index 05dfbba1c..68c776325 100644 --- a/samples/orm/package.json +++ b/samples/orm/package.json @@ -1,6 +1,6 @@ { "name": "sample-orm", - "version": "3.4.5", + "version": "3.4.6", "description": "", "main": "index.js", "private": true, diff --git a/tests/e2e/package.json b/tests/e2e/package.json index 29ea61baf..a705d293d 100644 --- a/tests/e2e/package.json +++ b/tests/e2e/package.json @@ -1,6 +1,6 @@ { "name": "e2e", - "version": "3.4.5", + "version": "3.4.6", "private": true, "type": "module", "scripts": { diff --git a/tests/regression/package.json b/tests/regression/package.json index cdd511a7e..31e519a90 100644 --- a/tests/regression/package.json +++ b/tests/regression/package.json @@ -1,6 +1,6 @@ { "name": "regression", - "version": "3.4.5", + "version": "3.4.6", "private": true, "type": "module", "scripts": { diff --git a/tests/runtimes/bun/package.json b/tests/runtimes/bun/package.json index 9c2bce7e6..650efd1b0 100644 --- a/tests/runtimes/bun/package.json +++ b/tests/runtimes/bun/package.json @@ -1,6 +1,6 @@ { "name": "bun-e2e", - "version": "3.4.5", + "version": "3.4.6", "private": true, "type": "module", "scripts": { diff --git a/tests/runtimes/edge-runtime/package.json b/tests/runtimes/edge-runtime/package.json index 52b9009f1..363767089 100644 --- a/tests/runtimes/edge-runtime/package.json +++ b/tests/runtimes/edge-runtime/package.json @@ -1,6 +1,6 @@ { "name": "edge-runtime-e2e", - "version": "3.4.5", + "version": "3.4.6", "private": true, "type": "module", "scripts": { From d4fbb3860926f375002f8302a15d3865d7d53a1d Mon Sep 17 00:00:00 2001 From: Yiming Cao Date: Wed, 11 Mar 2026 11:51:25 -0700 Subject: [PATCH 4/4] fix(orm): add webpack magic comment to suppress bundler warnings for optional dependencies (#2476) --- packages/orm/src/client/crud/dialects/postgresql.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/orm/src/client/crud/dialects/postgresql.ts b/packages/orm/src/client/crud/dialects/postgresql.ts index 5f962dbb5..67d429de8 100644 --- a/packages/orm/src/client/crud/dialects/postgresql.ts +++ b/packages/orm/src/client/crud/dialects/postgresql.ts @@ -37,7 +37,7 @@ export class PostgresCrudDialect extends LateralJoinDi // override node-pg's default type parser to resolve the timezone handling issue // with "TIMESTAMP WITHOUT TIME ZONE" fields // https://github.com/brianc/node-postgres/issues/429 - import('pg') + import(/* webpackIgnore: true */ 'pg') // suppress bundler analysis warnings .then((pg) => { pg.types.setTypeParser(pg.types.builtins.TIMESTAMP, (value) => { if (typeof value !== 'string') {