mirror of
https://github.com/expo/entity.git
synced 2026-11-13 20:28:06 -10:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c692afbcb |
No files matched your search
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
env:
|
||||
# renovate: datasource=github-releases depName=jdx/mise
|
||||
MISE_VERSION: v2026.10.19
|
||||
MISE_VERSION: v2026.9.7
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
+1
-1
@@ -4,4 +4,4 @@ npmMinimalAgeGate: 2d
|
||||
|
||||
npmPreapprovedPackages:
|
||||
- '@expo/*'
|
||||
- oxlint-config-universe
|
||||
- 'oxlint-config-universe'
|
||||
@@ -3,24 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [0.72.0](https://github.com/expo/entity/compare/v0.71.0...v0.72.0) (2026-10-29)
|
||||
|
||||
### Features
|
||||
|
||||
- add notAuthorizedEntity to getUserFacingDenialReason ([#732](https://github.com/expo/entity/issues/732)) ([24109be](https://github.com/expo/entity/commit/24109bec32c03b228bd35cef8bd6a379dcf7277b))
|
||||
|
||||
# [0.71.0](https://github.com/expo/entity/compare/v0.70.0...v0.71.0) (2026-10-09)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- declare runtime dependencies imported by published packages ([#717](https://github.com/expo/entity/issues/717)) ([3c96f3b](https://github.com/expo/entity/commit/3c96f3b9ea2b097c979447354f0abe35ed5e8d57))
|
||||
|
||||
# [0.70.0](https://github.com/expo/entity/compare/v0.69.0...v0.70.0) (2026-10-05)
|
||||
|
||||
### Features
|
||||
|
||||
- add transient error for PostgreSQL lock timeouts ([#716](https://github.com/expo/entity/issues/716)) ([13ce74e](https://github.com/expo/entity/commit/13ce74e93aec0a0b0b84edd1e1e89d785a3dbf46)), closes [#714](https://github.com/expo/entity/issues/714) [#715](https://github.com/expo/entity/issues/715)
|
||||
|
||||
# [0.69.0](https://github.com/expo/entity/compare/v0.68.0...v0.69.0) (2026-09-29)
|
||||
|
||||
### Features
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"packages": ["packages/*"],
|
||||
"npmClient": "yarn",
|
||||
"version": "0.72.0",
|
||||
"version": "0.69.0",
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
[tools]
|
||||
node = "24.22.1"
|
||||
node = "24.21.0"
|
||||
|
||||
[tasks.yarn]
|
||||
run = "yarn install --immutable"
|
||||
|
||||
+6
-6
@@ -23,20 +23,20 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsconfig/bases": "1.0.28",
|
||||
"@types/node": "24.19.2",
|
||||
"@types/node": "24.19.1",
|
||||
"ctix": "2.8.2",
|
||||
"jest": "30.5.2",
|
||||
"jest": "30.5.1",
|
||||
"lerna": "9.0.7",
|
||||
"oxfmt": "0.73.0",
|
||||
"oxlint": "1.88.0",
|
||||
"oxfmt": "0.67.0",
|
||||
"oxlint": "1.82.0",
|
||||
"oxlint-config-universe": "0.2.0",
|
||||
"oxlint-tsgolint": "0.25.0",
|
||||
"prettier": "3.9.10",
|
||||
"prettier": "3.9.6",
|
||||
"typedoc": "0.28.20",
|
||||
"typescript": "6.0.3"
|
||||
},
|
||||
"resolutions": {
|
||||
"collect-v8-coverage": "^1.0.3"
|
||||
},
|
||||
"packageManager": "[email protected]8.1+sha512.b2e1e7524f654f2749d32b4ebcb4622473cb5bcbc485df2007e12a154e50162a4d795526768bc5f5b8f81717bfd79deb2472813d86fb5ae2eb551fa9c872b08f"
|
||||
"packageManager": "[email protected]3.0+sha512.5c20ba010c99815433e5c8453112165e673f1c7948d8d2b267f4b5e52097538658388ebc9f9580656d9b75c5cc996f990f611f99304a2197d4c56d21eea370e7"
|
||||
}
|
||||
@@ -3,18 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [0.72.0](https://github.com/expo/entity/compare/v0.71.0...v0.72.0) (2026-10-29)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-cache-adapter-local-memory
|
||||
|
||||
# [0.71.0](https://github.com/expo/entity/compare/v0.70.0...v0.71.0) (2026-10-09)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-cache-adapter-local-memory
|
||||
|
||||
# [0.70.0](https://github.com/expo/entity/compare/v0.69.0...v0.70.0) (2026-10-05)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-cache-adapter-local-memory
|
||||
|
||||
# [0.69.0](https://github.com/expo/entity/compare/v0.68.0...v0.69.0) (2026-09-29)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-cache-adapter-local-memory
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/entity-cache-adapter-local-memory",
|
||||
"version": "0.72.0",
|
||||
"version": "0.69.0",
|
||||
"description": "Cross-request local memory cache adapter for @expo/entity",
|
||||
"keywords": [
|
||||
"entity"
|
||||
@@ -31,7 +31,7 @@
|
||||
"devDependencies": {
|
||||
"@expo/entity-testing-utils": "workspace:^",
|
||||
"@isaacs/ttlcache": "2.1.5",
|
||||
"@jest/globals": "30.5.2",
|
||||
"@jest/globals": "30.5.1",
|
||||
"typescript": "6.0.3"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -3,18 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [0.72.0](https://github.com/expo/entity/compare/v0.71.0...v0.72.0) (2026-10-29)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-cache-adapter-redis
|
||||
|
||||
# [0.71.0](https://github.com/expo/entity/compare/v0.70.0...v0.71.0) (2026-10-09)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-cache-adapter-redis
|
||||
|
||||
# [0.70.0](https://github.com/expo/entity/compare/v0.69.0...v0.70.0) (2026-10-05)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-cache-adapter-redis
|
||||
|
||||
# [0.69.0](https://github.com/expo/entity/compare/v0.68.0...v0.69.0) (2026-09-29)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-cache-adapter-redis
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/entity-cache-adapter-redis",
|
||||
"version": "0.72.0",
|
||||
"version": "0.69.0",
|
||||
"description": "Redis cache adapter for @expo/entity",
|
||||
"keywords": [
|
||||
"entity"
|
||||
@@ -31,7 +31,7 @@
|
||||
"devDependencies": {
|
||||
"@expo/batcher": "1.1.2",
|
||||
"@expo/entity-testing-utils": "workspace:^",
|
||||
"@jest/globals": "30.5.2",
|
||||
"@jest/globals": "30.5.1",
|
||||
"ioredis": "5.11.1",
|
||||
"ts-mockito": "2.6.1",
|
||||
"typescript": "6.0.3"
|
||||
|
||||
@@ -3,18 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [0.72.0](https://github.com/expo/entity/compare/v0.71.0...v0.72.0) (2026-10-29)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-codemod
|
||||
|
||||
# [0.71.0](https://github.com/expo/entity/compare/v0.70.0...v0.71.0) (2026-10-09)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-codemod
|
||||
|
||||
# [0.70.0](https://github.com/expo/entity/compare/v0.69.0...v0.70.0) (2026-10-05)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-codemod
|
||||
|
||||
# [0.69.0](https://github.com/expo/entity/compare/v0.68.0...v0.69.0) (2026-09-29)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-codemod
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/entity-codemod",
|
||||
"version": "0.72.0",
|
||||
"version": "0.69.0",
|
||||
"description": "jscodeshift codemods for @expo/entity upgrades",
|
||||
"keywords": [
|
||||
"entity"
|
||||
@@ -25,9 +25,9 @@
|
||||
"test": "yarn test:all --rootDir $(pwd)"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jest/globals": "30.5.2",
|
||||
"@jest/globals": "30.5.1",
|
||||
"@types/jscodeshift": "17.3.0",
|
||||
"@types/node": "24.19.2",
|
||||
"@types/node": "24.19.1",
|
||||
"jscodeshift": "17.4.0",
|
||||
"typescript": "6.0.3"
|
||||
},
|
||||
|
||||
@@ -3,20 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [0.72.0](https://github.com/expo/entity/compare/v0.71.0...v0.72.0) (2026-10-29)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-database-adapter-knex-testing-utils
|
||||
|
||||
# [0.71.0](https://github.com/expo/entity/compare/v0.70.0...v0.71.0) (2026-10-09)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- declare runtime dependencies imported by published packages ([#717](https://github.com/expo/entity/issues/717)) ([3c96f3b](https://github.com/expo/entity/commit/3c96f3b9ea2b097c979447354f0abe35ed5e8d57))
|
||||
|
||||
# [0.70.0](https://github.com/expo/entity/compare/v0.69.0...v0.70.0) (2026-10-05)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-database-adapter-knex-testing-utils
|
||||
|
||||
# [0.69.0](https://github.com/expo/entity/compare/v0.68.0...v0.69.0) (2026-09-29)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-database-adapter-knex-testing-utils
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/entity-database-adapter-knex-testing-utils",
|
||||
"version": "0.72.0",
|
||||
"version": "0.69.0",
|
||||
"description": "Testing utilities for applications using Entity with Knex database adapter",
|
||||
"keywords": [
|
||||
"entity"
|
||||
@@ -27,14 +27,13 @@
|
||||
"dependencies": {
|
||||
"@expo/entity": "workspace:^",
|
||||
"@expo/entity-database-adapter-knex": "workspace:^",
|
||||
"@expo/entity-testing-utils": "workspace:^",
|
||||
"invariant": "^2.2.4",
|
||||
"uuid": "^14.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jest/globals": "30.5.2",
|
||||
"@jest/globals": "30.5.1",
|
||||
"@types/invariant": "2.2.37",
|
||||
"@types/node": "24.19.2",
|
||||
"@types/node": "24.19.1",
|
||||
"typescript": "6.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -3,22 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [0.72.0](https://github.com/expo/entity/compare/v0.71.0...v0.72.0) (2026-10-29)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-database-adapter-knex
|
||||
|
||||
# [0.71.0](https://github.com/expo/entity/compare/v0.70.0...v0.71.0) (2026-10-09)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- declare runtime dependencies imported by published packages ([#717](https://github.com/expo/entity/issues/717)) ([3c96f3b](https://github.com/expo/entity/commit/3c96f3b9ea2b097c979447354f0abe35ed5e8d57))
|
||||
|
||||
# [0.70.0](https://github.com/expo/entity/compare/v0.69.0...v0.70.0) (2026-10-05)
|
||||
|
||||
### Features
|
||||
|
||||
- add transient error for PostgreSQL lock timeouts ([#716](https://github.com/expo/entity/issues/716)) ([13ce74e](https://github.com/expo/entity/commit/13ce74e93aec0a0b0b84edd1e1e89d785a3dbf46)), closes [#714](https://github.com/expo/entity/issues/714) [#715](https://github.com/expo/entity/issues/715)
|
||||
|
||||
# [0.69.0](https://github.com/expo/entity/compare/v0.68.0...v0.69.0) (2026-09-29)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/entity-database-adapter-knex",
|
||||
"version": "0.72.0",
|
||||
"version": "0.69.0",
|
||||
"description": "Knex database adapter for @expo/entity",
|
||||
"keywords": [
|
||||
"entity"
|
||||
@@ -27,12 +27,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/entity": "workspace:^",
|
||||
"@expo/results": "^1.0.0",
|
||||
"knex": "^3.2.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@expo/entity-testing-utils": "workspace:^",
|
||||
"@jest/globals": "30.5.2",
|
||||
"@jest/globals": "30.5.1",
|
||||
"pg": "8.23.1",
|
||||
"ts-mockito": "2.6.1",
|
||||
"typescript": "6.0.3"
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
import { EntityDatabaseAdapterUniqueConstraintError, ViewerContext } from '@expo/entity';
|
||||
import {
|
||||
EntityDatabaseAdapterCheckConstraintError,
|
||||
EntityDatabaseAdapterExclusionConstraintError,
|
||||
EntityDatabaseAdapterForeignKeyConstraintError,
|
||||
EntityDatabaseAdapterNotNullConstraintError,
|
||||
EntityDatabaseAdapterTransientError,
|
||||
EntityDatabaseAdapterUniqueConstraintError,
|
||||
EntityDatabaseAdapterUnknownError,
|
||||
ViewerContext,
|
||||
} from '@expo/entity';
|
||||
import nullthrows from '@expo/nullthrows';
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, it } from '@jest/globals';
|
||||
import type { Knex } from 'knex';
|
||||
@@ -6,15 +15,6 @@ import knex from 'knex';
|
||||
|
||||
import { ErrorsTestEntity } from '../__testfixtures__/ErrorsTestEntity.ts';
|
||||
import { createKnexIntegrationTestEntityCompanionProvider } from '../__testfixtures__/createKnexIntegrationTestEntityCompanionProvider.ts';
|
||||
import {
|
||||
EntityDatabaseAdapterCheckConstraintError,
|
||||
EntityDatabaseAdapterExclusionConstraintError,
|
||||
EntityDatabaseAdapterForeignKeyConstraintError,
|
||||
EntityDatabaseAdapterLockNotAvailableError,
|
||||
EntityDatabaseAdapterNotNullConstraintError,
|
||||
EntityDatabaseAdapterTransientError,
|
||||
EntityDatabaseAdapterUnknownError,
|
||||
} from '../errors/KnexEntityDatabaseAdapterError.ts';
|
||||
|
||||
describe('postgres errors', () => {
|
||||
let knexInstance: Knex;
|
||||
@@ -160,34 +160,6 @@ describe('postgres errors', () => {
|
||||
).rejects.toThrow(EntityDatabaseAdapterExclusionConstraintError);
|
||||
});
|
||||
|
||||
it('throws EntityDatabaseAdapterLockNotAvailableError when lock_timeout is exceeded', async () => {
|
||||
const vc = new ViewerContext(createKnexIntegrationTestEntityCompanionProvider(knexInstance));
|
||||
const entity = await ErrorsTestEntity.creator(vc)
|
||||
.setField('id', 1)
|
||||
.setField('fieldNonNull', 'hello')
|
||||
.createAsync();
|
||||
|
||||
// hold a row lock from a separate transaction while the entity update attempts to acquire it
|
||||
const lockHolderTransaction = await knexInstance.transaction();
|
||||
await lockHolderTransaction.raw(
|
||||
'SELECT * FROM postgres_test_entities WHERE id = ? FOR UPDATE',
|
||||
[entity.getID()],
|
||||
);
|
||||
|
||||
try {
|
||||
await expect(
|
||||
vc.runInTransactionForDatabaseAdapterFlavorAsync('postgres', async (queryContext) => {
|
||||
await queryContext.getQueryInterface().raw("SET LOCAL lock_timeout = '100ms'");
|
||||
await ErrorsTestEntity.updater(entity, queryContext)
|
||||
.setField('fieldNonNull', 'world')
|
||||
.updateAsync();
|
||||
}),
|
||||
).rejects.toThrow(EntityDatabaseAdapterLockNotAvailableError);
|
||||
} finally {
|
||||
await lockHolderTransaction.rollback();
|
||||
}
|
||||
});
|
||||
|
||||
it('throws EntityDatabaseAdapterUnknownError otherwise', async () => {
|
||||
const vc = new ViewerContext(createKnexIntegrationTestEntityCompanionProvider(knexInstance));
|
||||
await expect(
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
import { EntityDatabaseAdapterError, EntityErrorState } from '@expo/entity';
|
||||
|
||||
/**
|
||||
* Error code for an entity error thrown by the knex database adapter.
|
||||
* Each error code should map to a specific class of Entity error.
|
||||
*/
|
||||
export enum KnexEntityDatabaseAdapterErrorCode {
|
||||
ERR_ENTITY_DATABASE_ADAPTER_TRANSIENT = 'ERR_ENTITY_DATABASE_ADAPTER_TRANSIENT',
|
||||
ERR_ENTITY_DATABASE_ADAPTER_UNKNOWN = 'ERR_ENTITY_DATABASE_ADAPTER_UNKNOWN',
|
||||
ERR_ENTITY_DATABASE_ADAPTER_CHECK_CONSTRAINT = 'ERR_ENTITY_DATABASE_ADAPTER_CHECK_CONSTRAINT',
|
||||
ERR_ENTITY_DATABASE_ADAPTER_EXCLUSION_CONSTRAINT = 'ERR_ENTITY_DATABASE_ADAPTER_EXCLUSION_CONSTRAINT',
|
||||
ERR_ENTITY_DATABASE_ADAPTER_FOREIGN_KEY_CONSTRAINT = 'ERR_ENTITY_DATABASE_ADAPTER_FOREIGN_KEY_CONSTRAINT',
|
||||
ERR_ENTITY_DATABASE_ADAPTER_NOT_NULL_CONSTRAINT = 'ERR_ENTITY_DATABASE_ADAPTER_NOT_NULL_CONSTRAINT',
|
||||
ERR_ENTITY_DATABASE_ADAPTER_PAGINATION_CURSOR_INVALID = 'ERR_ENTITY_DATABASE_ADAPTER_PAGINATION_CURSOR_INVALID',
|
||||
ERR_ENTITY_DATABASE_ADAPTER_LOCK_NOT_AVAILABLE = 'ERR_ENTITY_DATABASE_ADAPTER_LOCK_NOT_AVAILABLE',
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when a transient error occurrs within the database adapter.
|
||||
* Transient errors may succeed if retried.
|
||||
*/
|
||||
export class EntityDatabaseAdapterTransientError extends EntityDatabaseAdapterError {
|
||||
static {
|
||||
this.prototype.name = 'EntityDatabaseAdapterTransientError';
|
||||
}
|
||||
|
||||
get state(): EntityErrorState.TRANSIENT {
|
||||
return EntityErrorState.TRANSIENT;
|
||||
}
|
||||
|
||||
get code(): KnexEntityDatabaseAdapterErrorCode.ERR_ENTITY_DATABASE_ADAPTER_TRANSIENT {
|
||||
return KnexEntityDatabaseAdapterErrorCode.ERR_ENTITY_DATABASE_ADAPTER_TRANSIENT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when an unknown error occurrs within the database adapter.
|
||||
* This is a catch-all error class for DBMS-specific errors that do not fit into other categories.
|
||||
*/
|
||||
export class EntityDatabaseAdapterUnknownError extends EntityDatabaseAdapterError {
|
||||
static {
|
||||
this.prototype.name = 'EntityDatabaseAdapterUnknownError';
|
||||
}
|
||||
|
||||
get state(): EntityErrorState.UNKNOWN {
|
||||
return EntityErrorState.UNKNOWN;
|
||||
}
|
||||
|
||||
get code(): KnexEntityDatabaseAdapterErrorCode.ERR_ENTITY_DATABASE_ADAPTER_UNKNOWN {
|
||||
return KnexEntityDatabaseAdapterErrorCode.ERR_ENTITY_DATABASE_ADAPTER_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when a check constraint is violated within the database adapter.
|
||||
* This indicates that a value being inserted or updated does not satisfy a defined data integrity constraint.
|
||||
*/
|
||||
export class EntityDatabaseAdapterCheckConstraintError extends EntityDatabaseAdapterError {
|
||||
static {
|
||||
this.prototype.name = 'EntityDatabaseAdapterCheckConstraintError';
|
||||
}
|
||||
|
||||
get state(): EntityErrorState.PERMANENT {
|
||||
return EntityErrorState.PERMANENT;
|
||||
}
|
||||
|
||||
get code(): KnexEntityDatabaseAdapterErrorCode.ERR_ENTITY_DATABASE_ADAPTER_CHECK_CONSTRAINT {
|
||||
return KnexEntityDatabaseAdapterErrorCode.ERR_ENTITY_DATABASE_ADAPTER_CHECK_CONSTRAINT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when an exclusion constraint is violated within the database adapter.
|
||||
* This indicates that a value being inserted or updated conflicts with an existing value based on a defined exclusion constraint.
|
||||
*/
|
||||
export class EntityDatabaseAdapterExclusionConstraintError extends EntityDatabaseAdapterError {
|
||||
static {
|
||||
this.prototype.name = 'EntityDatabaseAdapterExclusionConstraintError';
|
||||
}
|
||||
|
||||
get state(): EntityErrorState.PERMANENT {
|
||||
return EntityErrorState.PERMANENT;
|
||||
}
|
||||
|
||||
get code(): KnexEntityDatabaseAdapterErrorCode.ERR_ENTITY_DATABASE_ADAPTER_EXCLUSION_CONSTRAINT {
|
||||
return KnexEntityDatabaseAdapterErrorCode.ERR_ENTITY_DATABASE_ADAPTER_EXCLUSION_CONSTRAINT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when a foreign key constraint is violated within the database adapter.
|
||||
* This indicates that a value being inserted, updated, or deleted references a non-existent value in a related table
|
||||
* or is referenced in a related table.
|
||||
*/
|
||||
export class EntityDatabaseAdapterForeignKeyConstraintError extends EntityDatabaseAdapterError {
|
||||
static {
|
||||
this.prototype.name = 'EntityDatabaseAdapterForeignKeyConstraintError';
|
||||
}
|
||||
|
||||
get state(): EntityErrorState.PERMANENT {
|
||||
return EntityErrorState.PERMANENT;
|
||||
}
|
||||
|
||||
get code(): KnexEntityDatabaseAdapterErrorCode.ERR_ENTITY_DATABASE_ADAPTER_FOREIGN_KEY_CONSTRAINT {
|
||||
return KnexEntityDatabaseAdapterErrorCode.ERR_ENTITY_DATABASE_ADAPTER_FOREIGN_KEY_CONSTRAINT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when a not-null constraint is violated within the database adapter.
|
||||
* This indicates that a null value is being inserted or updated into a column that does not allow null values.
|
||||
*/
|
||||
export class EntityDatabaseAdapterNotNullConstraintError extends EntityDatabaseAdapterError {
|
||||
static {
|
||||
this.prototype.name = 'EntityDatabaseAdapterNotNullConstraintError';
|
||||
}
|
||||
|
||||
get state(): EntityErrorState.PERMANENT {
|
||||
return EntityErrorState.PERMANENT;
|
||||
}
|
||||
|
||||
get code(): KnexEntityDatabaseAdapterErrorCode.ERR_ENTITY_DATABASE_ADAPTER_NOT_NULL_CONSTRAINT {
|
||||
return KnexEntityDatabaseAdapterErrorCode.ERR_ENTITY_DATABASE_ADAPTER_NOT_NULL_CONSTRAINT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when a pagination cursor cannot be decoded or does not match the expected pagination
|
||||
* specification.
|
||||
*/
|
||||
export class EntityDatabaseAdapterPaginationCursorInvalidError extends EntityDatabaseAdapterError {
|
||||
static {
|
||||
this.prototype.name = 'EntityDatabaseAdapterPaginationCursorInvalidError';
|
||||
}
|
||||
|
||||
get state(): EntityErrorState.PERMANENT {
|
||||
return EntityErrorState.PERMANENT;
|
||||
}
|
||||
|
||||
get code(): KnexEntityDatabaseAdapterErrorCode.ERR_ENTITY_DATABASE_ADAPTER_PAGINATION_CURSOR_INVALID {
|
||||
return KnexEntityDatabaseAdapterErrorCode.ERR_ENTITY_DATABASE_ADAPTER_PAGINATION_CURSOR_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when a lock on a row or table cannot be acquired (PostgreSQL error code 55P03, `lock_not_available`).
|
||||
* This happens when a statement exceeds `lock_timeout` while waiting for a lock, or when a `NOWAIT`
|
||||
* lock request conflicts with a lock held by another transaction.
|
||||
* This error is transient since the conflicting lock is released when the other transaction completes.
|
||||
*/
|
||||
export class EntityDatabaseAdapterLockNotAvailableError extends EntityDatabaseAdapterError {
|
||||
static {
|
||||
this.prototype.name = 'EntityDatabaseAdapterLockNotAvailableError';
|
||||
}
|
||||
|
||||
get state(): EntityErrorState.TRANSIENT {
|
||||
return EntityErrorState.TRANSIENT;
|
||||
}
|
||||
|
||||
get code(): KnexEntityDatabaseAdapterErrorCode.ERR_ENTITY_DATABASE_ADAPTER_LOCK_NOT_AVAILABLE {
|
||||
return KnexEntityDatabaseAdapterErrorCode.ERR_ENTITY_DATABASE_ADAPTER_LOCK_NOT_AVAILABLE;
|
||||
}
|
||||
}
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
import { EntityDatabaseAdapterError, EntityErrorState } from '@expo/entity';
|
||||
import { describe, expect, it } from '@jest/globals';
|
||||
|
||||
import {
|
||||
EntityDatabaseAdapterCheckConstraintError,
|
||||
EntityDatabaseAdapterExclusionConstraintError,
|
||||
EntityDatabaseAdapterForeignKeyConstraintError,
|
||||
EntityDatabaseAdapterLockNotAvailableError,
|
||||
EntityDatabaseAdapterNotNullConstraintError,
|
||||
EntityDatabaseAdapterPaginationCursorInvalidError,
|
||||
EntityDatabaseAdapterTransientError,
|
||||
EntityDatabaseAdapterUnknownError,
|
||||
KnexEntityDatabaseAdapterErrorCode,
|
||||
} from '../KnexEntityDatabaseAdapterError.ts';
|
||||
|
||||
describe('KnexEntityDatabaseAdapterError', () => {
|
||||
it('instantiates all errors with correct state and code', () => {
|
||||
const transientError = new EntityDatabaseAdapterTransientError('test');
|
||||
expect(transientError).toBeInstanceOf(EntityDatabaseAdapterError);
|
||||
expect(transientError.state).toBe(EntityErrorState.TRANSIENT);
|
||||
expect(transientError.code).toBe(
|
||||
KnexEntityDatabaseAdapterErrorCode.ERR_ENTITY_DATABASE_ADAPTER_TRANSIENT,
|
||||
);
|
||||
|
||||
const unknownError = new EntityDatabaseAdapterUnknownError('test');
|
||||
expect(unknownError).toBeInstanceOf(EntityDatabaseAdapterError);
|
||||
expect(unknownError.state).toBe(EntityErrorState.UNKNOWN);
|
||||
expect(unknownError.code).toBe(
|
||||
KnexEntityDatabaseAdapterErrorCode.ERR_ENTITY_DATABASE_ADAPTER_UNKNOWN,
|
||||
);
|
||||
|
||||
const checkError = new EntityDatabaseAdapterCheckConstraintError('test');
|
||||
expect(checkError).toBeInstanceOf(EntityDatabaseAdapterError);
|
||||
expect(checkError.state).toBe(EntityErrorState.PERMANENT);
|
||||
expect(checkError.code).toBe(
|
||||
KnexEntityDatabaseAdapterErrorCode.ERR_ENTITY_DATABASE_ADAPTER_CHECK_CONSTRAINT,
|
||||
);
|
||||
|
||||
const exclusionError = new EntityDatabaseAdapterExclusionConstraintError('test');
|
||||
expect(exclusionError).toBeInstanceOf(EntityDatabaseAdapterError);
|
||||
expect(exclusionError.state).toBe(EntityErrorState.PERMANENT);
|
||||
expect(exclusionError.code).toBe(
|
||||
KnexEntityDatabaseAdapterErrorCode.ERR_ENTITY_DATABASE_ADAPTER_EXCLUSION_CONSTRAINT,
|
||||
);
|
||||
|
||||
const foreignKeyError = new EntityDatabaseAdapterForeignKeyConstraintError('test');
|
||||
expect(foreignKeyError).toBeInstanceOf(EntityDatabaseAdapterError);
|
||||
expect(foreignKeyError.state).toBe(EntityErrorState.PERMANENT);
|
||||
expect(foreignKeyError.code).toBe(
|
||||
KnexEntityDatabaseAdapterErrorCode.ERR_ENTITY_DATABASE_ADAPTER_FOREIGN_KEY_CONSTRAINT,
|
||||
);
|
||||
|
||||
const notNullError = new EntityDatabaseAdapterNotNullConstraintError('test');
|
||||
expect(notNullError).toBeInstanceOf(EntityDatabaseAdapterError);
|
||||
expect(notNullError.state).toBe(EntityErrorState.PERMANENT);
|
||||
expect(notNullError.code).toBe(
|
||||
KnexEntityDatabaseAdapterErrorCode.ERR_ENTITY_DATABASE_ADAPTER_NOT_NULL_CONSTRAINT,
|
||||
);
|
||||
|
||||
const paginationCursorInvalidError = new EntityDatabaseAdapterPaginationCursorInvalidError(
|
||||
'test',
|
||||
);
|
||||
expect(paginationCursorInvalidError).toBeInstanceOf(EntityDatabaseAdapterError);
|
||||
expect(paginationCursorInvalidError.state).toBe(EntityErrorState.PERMANENT);
|
||||
expect(paginationCursorInvalidError.code).toBe(
|
||||
KnexEntityDatabaseAdapterErrorCode.ERR_ENTITY_DATABASE_ADAPTER_PAGINATION_CURSOR_INVALID,
|
||||
);
|
||||
|
||||
const lockNotAvailableError = new EntityDatabaseAdapterLockNotAvailableError('test');
|
||||
expect(lockNotAvailableError).toBeInstanceOf(EntityDatabaseAdapterError);
|
||||
expect(lockNotAvailableError.state).toBe(EntityErrorState.TRANSIENT);
|
||||
expect(lockNotAvailableError.code).toBe(
|
||||
KnexEntityDatabaseAdapterErrorCode.ERR_ENTITY_DATABASE_ADAPTER_LOCK_NOT_AVAILABLE,
|
||||
);
|
||||
});
|
||||
});
|
||||
+3
-4
@@ -1,11 +1,10 @@
|
||||
import { EntityErrorState } from '@expo/entity';
|
||||
import { EntityDatabaseAdapterTransientError, EntityErrorState } from '@expo/entity';
|
||||
import { describe, expect, it } from '@jest/globals';
|
||||
|
||||
import { EntityDatabaseAdapterLockNotAvailableError } from '../KnexEntityDatabaseAdapterError.ts';
|
||||
import { wrapNativePostgresCallAsync } from '../wrapNativePostgresCallAsync.ts';
|
||||
|
||||
describe(wrapNativePostgresCallAsync, () => {
|
||||
it('translates a PostgreSQL lock_not_available error and preserves the cause', async () => {
|
||||
it('classifies a PostgreSQL lock timeout as transient and preserves the cause', async () => {
|
||||
const error = Object.assign(new Error('canceling statement due to lock timeout'), {
|
||||
code: '55P03',
|
||||
});
|
||||
@@ -13,7 +12,7 @@ describe(wrapNativePostgresCallAsync, () => {
|
||||
throw error;
|
||||
});
|
||||
|
||||
await expect(result).rejects.toBeInstanceOf(EntityDatabaseAdapterLockNotAvailableError);
|
||||
await expect(result).rejects.toBeInstanceOf(EntityDatabaseAdapterTransientError);
|
||||
await expect(result).rejects.toMatchObject({
|
||||
state: EntityErrorState.TRANSIENT,
|
||||
message: error.message,
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import type { EntityDatabaseAdapterError } from '@expo/entity';
|
||||
import { EntityDatabaseAdapterUniqueConstraintError } from '@expo/entity';
|
||||
import knex from 'knex';
|
||||
|
||||
import {
|
||||
EntityDatabaseAdapterCheckConstraintError,
|
||||
EntityDatabaseAdapterExclusionConstraintError,
|
||||
EntityDatabaseAdapterForeignKeyConstraintError,
|
||||
EntityDatabaseAdapterLockNotAvailableError,
|
||||
EntityDatabaseAdapterNotNullConstraintError,
|
||||
EntityDatabaseAdapterTransientError,
|
||||
EntityDatabaseAdapterUniqueConstraintError,
|
||||
EntityDatabaseAdapterUnknownError,
|
||||
} from './KnexEntityDatabaseAdapterError.ts';
|
||||
} from '@expo/entity';
|
||||
import knex from 'knex';
|
||||
|
||||
function wrapNativePostgresError(
|
||||
error: Error & { code?: string },
|
||||
@@ -41,7 +39,7 @@ function translatePostgresError(
|
||||
case '23P01':
|
||||
return new EntityDatabaseAdapterExclusionConstraintError(error.message, error);
|
||||
case '55P03':
|
||||
return new EntityDatabaseAdapterLockNotAvailableError(error.message, error);
|
||||
return new EntityDatabaseAdapterTransientError(error.message, error);
|
||||
default:
|
||||
return new EntityDatabaseAdapterUnknownError(error.message, error);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ export * from './PostgresEntityDatabaseAdapterProvider.ts';
|
||||
export * from './PostgresEntityQueryContextProvider.ts';
|
||||
export * from './ReadonlyPostgresEntity.ts';
|
||||
export * from './SQLOperator.ts';
|
||||
export * from './errors/KnexEntityDatabaseAdapterError.ts';
|
||||
export * from './errors/wrapNativePostgresCallAsync.ts';
|
||||
export * from './internal/EntityKnexDataManager.ts';
|
||||
export * from './internal/getKnexDataManager.ts';
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { EntityConfiguration, EntityQueryContext, IEntityMetricsAdapter } from '@expo/entity';
|
||||
import {
|
||||
EntityDatabaseAdapterPaginationCursorInvalidError,
|
||||
EntityMetricsCountType,
|
||||
EntityMetricsLoadType,
|
||||
getDatabaseFieldForEntityField,
|
||||
@@ -17,7 +18,6 @@ import type {
|
||||
import { NullsOrdering, OrderByOrdering } from '../BasePostgresEntityDatabaseAdapter.ts';
|
||||
import { PaginationStrategy } from '../PaginationStrategy.ts';
|
||||
import { SQLFragment, SQLExpression, identifier, sql, unsafeRaw } from '../SQLOperator.ts';
|
||||
import { EntityDatabaseAdapterPaginationCursorInvalidError } from '../errors/KnexEntityDatabaseAdapterError.ts';
|
||||
import type { DistributiveOmit, NonNullableKeys } from './utilityTypes.ts';
|
||||
|
||||
interface DataManagerStandardSpecification<TFields extends Record<string, any>> {
|
||||
|
||||
@@ -3,18 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [0.72.0](https://github.com/expo/entity/compare/v0.71.0...v0.72.0) (2026-10-29)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-example
|
||||
|
||||
# [0.71.0](https://github.com/expo/entity/compare/v0.70.0...v0.71.0) (2026-10-09)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-example
|
||||
|
||||
# [0.70.0](https://github.com/expo/entity/compare/v0.69.0...v0.70.0) (2026-10-05)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-example
|
||||
|
||||
# [0.69.0](https://github.com/expo/entity/compare/v0.68.0...v0.69.0) (2026-09-29)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-example
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/entity-example",
|
||||
"version": "0.72.0",
|
||||
"version": "0.69.0",
|
||||
"private": true,
|
||||
"description": "An example integration of the @expo/entity framework",
|
||||
"keywords": [
|
||||
@@ -30,12 +30,12 @@
|
||||
"koa": "3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jest/globals": "30.5.2",
|
||||
"@jest/globals": "30.5.1",
|
||||
"@types/koa": "3.0.3",
|
||||
"@types/koa__cors": "5.0.1",
|
||||
"@types/koa__router": "12.0.5",
|
||||
"@types/supertest": "7.2.1",
|
||||
"supertest": "7.3.1",
|
||||
"supertest": "7.2.2",
|
||||
"typescript": "6.0.3"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -3,18 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [0.72.0](https://github.com/expo/entity/compare/v0.71.0...v0.72.0) (2026-10-29)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-full-integration-tests
|
||||
|
||||
# [0.71.0](https://github.com/expo/entity/compare/v0.70.0...v0.71.0) (2026-10-09)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-full-integration-tests
|
||||
|
||||
# [0.70.0](https://github.com/expo/entity/compare/v0.69.0...v0.70.0) (2026-10-05)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-full-integration-tests
|
||||
|
||||
# [0.69.0](https://github.com/expo/entity/compare/v0.68.0...v0.69.0) (2026-09-29)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-full-integration-tests
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/entity-full-integration-tests",
|
||||
"version": "0.72.0",
|
||||
"version": "0.69.0",
|
||||
"private": true,
|
||||
"description": "Full redis and knex integration tests for the entity framework",
|
||||
"keywords": [
|
||||
@@ -22,7 +22,7 @@
|
||||
"@expo/entity-database-adapter-knex": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jest/globals": "30.5.2",
|
||||
"@jest/globals": "30.5.1",
|
||||
"typescript": "6.0.3"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -3,18 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [0.72.0](https://github.com/expo/entity/compare/v0.71.0...v0.72.0) (2026-10-29)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-ip-address-field
|
||||
|
||||
# [0.71.0](https://github.com/expo/entity/compare/v0.70.0...v0.71.0) (2026-10-09)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-ip-address-field
|
||||
|
||||
# [0.70.0](https://github.com/expo/entity/compare/v0.69.0...v0.70.0) (2026-10-05)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-ip-address-field
|
||||
|
||||
# [0.69.0](https://github.com/expo/entity/compare/v0.68.0...v0.69.0) (2026-09-29)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-ip-address-field
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/entity-ip-address-field",
|
||||
"version": "0.72.0",
|
||||
"version": "0.69.0",
|
||||
"description": "IP address EntityField definitions for @expo/entity",
|
||||
"keywords": [
|
||||
"entity"
|
||||
@@ -30,7 +30,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@expo/entity-testing-utils": "workspace:^",
|
||||
"@jest/globals": "30.5.2",
|
||||
"@jest/globals": "30.5.1",
|
||||
"typescript": "6.0.3"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -3,18 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [0.72.0](https://github.com/expo/entity/compare/v0.71.0...v0.72.0) (2026-10-29)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-secondary-cache-local-memory
|
||||
|
||||
# [0.71.0](https://github.com/expo/entity/compare/v0.70.0...v0.71.0) (2026-10-09)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-secondary-cache-local-memory
|
||||
|
||||
# [0.70.0](https://github.com/expo/entity/compare/v0.69.0...v0.70.0) (2026-10-05)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-secondary-cache-local-memory
|
||||
|
||||
# [0.69.0](https://github.com/expo/entity/compare/v0.68.0...v0.69.0) (2026-09-29)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-secondary-cache-local-memory
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/entity-secondary-cache-local-memory",
|
||||
"version": "0.72.0",
|
||||
"version": "0.69.0",
|
||||
"description": "Local memory secondary cache for @expo/entity",
|
||||
"keywords": [
|
||||
"entity"
|
||||
@@ -32,7 +32,7 @@
|
||||
"devDependencies": {
|
||||
"@expo/nullthrows": "2.1.0",
|
||||
"@isaacs/ttlcache": "2.1.5",
|
||||
"@jest/globals": "30.5.2",
|
||||
"@jest/globals": "30.5.1",
|
||||
"typescript": "6.0.3"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -3,18 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [0.72.0](https://github.com/expo/entity/compare/v0.71.0...v0.72.0) (2026-10-29)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-secondary-cache-redis
|
||||
|
||||
# [0.71.0](https://github.com/expo/entity/compare/v0.70.0...v0.71.0) (2026-10-09)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-secondary-cache-redis
|
||||
|
||||
# [0.70.0](https://github.com/expo/entity/compare/v0.69.0...v0.70.0) (2026-10-05)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-secondary-cache-redis
|
||||
|
||||
# [0.69.0](https://github.com/expo/entity/compare/v0.68.0...v0.69.0) (2026-09-29)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-secondary-cache-redis
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/entity-secondary-cache-redis",
|
||||
"version": "0.72.0",
|
||||
"version": "0.69.0",
|
||||
"description": "Redis secondary cache for @expo/entity",
|
||||
"keywords": [
|
||||
"entity"
|
||||
@@ -30,7 +30,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@expo/nullthrows": "2.1.0",
|
||||
"@jest/globals": "30.5.2",
|
||||
"@jest/globals": "30.5.1",
|
||||
"ioredis": "5.11.1",
|
||||
"typescript": "6.0.3"
|
||||
},
|
||||
|
||||
@@ -3,20 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [0.72.0](https://github.com/expo/entity/compare/v0.71.0...v0.72.0) (2026-10-29)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-testing-utils
|
||||
|
||||
# [0.71.0](https://github.com/expo/entity/compare/v0.70.0...v0.71.0) (2026-10-09)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- declare runtime dependencies imported by published packages ([#717](https://github.com/expo/entity/issues/717)) ([3c96f3b](https://github.com/expo/entity/commit/3c96f3b9ea2b097c979447354f0abe35ed5e8d57))
|
||||
|
||||
# [0.70.0](https://github.com/expo/entity/compare/v0.69.0...v0.70.0) (2026-10-05)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-testing-utils
|
||||
|
||||
# [0.69.0](https://github.com/expo/entity/compare/v0.68.0...v0.69.0) (2026-09-29)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity-testing-utils
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/entity-testing-utils",
|
||||
"version": "0.72.0",
|
||||
"version": "0.69.0",
|
||||
"description": "A package containing utilities for testing applications that use Entity",
|
||||
"keywords": [
|
||||
"entity"
|
||||
@@ -26,15 +26,13 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/entity": "workspace:^",
|
||||
"invariant": "^2.2.4",
|
||||
"lodash-es": "^4.17.23",
|
||||
"ts-mockito": "^2.6.1",
|
||||
"uuid": "^14.0.0"
|
||||
"ts-mockito": "^2.6.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jest/globals": "30.5.2",
|
||||
"@jest/globals": "30.5.1",
|
||||
"@types/lodash-es": "4.17.12",
|
||||
"@types/node": "24.19.2",
|
||||
"@types/node": "24.19.1",
|
||||
"typescript": "6.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -3,22 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [0.72.0](https://github.com/expo/entity/compare/v0.71.0...v0.72.0) (2026-10-29)
|
||||
|
||||
### Features
|
||||
|
||||
- add notAuthorizedEntity to getUserFacingDenialReason ([#732](https://github.com/expo/entity/issues/732)) ([24109be](https://github.com/expo/entity/commit/24109bec32c03b228bd35cef8bd6a379dcf7277b))
|
||||
|
||||
# [0.71.0](https://github.com/expo/entity/compare/v0.70.0...v0.71.0) (2026-10-09)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- declare runtime dependencies imported by published packages ([#717](https://github.com/expo/entity/issues/717)) ([3c96f3b](https://github.com/expo/entity/commit/3c96f3b9ea2b097c979447354f0abe35ed5e8d57))
|
||||
|
||||
# [0.70.0](https://github.com/expo/entity/compare/v0.69.0...v0.70.0) (2026-10-05)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity
|
||||
|
||||
# [0.69.0](https://github.com/expo/entity/compare/v0.68.0...v0.69.0) (2026-09-29)
|
||||
|
||||
**Note:** Version bump only for package @expo/entity
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/entity",
|
||||
"version": "0.72.0",
|
||||
"version": "0.69.0",
|
||||
"description": "A privacy-first data model",
|
||||
"keywords": [
|
||||
"entity"
|
||||
@@ -25,20 +25,19 @@
|
||||
"test": "yarn test:all --rootDir $(pwd)"
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/nullthrows": "^2.1.0",
|
||||
"@expo/results": "^1.0.0",
|
||||
"dataloader": "^2.2.3",
|
||||
"invariant": "^2.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jest/globals": "30.5.2",
|
||||
"@jest/globals": "30.5.1",
|
||||
"@types/invariant": "2.2.37",
|
||||
"@types/lodash-es": "4.17.12",
|
||||
"@types/node": "24.19.2",
|
||||
"@types/node": "24.19.1",
|
||||
"lodash-es": "4.18.1",
|
||||
"ts-mockito": "2.6.1",
|
||||
"typescript": "6.0.3",
|
||||
"uuid": "14.0.3"
|
||||
"uuid": "14.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
|
||||
@@ -224,28 +224,17 @@ export abstract class EntityPrivacyPolicy<
|
||||
* contributed, so the policy can select a message for a specific reason, for example "You must be a
|
||||
* member of this account to view this project." when a rule skipped with a NOT_ACCOUNT_MEMBER reason.
|
||||
*
|
||||
* The evaluation context and the denied entity are passed so that the message can be selected based
|
||||
* on them (for example, the entity type or whether the denial is part of a cascading deletion). The
|
||||
* viewer is not authorized for the entity, and the evaluation context may contain it (as the previous
|
||||
* value) or other entities (as the cascading deletion cause), so take care that the message does not
|
||||
* reveal any data that the viewer may not see. Rules should communicate what is needed through reason codes.
|
||||
* The denied entity and the evaluation context are intentionally not passed to this method. The viewer
|
||||
* is not authorized to see the entity, and the evaluation context may contain it (as the previous value
|
||||
* or the cascading deletion cause), so neither may influence or appear in the user-facing message.
|
||||
* Rules should instead communicate what is needed through reason codes.
|
||||
*
|
||||
* @param _viewerContext - viewer context that was denied
|
||||
* @param _evaluationContext - context in which the policy was evaluated
|
||||
* @param _notAuthorizedEntity - entity for which the action was denied
|
||||
* @param _action - the action that was denied
|
||||
* @param _denialReason - structured, developer-facing reason for the denial
|
||||
*/
|
||||
protected getUserFacingDenialReason(
|
||||
_viewerContext: TViewerContext,
|
||||
_evaluationContext: EntityPrivacyPolicyEvaluationContext<
|
||||
TFields,
|
||||
TIDField,
|
||||
TViewerContext,
|
||||
TEntity,
|
||||
TSelectedFields
|
||||
>,
|
||||
_notAuthorizedEntity: TEntity,
|
||||
_action: EntityAuthorizationAction,
|
||||
_denialReason: EntityNotAuthorizedDenialReason,
|
||||
): EntityNotAuthorizedUserFacingReason | null {
|
||||
@@ -534,7 +523,7 @@ export abstract class EntityPrivacyPolicy<
|
||||
);
|
||||
switch (result) {
|
||||
case RuleEvaluationResult.DENY:
|
||||
throw this.createNotAuthorizedError(viewerContext, evaluationContext, entity, action, {
|
||||
throw this.createNotAuthorizedError(viewerContext, entity, action, {
|
||||
type: EntityNotAuthorizedDenialType.RULE_DENIED,
|
||||
rule: { ruleIndex: i, ruleName: rule.constructor.name, reasons },
|
||||
skippedRules,
|
||||
@@ -551,7 +540,7 @@ export abstract class EntityPrivacyPolicy<
|
||||
}
|
||||
}
|
||||
|
||||
throw this.createNotAuthorizedError(viewerContext, evaluationContext, entity, action, {
|
||||
throw this.createNotAuthorizedError(viewerContext, entity, action, {
|
||||
type: EntityNotAuthorizedDenialType.ALL_RULES_SKIPPED,
|
||||
skippedRules,
|
||||
});
|
||||
@@ -559,24 +548,11 @@ export abstract class EntityPrivacyPolicy<
|
||||
|
||||
private createNotAuthorizedError(
|
||||
viewerContext: TViewerContext,
|
||||
evaluationContext: EntityPrivacyPolicyEvaluationContext<
|
||||
TFields,
|
||||
TIDField,
|
||||
TViewerContext,
|
||||
TEntity,
|
||||
TSelectedFields
|
||||
>,
|
||||
entity: TEntity,
|
||||
action: EntityAuthorizationAction,
|
||||
denialReason: EntityNotAuthorizedDenialReason,
|
||||
): EntityNotAuthorizedError<TFields, TIDField, TViewerContext, TEntity, TSelectedFields> {
|
||||
const userFacingReason = this.getUserFacingDenialReason(
|
||||
viewerContext,
|
||||
evaluationContext,
|
||||
entity,
|
||||
action,
|
||||
denialReason,
|
||||
);
|
||||
const userFacingReason = this.getUserFacingDenialReason(viewerContext, action, denialReason);
|
||||
return new EntityNotAuthorizedError<
|
||||
TFields,
|
||||
TIDField,
|
||||
|
||||
@@ -398,13 +398,6 @@ class ReasonedDenialPolicy extends EntityPrivacyPolicy<
|
||||
|
||||
protected override getUserFacingDenialReason(
|
||||
_viewerContext: ViewerContext,
|
||||
_evaluationContext: EntityPrivacyPolicyEvaluationContext<
|
||||
BlahFields,
|
||||
'id',
|
||||
ViewerContext,
|
||||
BlahEntity
|
||||
>,
|
||||
notAuthorizedEntity: BlahEntity,
|
||||
_action: EntityAuthorizationAction,
|
||||
denialReason: EntityNotAuthorizedDenialReason,
|
||||
): EntityNotAuthorizedUserFacingReason | null {
|
||||
@@ -414,10 +407,7 @@ class ReasonedDenialPolicy extends EntityPrivacyPolicy<
|
||||
skippedRule.reasons.includes(TestSkipReason.NOT_MEMBER),
|
||||
)
|
||||
) {
|
||||
return {
|
||||
code: 'MUST_BE_MEMBER',
|
||||
message: `You must be a member to access this ${notAuthorizedEntity.constructor.name}.`,
|
||||
};
|
||||
return { code: 'MUST_BE_MEMBER', message: 'You must be a member to do this.' };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -951,7 +941,7 @@ describe(EntityPrivacyPolicy, () => {
|
||||
expect(error.message).not.toContain('NOT_OWNER');
|
||||
expect(error.userFacingDenialReason).toEqual({
|
||||
code: 'MUST_BE_MEMBER',
|
||||
message: 'You must be a member to access this BlahEntity.',
|
||||
message: 'You must be a member to do this.',
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -9,6 +9,115 @@ export abstract class EntityDatabaseAdapterError extends EntityError {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when a transient error occurrs within the database adapter.
|
||||
* Transient errors may succeed if retried.
|
||||
*/
|
||||
export class EntityDatabaseAdapterTransientError extends EntityDatabaseAdapterError {
|
||||
static {
|
||||
this.prototype.name = 'EntityDatabaseAdapterTransientError';
|
||||
}
|
||||
|
||||
get state(): EntityErrorState.TRANSIENT {
|
||||
return EntityErrorState.TRANSIENT;
|
||||
}
|
||||
|
||||
get code(): EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_TRANSIENT {
|
||||
return EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_TRANSIENT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when an unknown error occurrs within the database adapter.
|
||||
* This is a catch-all error class for DBMS-specific errors that do not fit into other categories.
|
||||
*/
|
||||
export class EntityDatabaseAdapterUnknownError extends EntityDatabaseAdapterError {
|
||||
static {
|
||||
this.prototype.name = 'EntityDatabaseAdapterUnknownError';
|
||||
}
|
||||
|
||||
get state(): EntityErrorState.UNKNOWN {
|
||||
return EntityErrorState.UNKNOWN;
|
||||
}
|
||||
|
||||
get code(): EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_UNKNOWN {
|
||||
return EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when a check constraint is violated within the database adapter.
|
||||
* This indicates that a value being inserted or updated does not satisfy a defined data integrity constraint.
|
||||
*/
|
||||
export class EntityDatabaseAdapterCheckConstraintError extends EntityDatabaseAdapterError {
|
||||
static {
|
||||
this.prototype.name = 'EntityDatabaseAdapterCheckConstraintError';
|
||||
}
|
||||
|
||||
get state(): EntityErrorState.PERMANENT {
|
||||
return EntityErrorState.PERMANENT;
|
||||
}
|
||||
|
||||
get code(): EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_CHECK_CONSTRAINT {
|
||||
return EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_CHECK_CONSTRAINT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when an exclusion constraint is violated within the database adapter.
|
||||
* This indicates that a value being inserted or updated conflicts with an existing value based on a defined exclusion constraint.
|
||||
*/
|
||||
export class EntityDatabaseAdapterExclusionConstraintError extends EntityDatabaseAdapterError {
|
||||
static {
|
||||
this.prototype.name = 'EntityDatabaseAdapterExclusionConstraintError';
|
||||
}
|
||||
|
||||
get state(): EntityErrorState.PERMANENT {
|
||||
return EntityErrorState.PERMANENT;
|
||||
}
|
||||
|
||||
get code(): EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_EXCLUSION_CONSTRAINT {
|
||||
return EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_EXCLUSION_CONSTRAINT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when a foreign key constraint is violated within the database adapter.
|
||||
* This indicates that a value being inserted, updated, or deleted references a non-existent value in a related table
|
||||
* or is referenced in a related table.
|
||||
*/
|
||||
export class EntityDatabaseAdapterForeignKeyConstraintError extends EntityDatabaseAdapterError {
|
||||
static {
|
||||
this.prototype.name = 'EntityDatabaseAdapterForeignKeyConstraintError';
|
||||
}
|
||||
|
||||
get state(): EntityErrorState.PERMANENT {
|
||||
return EntityErrorState.PERMANENT;
|
||||
}
|
||||
|
||||
get code(): EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_FOREIGN_KEY_CONSTRAINT {
|
||||
return EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_FOREIGN_KEY_CONSTRAINT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when a not-null constraint is violated within the database adapter.
|
||||
* This indicates that a null value is being inserted or updated into a column that does not allow null values.
|
||||
*/
|
||||
export class EntityDatabaseAdapterNotNullConstraintError extends EntityDatabaseAdapterError {
|
||||
static {
|
||||
this.prototype.name = 'EntityDatabaseAdapterNotNullConstraintError';
|
||||
}
|
||||
|
||||
get state(): EntityErrorState.PERMANENT {
|
||||
return EntityErrorState.PERMANENT;
|
||||
}
|
||||
|
||||
get code(): EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_NOT_NULL_CONSTRAINT {
|
||||
return EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_NOT_NULL_CONSTRAINT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown when a unique constraint is violated within the database adapter.
|
||||
* This indicates that a value being inserted or updated duplicates an existing value in a column or set of columns
|
||||
@@ -122,3 +231,17 @@ export class EntityDatabaseAdapterExcessiveDeleteResultError extends EntityDatab
|
||||
return EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_EXCESSIVE_DELETE_RESULT;
|
||||
}
|
||||
}
|
||||
|
||||
export class EntityDatabaseAdapterPaginationCursorInvalidError extends EntityDatabaseAdapterError {
|
||||
static {
|
||||
this.prototype.name = 'EntityDatabaseAdapterPaginationCursorError';
|
||||
}
|
||||
|
||||
get state(): EntityErrorState.PERMANENT {
|
||||
return EntityErrorState.PERMANENT;
|
||||
}
|
||||
|
||||
get code(): EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_PAGINATION_CURSOR_INVALID {
|
||||
return EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_PAGINATION_CURSOR_INVALID;
|
||||
}
|
||||
}
|
||||
@@ -8,13 +8,18 @@ export enum EntityErrorState {
|
||||
}
|
||||
|
||||
/**
|
||||
* Error code for an entity error defined in this package. Each error code should map to a specific class of Entity error.
|
||||
* Other packages that define entity errors (such as database adapter implementations) define their own error code enums.
|
||||
* Error code for an entity error. Each error code should map to a specific class of Entity error.
|
||||
*/
|
||||
export enum EntityErrorCode {
|
||||
ERR_ENTITY_NOT_AUTHORIZED = 'ERR_ENTITY_NOT_AUTHORIZED',
|
||||
ERR_ENTITY_NOT_FOUND = 'ERR_ENTITY_NOT_FOUND',
|
||||
ERR_ENTITY_INVALID_FIELD_VALUE = 'ERR_ENTITY_INVALID_FIELD_VALUE',
|
||||
ERR_ENTITY_DATABASE_ADAPTER_TRANSIENT = 'ERR_ENTITY_DATABASE_ADAPTER_TRANSIENT',
|
||||
ERR_ENTITY_DATABASE_ADAPTER_UNKNOWN = 'ERR_ENTITY_DATABASE_ADAPTER_UNKNOWN',
|
||||
ERR_ENTITY_DATABASE_ADAPTER_CHECK_CONSTRAINT = 'ERR_ENTITY_DATABASE_ADAPTER_CHECK_CONSTRAINT',
|
||||
ERR_ENTITY_DATABASE_ADAPTER_EXCLUSION_CONSTRAINT = 'ERR_ENTITY_DATABASE_ADAPTER_EXCLUSION_CONSTRAINT',
|
||||
ERR_ENTITY_DATABASE_ADAPTER_FOREIGN_KEY_CONSTRAINT = 'ERR_ENTITY_DATABASE_ADAPTER_FOREIGN_KEY_CONSTRAINT',
|
||||
ERR_ENTITY_DATABASE_ADAPTER_NOT_NULL_CONSTRAINT = 'ERR_ENTITY_DATABASE_ADAPTER_NOT_NULL_CONSTRAINT',
|
||||
ERR_ENTITY_DATABASE_ADAPTER_UNIQUE_CONSTRAINT = 'ERR_ENTITY_DATABASE_ADAPTER_UNIQUE_CONSTRAINT',
|
||||
ERR_ENTITY_DATABASE_ADAPTER_EXCESSIVE_INSERT_RESULT = 'ERR_ENTITY_DATABASE_ADAPTER_EXCESSIVE_INSERT_RESULT',
|
||||
ERR_ENTITY_DATABASE_ADAPTER_EMPTY_INSERT_RESULT = 'ERR_ENTITY_DATABASE_ADAPTER_EMPTY_INSERT_RESULT',
|
||||
@@ -22,6 +27,7 @@ export enum EntityErrorCode {
|
||||
ERR_ENTITY_DATABASE_ADAPTER_EMPTY_UPDATE_RESULT = 'ERR_ENTITY_DATABASE_ADAPTER_EMPTY_UPDATE_RESULT',
|
||||
ERR_ENTITY_DATABASE_ADAPTER_EXCESSIVE_DELETE_RESULT = 'ERR_ENTITY_DATABASE_ADAPTER_EXCESSIVE_DELETE_RESULT',
|
||||
ERR_ENTITY_CACHE_ADAPTER_TRANSIENT = 'ERR_ENTITY_CACHE_ADAPTER_TRANSIENT',
|
||||
ERR_ENTITY_DATABASE_ADAPTER_PAGINATION_CURSOR_INVALID = 'ERR_ENTITY_DATABASE_ADAPTER_PAGINATION_CURSOR_INVALID',
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,7 +39,7 @@ export abstract class EntityError extends Error {
|
||||
}
|
||||
|
||||
public abstract readonly state: EntityErrorState;
|
||||
public abstract readonly code: string;
|
||||
public abstract readonly code: EntityErrorCode;
|
||||
|
||||
constructor(message: string, cause?: Error) {
|
||||
super(message, { cause });
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
import { describe, expect, it } from '@jest/globals';
|
||||
|
||||
import {
|
||||
EntityDatabaseAdapterCheckConstraintError,
|
||||
EntityDatabaseAdapterEmptyInsertResultError,
|
||||
EntityDatabaseAdapterEmptyUpdateResultError,
|
||||
EntityDatabaseAdapterError,
|
||||
EntityDatabaseAdapterExcessiveDeleteResultError,
|
||||
EntityDatabaseAdapterExcessiveInsertResultError,
|
||||
EntityDatabaseAdapterExcessiveUpdateResultError,
|
||||
EntityDatabaseAdapterExclusionConstraintError,
|
||||
EntityDatabaseAdapterForeignKeyConstraintError,
|
||||
EntityDatabaseAdapterNotNullConstraintError,
|
||||
EntityDatabaseAdapterPaginationCursorInvalidError,
|
||||
EntityDatabaseAdapterTransientError,
|
||||
EntityDatabaseAdapterUniqueConstraintError,
|
||||
EntityDatabaseAdapterUnknownError,
|
||||
} from '../EntityDatabaseAdapterError.ts';
|
||||
import { EntityErrorCode, EntityErrorState } from '../EntityError.ts';
|
||||
|
||||
@@ -15,6 +22,34 @@ describe(EntityDatabaseAdapterError, () => {
|
||||
// necessary for coverage within the entity package since these errors are
|
||||
// currently only ever instantiated by database adapter implementations
|
||||
it('instantiates all errors with correct state and code', () => {
|
||||
const transientError = new EntityDatabaseAdapterTransientError('test');
|
||||
expect(transientError.state).toBe(EntityErrorState.TRANSIENT);
|
||||
expect(transientError.code).toBe(EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_TRANSIENT);
|
||||
|
||||
const unknownError = new EntityDatabaseAdapterUnknownError('test');
|
||||
expect(unknownError.state).toBe(EntityErrorState.UNKNOWN);
|
||||
expect(unknownError.code).toBe(EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_UNKNOWN);
|
||||
|
||||
const checkError = new EntityDatabaseAdapterCheckConstraintError('test');
|
||||
expect(checkError.state).toBe(EntityErrorState.PERMANENT);
|
||||
expect(checkError.code).toBe(EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_CHECK_CONSTRAINT);
|
||||
|
||||
const exclusionError = new EntityDatabaseAdapterExclusionConstraintError('test');
|
||||
expect(exclusionError.state).toBe(EntityErrorState.PERMANENT);
|
||||
expect(exclusionError.code).toBe(
|
||||
EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_EXCLUSION_CONSTRAINT,
|
||||
);
|
||||
|
||||
const foreignKeyError = new EntityDatabaseAdapterForeignKeyConstraintError('test');
|
||||
expect(foreignKeyError.state).toBe(EntityErrorState.PERMANENT);
|
||||
expect(foreignKeyError.code).toBe(
|
||||
EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_FOREIGN_KEY_CONSTRAINT,
|
||||
);
|
||||
|
||||
const notNullError = new EntityDatabaseAdapterNotNullConstraintError('test');
|
||||
expect(notNullError.state).toBe(EntityErrorState.PERMANENT);
|
||||
expect(notNullError.code).toBe(EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_NOT_NULL_CONSTRAINT);
|
||||
|
||||
const uniqueError = new EntityDatabaseAdapterUniqueConstraintError('test');
|
||||
expect(uniqueError.state).toBe(EntityErrorState.PERMANENT);
|
||||
expect(uniqueError.code).toBe(EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_UNIQUE_CONSTRAINT);
|
||||
@@ -48,5 +83,13 @@ describe(EntityDatabaseAdapterError, () => {
|
||||
expect(excessiveDeleteError.code).toBe(
|
||||
EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_EXCESSIVE_DELETE_RESULT,
|
||||
);
|
||||
|
||||
const paginationCursorInvalidError = new EntityDatabaseAdapterPaginationCursorInvalidError(
|
||||
'test',
|
||||
);
|
||||
expect(paginationCursorInvalidError.state).toBe(EntityErrorState.PERMANENT);
|
||||
expect(paginationCursorInvalidError.code).toBe(
|
||||
EntityErrorCode.ERR_ENTITY_DATABASE_ADAPTER_PAGINATION_CURSOR_INVALID,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,13 +1,13 @@
|
||||
version: '2'
|
||||
services:
|
||||
redis:
|
||||
image: redis@sha256:97ec88159e6d7cfb5ddef9015386087d58a912de80112f06ebdfa78fa5989901
|
||||
image: redis@sha256:6f81e8915c60b065a524e6967e0ad1c639ba6efa84d669f823683ea04d9150ee
|
||||
ports:
|
||||
- '${REDIS_PORT}:6379'
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
postgres:
|
||||
image: postgres:18@sha256:edb0c08dc33befd0262ab3b5cf1c4f3f8cfd8b09e88c05b45adee29d6761ddb6
|
||||
image: postgres:18@sha256:5a5a84b19854a9ffaa54082c166ff4ec27473a361e496e5ea167f298f2da9722
|
||||
ports:
|
||||
- '${PGPORT}:5432'
|
||||
volumes:
|
||||
|
||||
Reference in new issue
Block a user