@requence/event-sourcing 1.4.1 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -5
- package/dist/{chunk-24xjphpk.js → chunk-06anxtk6.js} +6 -6
- package/dist/{chunk-24xjphpk.js.map → chunk-06anxtk6.js.map} +2 -2
- package/dist/{chunk-263c07sn.js → chunk-1mr4cwpw.js} +8 -8
- package/dist/{chunk-263c07sn.js.map → chunk-1mr4cwpw.js.map} +2 -2
- package/dist/{chunk-3ejt4h7x.js → chunk-2wqpktzp.js} +78 -66
- package/dist/chunk-2wqpktzp.js.map +10 -0
- package/dist/{chunk-dgzw1nj9.js → chunk-467qtkth.js} +8 -3
- package/dist/chunk-467qtkth.js.map +10 -0
- package/dist/chunk-6dn6wxg7.js +11 -0
- package/dist/{chunk-jd4q8fcf.js.map → chunk-6dn6wxg7.js.map} +2 -2
- package/dist/{chunk-pc1q4se9.js → chunk-9qtjz725.js} +2 -2
- package/dist/{chunk-pc1q4se9.js.map → chunk-9qtjz725.js.map} +2 -2
- package/dist/{chunk-tfy01fah.js → chunk-c7sytwpp.js} +4 -4
- package/dist/{chunk-tfy01fah.js.map → chunk-c7sytwpp.js.map} +2 -2
- package/dist/{chunk-vvv14qxy.js → chunk-d6jqbrq2.js} +33 -31
- package/dist/{chunk-vvv14qxy.js.map → chunk-d6jqbrq2.js.map} +3 -3
- package/dist/{chunk-vep2byx4.js → chunk-f95c3da4.js} +77 -64
- package/dist/chunk-f95c3da4.js.map +10 -0
- package/dist/chunk-fpjnk143.js +13 -0
- package/dist/{chunk-6q8mr1sg.js.map → chunk-fpjnk143.js.map} +2 -2
- package/dist/{chunk-8qn1nqnh.js → chunk-fxb9fbvz.js} +9 -9
- package/dist/{chunk-8qn1nqnh.js.map → chunk-fxb9fbvz.js.map} +2 -2
- package/dist/chunk-hmvwn02w.js +22 -0
- package/dist/{chunk-6yrb30rx.js.map → chunk-hmvwn02w.js.map} +2 -2
- package/dist/{chunk-ty9bhdvx.js → chunk-j4mwjx07.js} +5 -3
- package/dist/chunk-j4mwjx07.js.map +10 -0
- package/dist/{chunk-391m4mfp.js → chunk-jw9e0fr0.js} +13 -13
- package/dist/{chunk-391m4mfp.js.map → chunk-jw9e0fr0.js.map} +2 -2
- package/dist/chunk-mbxxvskx.js +67 -0
- package/dist/chunk-mbxxvskx.js.map +10 -0
- package/dist/{chunk-yn2sjcg3.js → chunk-nrhw3kde.js} +57 -8
- package/dist/chunk-nrhw3kde.js.map +10 -0
- package/dist/{chunk-57b88rt5.js → chunk-pcggjrxr.js} +32 -32
- package/dist/{chunk-57b88rt5.js.map → chunk-pcggjrxr.js.map} +2 -2
- package/dist/chunk-tkwe4r0y.js +22 -0
- package/dist/{chunk-2fe8jjdc.js.map → chunk-tkwe4r0y.js.map} +2 -2
- package/dist/{chunk-z388cd25.js → chunk-zkz6ew8c.js} +5 -5
- package/dist/{chunk-z388cd25.js.map → chunk-zkz6ew8c.js.map} +2 -2
- package/dist/createAggregateRoot.d.ts +1 -1
- package/dist/createAggregateRoot.d.ts.map +1 -1
- package/dist/createAggregateRoot.js +15 -14
- package/dist/createAggregateRoot.js.map +1 -1
- package/dist/createCheckpointApi.d.ts +30 -0
- package/dist/createCheckpointApi.d.ts.map +1 -1
- package/dist/createCheckpointApi.js +5 -5
- package/dist/createCheckpointApi.js.map +1 -1
- package/dist/createEventListener.js +8 -8
- package/dist/createEventListener.js.map +1 -1
- package/dist/createEventStore.js +16 -16
- package/dist/createEventStore.js.map +1 -1
- package/dist/createProcessManager.d.ts.map +1 -1
- package/dist/createProcessManager.js +12 -12
- package/dist/createProcessManager.js.map +1 -1
- package/dist/createProjection.d.ts.map +1 -1
- package/dist/createProjection.js +12 -12
- package/dist/createProjection.js.map +1 -1
- package/dist/drizzle/index.js +132 -132
- package/dist/drizzle/index.js.map +2 -2
- package/dist/drizzle/postgres.js +14 -14
- package/dist/drizzle/postgres.js.map +1 -1
- package/dist/errors.js +24 -24
- package/dist/errors.js.map +1 -1
- package/dist/index.js +63 -63
- package/dist/index.js.map +1 -1
- package/dist/lock.d.ts +11 -3
- package/dist/lock.d.ts.map +1 -1
- package/dist/lock.js +4 -2
- package/dist/lock.js.map +1 -1
- package/dist/memory/index.d.ts +14 -3
- package/dist/memory/index.d.ts.map +1 -1
- package/dist/memory/index.js +52 -45
- package/dist/memory/index.js.map +3 -3
- package/dist/memory/index.spec.d.ts +2 -0
- package/dist/memory/index.spec.d.ts.map +1 -0
- package/dist/redis/index.js +5 -4
- package/dist/redis/index.js.map +1 -1
- package/dist/redis/redisLock.d.ts +1 -1
- package/dist/redis/redisLock.d.ts.map +1 -1
- package/dist/redis/redisLock.js +5 -4
- package/dist/redis/redisLock.js.map +1 -1
- package/dist/refresh.js +9 -9
- package/dist/refresh.js.map +1 -1
- package/dist/replay.js +9 -9
- package/dist/replay.js.map +1 -1
- package/dist/snapshotGenerator.js +2 -2
- package/dist/superjson.js +8 -8
- package/dist/superjson.js.map +1 -1
- package/dist/surreal/index.d.ts +35 -0
- package/dist/surreal/index.d.ts.map +1 -0
- package/dist/surreal/index.js +415 -0
- package/dist/surreal/index.js.map +10 -0
- package/dist/surreal/index.spec.d.ts +2 -0
- package/dist/surreal/index.spec.d.ts.map +1 -0
- package/dist/surreal/schema.d.ts +12 -0
- package/dist/surreal/schema.d.ts.map +1 -0
- package/dist/surreal/schema.js +12 -0
- package/dist/surreal/schema.js.map +9 -0
- package/dist/tests/projectionMultiInstance.spec.d.ts +2 -0
- package/dist/tests/projectionMultiInstance.spec.d.ts.map +1 -0
- package/dist/tests/storageDriverContract.d.ts +19 -0
- package/dist/tests/storageDriverContract.d.ts.map +1 -0
- package/dist/transaction.js +1 -1
- package/dist/utils.js +6 -6
- package/dist/utils.js.map +1 -1
- package/dist/wrappedEventBuilder.js +8 -8
- package/dist/wrappedEventBuilder.js.map +1 -1
- package/package.json +19 -10
- package/src/createAggregateRoot.ts +24 -5
- package/src/createCheckpointApi.ts +98 -0
- package/src/createProcessManager.ts +7 -6
- package/src/createProjection.ts +91 -65
- package/src/lock.ts +17 -3
- package/src/memory/index.spec.ts +4 -0
- package/src/memory/index.ts +55 -29
- package/src/redis/redisLock.ts +6 -2
- package/src/surreal/index.spec.ts +152 -0
- package/src/surreal/index.ts +604 -0
- package/src/surreal/schema.ts +68 -0
- package/src/tests/aggregateRoot.spec.ts +48 -3
- package/src/tests/eventStore.spec.ts +1 -3
- package/src/tests/processManagerMultiInstance.spec.ts +18 -7
- package/src/tests/projectionMultiInstance.spec.ts +283 -0
- package/src/tests/storageDriverContract.ts +612 -0
- package/dist/chunk-2fe8jjdc.js +0 -22
- package/dist/chunk-3ejt4h7x.js.map +0 -10
- package/dist/chunk-6q8mr1sg.js +0 -13
- package/dist/chunk-6yrb30rx.js +0 -22
- package/dist/chunk-dgzw1nj9.js.map +0 -10
- package/dist/chunk-jd4q8fcf.js +0 -11
- package/dist/chunk-ty9bhdvx.js.map +0 -10
- package/dist/chunk-vep2byx4.js.map +0 -10
- package/dist/chunk-yn2sjcg3.js.map +0 -10
package/src/memory/index.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type {
|
|
2
|
+
AggregateRootSnapshotMethods,
|
|
3
|
+
AnyAggregateRoot,
|
|
4
|
+
} from '../createAggregateRoot.ts'
|
|
5
|
+
import type { Checkpoint, CheckpointMethods } from '../createCheckpointApi.ts'
|
|
6
|
+
import type { ProjectionSnapshotMethods } from '../createProjection.ts'
|
|
3
7
|
import {
|
|
4
8
|
type AggregateRootSnapshot,
|
|
5
9
|
ConcurrencyError,
|
|
@@ -13,16 +17,20 @@ import type {
|
|
|
13
17
|
EventStore,
|
|
14
18
|
EventStoreParamsWithAggregateRootSnapshots,
|
|
15
19
|
EventsFromRoot,
|
|
20
|
+
LoadEvents,
|
|
16
21
|
OnProgress,
|
|
17
22
|
} from '../createEventStore.ts'
|
|
18
23
|
import type { LockCreator } from '../lock.ts'
|
|
19
|
-
import type {
|
|
24
|
+
import type {
|
|
25
|
+
BaseInputEvent,
|
|
26
|
+
BaseOutputEvent,
|
|
27
|
+
MaybePromise,
|
|
28
|
+
StreamId,
|
|
29
|
+
} from '../utilityTypes.js'
|
|
20
30
|
|
|
21
31
|
export { createAggregateRoot } from '../index.ts'
|
|
22
32
|
|
|
23
|
-
export type OnEventsAppended = (
|
|
24
|
-
events: BaseOutputEvent[],
|
|
25
|
-
) => MaybePromise<void>
|
|
33
|
+
export type OnEventsAppended = (events: BaseOutputEvent[]) => MaybePromise<void>
|
|
26
34
|
|
|
27
35
|
interface MemoryEventStoreParams<Root extends AnyAggregateRoot> {
|
|
28
36
|
aggregateRoots: Root[]
|
|
@@ -34,33 +42,26 @@ interface MemoryEventStoreParams<Root extends AnyAggregateRoot> {
|
|
|
34
42
|
onEventsAppended?: OnEventsAppended
|
|
35
43
|
}
|
|
36
44
|
|
|
37
|
-
export function
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
export function createMemoryStorage(params?: {
|
|
46
|
+
onEventsAppended?: OnEventsAppended
|
|
47
|
+
}): {
|
|
48
|
+
loadEvents: LoadEvents
|
|
49
|
+
appendEvents: (
|
|
50
|
+
streamId: StreamId,
|
|
51
|
+
events: BaseInputEvent[],
|
|
52
|
+
expectedVersion: number,
|
|
53
|
+
) => BaseOutputEvent[]
|
|
54
|
+
checkpoint: CheckpointMethods
|
|
55
|
+
projectionSnapshot: ProjectionSnapshotMethods
|
|
56
|
+
aggregateRootSnapshot: AggregateRootSnapshotMethods
|
|
57
|
+
} {
|
|
50
58
|
const events: Event[] = []
|
|
51
59
|
const checkpoints = new Set<Checkpoint>()
|
|
52
60
|
const aggregateRootSnapshots = new Map<string, AggregateRootSnapshot>()
|
|
53
61
|
const projectionSnapshots = new Set<ProjectionSnapshot>()
|
|
54
62
|
const projectionAppliedCount = new Map<string, number>()
|
|
55
63
|
|
|
56
|
-
return
|
|
57
|
-
aggregateRoots,
|
|
58
|
-
postProcessEvent,
|
|
59
|
-
onProjectionReplay,
|
|
60
|
-
onProcessManagerRefresh,
|
|
61
|
-
autoInit,
|
|
62
|
-
lock,
|
|
63
|
-
|
|
64
|
+
return {
|
|
64
65
|
async *loadEvents(select, range) {
|
|
65
66
|
let streamTypes: string[] | null = null
|
|
66
67
|
let streamIds: string[] | null = null
|
|
@@ -131,7 +132,7 @@ export function createEventStore<const Root extends AnyAggregateRoot>({
|
|
|
131
132
|
}))
|
|
132
133
|
|
|
133
134
|
events.push(...extendedEvents)
|
|
134
|
-
onEventsAppended?.(extendedEvents)
|
|
135
|
+
params?.onEventsAppended?.(extendedEvents)
|
|
135
136
|
return extendedEvents
|
|
136
137
|
},
|
|
137
138
|
|
|
@@ -155,7 +156,10 @@ export function createEventStore<const Root extends AnyAggregateRoot>({
|
|
|
155
156
|
if (persisted) {
|
|
156
157
|
// Compare-and-swap: reject the write when another writer advanced the
|
|
157
158
|
// checkpoint, or when the caller expected to create a fresh one.
|
|
158
|
-
if (
|
|
159
|
+
if (
|
|
160
|
+
expectedVersion === null ||
|
|
161
|
+
persisted.version !== expectedVersion
|
|
162
|
+
) {
|
|
159
163
|
return false
|
|
160
164
|
}
|
|
161
165
|
persisted.lastEventPosition = checkpoint.lastEventPosition
|
|
@@ -232,5 +236,27 @@ export function createEventStore<const Root extends AnyAggregateRoot>({
|
|
|
232
236
|
}
|
|
233
237
|
},
|
|
234
238
|
},
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function createEventStore<const Root extends AnyAggregateRoot>({
|
|
243
|
+
aggregateRoots,
|
|
244
|
+
postProcessEvent,
|
|
245
|
+
onProjectionReplay,
|
|
246
|
+
onProcessManagerRefresh,
|
|
247
|
+
onEventsAppended,
|
|
248
|
+
autoInit,
|
|
249
|
+
lock,
|
|
250
|
+
}: MemoryEventStoreParams<Root>): EventStore<EventsFromRoot<Root>, Root, true> {
|
|
251
|
+
const storage = createMemoryStorage({ onEventsAppended })
|
|
252
|
+
|
|
253
|
+
return createBaseEventStore({
|
|
254
|
+
aggregateRoots,
|
|
255
|
+
postProcessEvent,
|
|
256
|
+
onProjectionReplay,
|
|
257
|
+
onProcessManagerRefresh,
|
|
258
|
+
autoInit,
|
|
259
|
+
lock,
|
|
260
|
+
...storage,
|
|
235
261
|
}) as any
|
|
236
262
|
}
|
package/src/redis/redisLock.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Redis } from 'ioredis'
|
|
2
2
|
|
|
3
|
-
import type
|
|
3
|
+
import { DEFAULT_LOCK_TTL, type LockCreator } from '../lock.ts'
|
|
4
4
|
|
|
5
5
|
const extendScript = `
|
|
6
6
|
if redis.call("get", KEYS[1]) == ARGV[1] then
|
|
@@ -18,7 +18,10 @@ const deleteScript = `
|
|
|
18
18
|
end
|
|
19
19
|
`
|
|
20
20
|
|
|
21
|
-
export function redisLock(
|
|
21
|
+
export function redisLock(
|
|
22
|
+
client: Redis,
|
|
23
|
+
defaultTtl = DEFAULT_LOCK_TTL,
|
|
24
|
+
): LockCreator {
|
|
22
25
|
return async (rawKey) => {
|
|
23
26
|
const key = Array.isArray(rawKey) ? rawKey.join(':') : rawKey
|
|
24
27
|
const token = crypto.randomUUID()
|
|
@@ -33,6 +36,7 @@ export function redisLock(client: Redis, defaultTtl = 5000): LockCreator {
|
|
|
33
36
|
|
|
34
37
|
let isReleased = false
|
|
35
38
|
return {
|
|
39
|
+
ttl: defaultTtl,
|
|
36
40
|
async extend(ttl) {
|
|
37
41
|
const extended = await client.eval(
|
|
38
42
|
extendScript,
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { afterAll, describe, expect, it } from 'bun:test'
|
|
2
|
+
import { createNodeEngines } from '@surrealdb/node'
|
|
3
|
+
import { Surreal } from 'surrealdb'
|
|
4
|
+
|
|
5
|
+
import { ConcurrencyError } from '../errors.ts'
|
|
6
|
+
import { runStorageDriverContract } from '../tests/storageDriverContract.ts'
|
|
7
|
+
import { createSurrealStorage, initSchema } from './index.ts'
|
|
8
|
+
|
|
9
|
+
// One shared embedded engine for the whole file; isolation comes from a
|
|
10
|
+
// fresh logical database per test (tests run sequentially).
|
|
11
|
+
let connection: Surreal | undefined
|
|
12
|
+
|
|
13
|
+
async function makeDatabase() {
|
|
14
|
+
if (!connection) {
|
|
15
|
+
connection = new Surreal({ engines: { ...createNodeEngines() } })
|
|
16
|
+
await connection.connect('mem://')
|
|
17
|
+
}
|
|
18
|
+
await connection.use({ namespace: 'test', database: crypto.randomUUID() })
|
|
19
|
+
return connection
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
afterAll(async () => {
|
|
23
|
+
await connection?.close()
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
runStorageDriverContract('surreal', async () => {
|
|
27
|
+
const database = await makeDatabase()
|
|
28
|
+
return createSurrealStorage({ database })
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
describe('surreal driver specifics', () => {
|
|
32
|
+
it('initializes the schema idempotently', async () => {
|
|
33
|
+
const database = await makeDatabase()
|
|
34
|
+
await initSchema(database)
|
|
35
|
+
await initSchema(database)
|
|
36
|
+
|
|
37
|
+
const storage = createSurrealStorage({ database })
|
|
38
|
+
const persisted = await storage.appendEvents(
|
|
39
|
+
{ stream: 'order', id: 'o1' },
|
|
40
|
+
[{ type: 'created', payload: {}, metadata: {}, schemaVersion: 1 }],
|
|
41
|
+
0,
|
|
42
|
+
)
|
|
43
|
+
expect(persisted[0].position).toBe(1)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('skips schema creation when initSchema is disabled', async () => {
|
|
47
|
+
const database = await makeDatabase()
|
|
48
|
+
const storage = createSurrealStorage({ database, initSchema: false })
|
|
49
|
+
|
|
50
|
+
let error: unknown
|
|
51
|
+
try {
|
|
52
|
+
await storage.appendEvents(
|
|
53
|
+
{ stream: 'order', id: 'o1' },
|
|
54
|
+
[{ type: 'created', payload: {}, metadata: {}, schemaVersion: 1 }],
|
|
55
|
+
0,
|
|
56
|
+
)
|
|
57
|
+
} catch (err) {
|
|
58
|
+
error = err
|
|
59
|
+
}
|
|
60
|
+
// Without the SCHEMAFULL tables the counter arithmetic cannot resolve.
|
|
61
|
+
expect(error).toBeTruthy()
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('assigns gapless, monotonically increasing global positions', async () => {
|
|
65
|
+
const database = await makeDatabase()
|
|
66
|
+
const storage = createSurrealStorage({ database })
|
|
67
|
+
|
|
68
|
+
await storage.appendEvents(
|
|
69
|
+
{ stream: 'order', id: 'o1' },
|
|
70
|
+
[
|
|
71
|
+
{ type: 'created', payload: {}, metadata: {}, schemaVersion: 1 },
|
|
72
|
+
{ type: 'updated', payload: {}, metadata: {}, schemaVersion: 1 },
|
|
73
|
+
],
|
|
74
|
+
0,
|
|
75
|
+
)
|
|
76
|
+
const second = await storage.appendEvents(
|
|
77
|
+
{ stream: 'order', id: 'o2' },
|
|
78
|
+
[{ type: 'created', payload: {}, metadata: {}, schemaVersion: 1 }],
|
|
79
|
+
0,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
expect(second[0].position).toBe(3)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('serializes concurrent appends to different streams without losing positions', async () => {
|
|
86
|
+
const database = await makeDatabase()
|
|
87
|
+
const storage = createSurrealStorage({ database })
|
|
88
|
+
|
|
89
|
+
const results = await Promise.all(
|
|
90
|
+
Array.from({ length: 8 }, (_, index) =>
|
|
91
|
+
storage.appendEvents(
|
|
92
|
+
{ stream: 'order', id: `o${index}` },
|
|
93
|
+
[{ type: 'created', payload: {}, metadata: {}, schemaVersion: 1 }],
|
|
94
|
+
0,
|
|
95
|
+
),
|
|
96
|
+
),
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
const positions = results
|
|
100
|
+
.map(([event]) => event.position)
|
|
101
|
+
.toSorted((a, b) => a - b)
|
|
102
|
+
expect(positions).toEqual([1, 2, 3, 4, 5, 6, 7, 8])
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('reports the current version in ConcurrencyError', async () => {
|
|
106
|
+
const database = await makeDatabase()
|
|
107
|
+
const storage = createSurrealStorage({ database })
|
|
108
|
+
|
|
109
|
+
await storage.appendEvents(
|
|
110
|
+
{ stream: 'order', id: 'o1' },
|
|
111
|
+
[
|
|
112
|
+
{ type: 'created', payload: {}, metadata: {}, schemaVersion: 1 },
|
|
113
|
+
{ type: 'updated', payload: {}, metadata: {}, schemaVersion: 1 },
|
|
114
|
+
],
|
|
115
|
+
0,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
let error: unknown
|
|
119
|
+
try {
|
|
120
|
+
await storage.appendEvents(
|
|
121
|
+
{ stream: 'order', id: 'o1' },
|
|
122
|
+
[{ type: 'updated', payload: {}, metadata: {}, schemaVersion: 1 }],
|
|
123
|
+
1,
|
|
124
|
+
)
|
|
125
|
+
} catch (err) {
|
|
126
|
+
error = err
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
expect(error).toBeInstanceOf(ConcurrencyError)
|
|
130
|
+
expect((error as Error).message).toContain('expected v1, actual v2')
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it('notifies onEventsAppended with the persisted events', async () => {
|
|
134
|
+
const database = await makeDatabase()
|
|
135
|
+
let appended: unknown[] = []
|
|
136
|
+
const storage = createSurrealStorage({
|
|
137
|
+
database,
|
|
138
|
+
onEventsAppended: (events) => {
|
|
139
|
+
appended = events
|
|
140
|
+
},
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
await storage.appendEvents(
|
|
144
|
+
{ stream: 'order', id: 'o1' },
|
|
145
|
+
[{ type: 'created', payload: { n: 1 }, metadata: {}, schemaVersion: 1 }],
|
|
146
|
+
0,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
expect(appended).toHaveLength(1)
|
|
150
|
+
expect((appended[0] as any).position).toBe(1)
|
|
151
|
+
})
|
|
152
|
+
})
|