feat(namespace): NO SPECIAL NAMES + storage fidelity — the ruled completion of the field-addressing law
The write side of the law, ruled 2026-08-03: data is either in main space where developers can use anything, or it is in system.*. - The reserved-name write door DIES: add/update/relate/updateRelation metadata bags accept EVERY name (confidence, type, id, data, level, content, ...) as ordinary user fields — indexed, filterable, sortable, aggregatable, identical to any other field. The remap/enforce/warn machinery, the reservedFieldPolicy config (now a typed init refusal), and the compile-time metadata key bans are all removed. The one write refusal left: keys spelled 'system.*' (namespace forgery), now enforced on all four write doors. - STORED RECORDS GO NESTED (v2): engine fields top-level, the user bag nested verbatim under 'metadata', sealed by a format stamp — by-name storage discrimination is unsound once colliders are admitted. Legacy flat records stay readable forever through the shape-aware splitters (sound for them: the old door refused colliders). Time travel rides the same split (generation store snapshots whole records). - Name-based index exclusions DIE: user frame indexes every name; the excludeFields/indexedFields knobs and their silent-[] holes are gone; bulk-payload protection is value-shape only, uniform across names. - Consumer-sweep findings fixed in the same wave: per-type counts read the frozen 'system.type' column (addToIndex sort, affinity tracking, cold-count rehydration, VFS type bitmaps — legacy 'noun' fallback for pre-rebuild reads); resolveHiddenIds addresses 'system.visibility' (bare 'visibility' was a silent no-op under the law — VFS/system entities leaked into default reads). - Fidelity fallout fixed in the owning layers: readEntityFieldAddress reads the bag first (colliders were absent-shadowed by its own guard) and never serves system addresses from the bag; blob history refs read the bag shape-aware; migration transforms now receive ONE normalized view (engine fields + nested bag) regardless of stored era, and stray flat-habit keys refuse with the fix in the message. - THE REOPEN-COLLIDER CONFORMANCE CASE (required before any RC counts as gates-green): all ten collider names + plumbing names written as user fields, verified verbatim + queryable across live reads, flush+reopen, a forced epoch rebuild, and asOf time travel; relation mirror; forgery refusals; legacy flat-record compat. 8/8 green. Gates: unit 1901/1901 (exit 0) · integration 758 (exit 0) · conformance 27/27 (exit 0) · consumer test sweep migrated (10 files).
This commit is contained in:
parent
48a6130a50
commit
24bf6cdbc5
32 changed files with 1355 additions and 1905 deletions
|
|
@ -164,19 +164,19 @@ describe('BR-ADV-FEATURES-BUN regression', () => {
|
|||
await b.close()
|
||||
})
|
||||
|
||||
it('groupBy "noun" resolves to the entity type, not null', async () => {
|
||||
it('groupBy "system.type" resolves to the entity type, not null (the legacy "noun" alias is dead)', async () => {
|
||||
const b: any = new Brainy({ requireSubtype: false, storage: { type: 'memory' } })
|
||||
await b.init()
|
||||
await b.add({ data: 'p', type: NounType.Person })
|
||||
b.defineAggregate({
|
||||
name: 'byNoun',
|
||||
source: { type: NounType.Person },
|
||||
groupBy: ['noun'],
|
||||
groupBy: ['system.type'],
|
||||
metrics: { count: { op: 'count' } }
|
||||
})
|
||||
const rows: any[] = await b.find({ aggregate: 'byNoun' })
|
||||
expect(rows.length).toBe(1)
|
||||
expect(rows[0].groupKey.noun).toBe(NounType.Person)
|
||||
expect(rows[0].groupKey['system.type']).toBe(NounType.Person)
|
||||
await b.close()
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -42,8 +42,11 @@ describe('aggregation + query field-resolution law', () => {
|
|||
it('reserved-field groupBy decrements on delete (the drift bug)', async () => {
|
||||
brain.defineAggregate({
|
||||
name: 'by_subtype',
|
||||
// system.subtype — subtype is an add() param (an engine scalar), never
|
||||
// a user metadata field; bare 'subtype' now addresses the user's own
|
||||
// metadata bag under the sealed field-addressing law.
|
||||
source: { type: NounType.Document },
|
||||
groupBy: ['subtype'],
|
||||
groupBy: ['system.subtype'],
|
||||
metrics: { count: { op: 'count' } }
|
||||
})
|
||||
|
||||
|
|
@ -60,7 +63,7 @@ describe('aggregation + query field-resolution law', () => {
|
|||
}
|
||||
let groups = await brain.queryAggregate('by_subtype')
|
||||
expect(groups).toHaveLength(1)
|
||||
expect(groups[0].groupKey).toEqual({ subtype: 'note' })
|
||||
expect(groups[0].groupKey).toEqual({ 'system.subtype': 'note' })
|
||||
expect(groups[0].metrics.count).toBe(5)
|
||||
|
||||
await brain.remove(ids[0])
|
||||
|
|
@ -76,7 +79,7 @@ describe('aggregation + query field-resolution law', () => {
|
|||
brain.defineAggregate({
|
||||
name: 'by_subtype',
|
||||
source: { type: NounType.Document },
|
||||
groupBy: ['subtype'],
|
||||
groupBy: ['system.subtype'],
|
||||
metrics: { count: { op: 'count' } }
|
||||
})
|
||||
const id = await brain.add({
|
||||
|
|
@ -88,7 +91,7 @@ describe('aggregation + query field-resolution law', () => {
|
|||
|
||||
const groups = await brain.queryAggregate('by_subtype')
|
||||
const byKey = Object.fromEntries(
|
||||
groups.map((g) => [String(g.groupKey.subtype), g.metrics.count])
|
||||
groups.map((g) => [String(g.groupKey['system.subtype']), g.metrics.count])
|
||||
)
|
||||
expect(byKey['published']).toBe(1)
|
||||
// The old group must be gone or zero — never still counting the entity.
|
||||
|
|
@ -98,7 +101,7 @@ describe('aggregation + query field-resolution law', () => {
|
|||
it('source.where on a reserved field filters instead of matching nothing', async () => {
|
||||
brain.defineAggregate({
|
||||
name: 'notes_only',
|
||||
source: { type: NounType.Document, where: { subtype: 'note' } },
|
||||
source: { type: NounType.Document, where: { 'system.subtype': 'note' } },
|
||||
groupBy: ['team'],
|
||||
metrics: { count: { op: 'count' } }
|
||||
})
|
||||
|
|
|
|||
|
|
@ -331,8 +331,10 @@ describe('Comprehensive All-APIs Test', () => {
|
|||
it('should handle metadata queries efficiently', async () => {
|
||||
const start = Date.now()
|
||||
|
||||
// system.type — the legacy where.type→noun alias is dead; bare 'type'
|
||||
// in where now addresses the user's own metadata field.
|
||||
const results = await brain.find({
|
||||
where: { type: NounType.Document },
|
||||
where: { 'system.type': NounType.Document },
|
||||
limit: 100
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest'
|
|||
import * as fs from 'node:fs'
|
||||
import * as os from 'node:os'
|
||||
import * as path from 'node:path'
|
||||
import { Brainy, ProtectedArtifactError, type CommitFact } from '../../src/index.js'
|
||||
import { Brainy, ProtectedArtifactError, splitNounMetadataRecord, type CommitFact } from '../../src/index.js'
|
||||
|
||||
async function allFacts(brain: any): Promise<CommitFact[]> {
|
||||
const scan = brain.scanFacts()
|
||||
|
|
@ -65,7 +65,13 @@ describe('fact log dual-write (memory adapter)', () => {
|
|||
const updateFact = facts[facts.length - 1]
|
||||
const op = updateFact.ops.find((o) => o.id === id)!
|
||||
expect(op.record).not.toBeNull()
|
||||
expect((op.record!.metadata as any).v).toBe('new')
|
||||
// The fact log is byte-faithful: op.record.metadata is the RAW stored
|
||||
// record (v2 nested-bag since the field-addressing law) — read the user
|
||||
// field through the shape-aware split, like every other reader.
|
||||
const { custom } = splitNounMetadataRecord(
|
||||
op.record!.metadata as Record<string, unknown>
|
||||
)
|
||||
expect(custom.v).toBe('new')
|
||||
})
|
||||
|
||||
it('a transact commits ONE fact carrying all its ops, with meta', async () => {
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
* @module tests/integration/lens-consistency
|
||||
* @description The three metadata "lenses" over one corpus must agree with
|
||||
* canonical ground truth id-for-id, warm AND after a cold reopen:
|
||||
* - combined: find({ type: T, where: { subtype: S } })
|
||||
* - subtype-only: find({ where: { subtype: S } })
|
||||
* - combined: find({ type: T, where: { 'system.subtype': S } })
|
||||
* - subtype-only: find({ where: { 'system.subtype': S } })
|
||||
* - type-only: find({ type: T })
|
||||
* Ported from the fresh-brain probe that closed the type+subtype lens-drop
|
||||
* investigation (a restored pre-8.2.2 torn capture had entities visible to the
|
||||
|
|
@ -63,8 +63,11 @@ async function assertAllLenses(brain: any): Promise<void> {
|
|||
const subtypes = [...new Set(CORPUS.map((c) => c.subtype))]
|
||||
|
||||
for (const { type, subtype } of CORPUS) {
|
||||
const combined = idSet(await brain.find({ type, where: { subtype }, limit: 1000 }))
|
||||
const subtypeOnly = idSet(await brain.find({ where: { subtype }, limit: 1000 }))
|
||||
// system.subtype — subtype is an add()/update() param (an engine scalar),
|
||||
// never a user metadata field; bare 'subtype' now addresses the user's
|
||||
// own metadata bag under the sealed field-addressing law.
|
||||
const combined = idSet(await brain.find({ type, where: { 'system.subtype': subtype }, limit: 1000 }))
|
||||
const subtypeOnly = idSet(await brain.find({ where: { 'system.subtype': subtype }, limit: 1000 }))
|
||||
const truthPair = await groundTruth(brain, { type, subtype })
|
||||
const truthSubtype = await groundTruth(brain, { subtype })
|
||||
|
||||
|
|
@ -82,7 +85,7 @@ async function assertAllLenses(brain: any): Promise<void> {
|
|||
// Count cross-check against the corpus definition itself.
|
||||
for (const subtype of subtypes) {
|
||||
const expected = CORPUS.filter((c) => c.subtype === subtype).reduce((s, c) => s + c.count, 0)
|
||||
const got = (await brain.find({ where: { subtype }, limit: 1000 })).length
|
||||
const got = (await brain.find({ where: { 'system.subtype': subtype }, limit: 1000 })).length
|
||||
expect(got).toBe(expected)
|
||||
}
|
||||
}
|
||||
|
|
@ -123,16 +126,16 @@ describe('lens consistency — combined vs subtype-only vs canonical ground trut
|
|||
|
||||
it('after an update() flips type AND subtype, every lens tracks the move exactly', async () => {
|
||||
// The historical cross-bucket-staleness path: change (concept, action) -> (task, review).
|
||||
const victims = await brain.find({ type: 'concept', where: { subtype: 'action' }, limit: 1 })
|
||||
const victims = await brain.find({ type: 'concept', where: { 'system.subtype': 'action' }, limit: 1 })
|
||||
expect(victims.length).toBe(1)
|
||||
const id = victims[0].id
|
||||
await brain.update({ id, type: 'task', subtype: 'review' })
|
||||
|
||||
const oldCombined = idSet(await brain.find({ type: 'concept', where: { subtype: 'action' }, limit: 1000 }))
|
||||
const oldCombined = idSet(await brain.find({ type: 'concept', where: { 'system.subtype': 'action' }, limit: 1000 }))
|
||||
expect(oldCombined.has(id)).toBe(false) // unposted from the old buckets
|
||||
const newCombined = idSet(await brain.find({ type: 'task', where: { subtype: 'review' }, limit: 1000 }))
|
||||
const newCombined = idSet(await brain.find({ type: 'task', where: { 'system.subtype': 'review' }, limit: 1000 }))
|
||||
expect(newCombined.has(id)).toBe(true) // posted to the new buckets
|
||||
const subtypeOnly = idSet(await brain.find({ where: { subtype: 'review' }, limit: 1000 }))
|
||||
const subtypeOnly = idSet(await brain.find({ where: { 'system.subtype': 'review' }, limit: 1000 }))
|
||||
expect(subtypeOnly.has(id)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -20,6 +20,16 @@ import { MigrationRunner, MIGRATIONS } from '../../src/migration/index.js'
|
|||
import type { Migration } from '../../src/migration/index.js'
|
||||
import { NounType, VerbType } from '../../src/types/graphTypes.js'
|
||||
|
||||
// THE VIEW CONTRACT (field-addressing law): transforms receive engine fields
|
||||
// top-level and the USER's bag nested under `metadata` — user-field changes
|
||||
// go inside the bag. These two helpers keep the one-liner migrations tidy.
|
||||
const bagOf = (m: Record<string, unknown>): Record<string, unknown> =>
|
||||
m.metadata as Record<string, unknown>
|
||||
const withBag = (
|
||||
m: Record<string, unknown>,
|
||||
patch: Record<string, unknown>
|
||||
): Record<string, unknown> => ({ ...m, metadata: { ...bagOf(m), ...patch } })
|
||||
|
||||
// Helper to temporarily inject migrations into the MIGRATIONS array
|
||||
function withMigrations(migrations: Migration[], fn: () => Promise<void>): Promise<void> {
|
||||
const original = MIGRATIONS.splice(0, MIGRATIONS.length)
|
||||
|
|
@ -78,9 +88,11 @@ describe('Migration System', () => {
|
|||
description: 'Add version field to entities with status',
|
||||
applies: 'nouns',
|
||||
transform: (m) => {
|
||||
// Only transform entities that have our specific 'status' field
|
||||
if ('status' in m && !('version' in m)) {
|
||||
return { ...m, version: 1 }
|
||||
// Only transform entities that have our specific 'status' USER field
|
||||
// (user fields live in the nested bag — the view contract).
|
||||
const bag = m.metadata as Record<string, unknown>
|
||||
if ('status' in bag && !('version' in bag)) {
|
||||
return { ...m, metadata: { ...bag, version: 1 } }
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
|
@ -94,7 +106,8 @@ describe('Migration System', () => {
|
|||
// All 3 entities have 'status' metadata
|
||||
expect(p.affectedEntities).toBeGreaterThanOrEqual(3)
|
||||
expect(p.sampleChanges.length).toBeGreaterThan(0)
|
||||
expect(p.sampleChanges[0].after.version).toBe(1)
|
||||
// Samples carry the VIEW shape: user fields inside `.metadata`.
|
||||
expect(p.sampleChanges[0].after.metadata.version).toBe(1)
|
||||
|
||||
// Verify no data was modified (dry-run)
|
||||
const entity = await brain.get(id1)
|
||||
|
|
@ -111,9 +124,10 @@ describe('Migration System', () => {
|
|||
description: 'Rename state to status',
|
||||
applies: 'nouns',
|
||||
transform: (m) => {
|
||||
if ('state' in m) {
|
||||
const { state, ...rest } = m
|
||||
return { ...rest, status: state }
|
||||
const bag = m.metadata as Record<string, unknown>
|
||||
if ('state' in bag) {
|
||||
const { state, ...rest } = bag
|
||||
return { ...m, metadata: { ...rest, status: state } }
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
|
@ -124,11 +138,12 @@ describe('Migration System', () => {
|
|||
const p = preview as any
|
||||
expect(p.sampleChanges.length).toBeGreaterThanOrEqual(1)
|
||||
|
||||
// Find the sample for our entity (it has the 'state' field)
|
||||
const sample = p.sampleChanges.find((s: any) => s.before.state === 'draft')
|
||||
// Find the sample for our entity (it has the 'state' USER field —
|
||||
// samples carry the VIEW shape, user fields inside `.metadata`)
|
||||
const sample = p.sampleChanges.find((s: any) => s.before.metadata.state === 'draft')
|
||||
expect(sample).toBeDefined()
|
||||
expect(sample.after.status).toBe('draft')
|
||||
expect(sample.after.state).toBeUndefined()
|
||||
expect(sample.after.metadata.status).toBe('draft')
|
||||
expect(sample.after.metadata.state).toBeUndefined()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -149,8 +164,8 @@ describe('Migration System', () => {
|
|||
description: 'Add migrated flag to entities with priority',
|
||||
applies: 'nouns',
|
||||
transform: (m) => {
|
||||
if ('priority' in m && !('migrated' in m)) {
|
||||
return { ...m, migrated: true }
|
||||
if ('priority' in bagOf(m) && !('migrated' in bagOf(m))) {
|
||||
return withBag(m, { migrated: true })
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
|
@ -179,8 +194,8 @@ describe('Migration System', () => {
|
|||
description: 'Uppercase status field only when present',
|
||||
applies: 'nouns',
|
||||
transform: (m) => {
|
||||
if (typeof m.status === 'string') {
|
||||
return { ...m, status: (m.status as string).toUpperCase() }
|
||||
if (typeof bagOf(m).status === 'string') {
|
||||
return withBag(m, { status: (bagOf(m).status as string).toUpperCase() })
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
|
@ -203,7 +218,7 @@ describe('Migration System', () => {
|
|||
version: '1.0.0',
|
||||
description: 'Double count',
|
||||
applies: 'nouns',
|
||||
transform: (m) => typeof m.count === 'number' ? { ...m, count: (m.count as number) * 2 } : null
|
||||
transform: (m) => typeof bagOf(m).count === 'number' ? withBag(m, { count: (bagOf(m).count as number) * 2 }) : null
|
||||
}
|
||||
|
||||
const migration2: Migration = {
|
||||
|
|
@ -211,7 +226,7 @@ describe('Migration System', () => {
|
|||
version: '1.1.0',
|
||||
description: 'Add 10 to count',
|
||||
applies: 'nouns',
|
||||
transform: (m) => typeof m.count === 'number' ? { ...m, count: (m.count as number) + 10 } : null
|
||||
transform: (m) => typeof bagOf(m).count === 'number' ? withBag(m, { count: (bagOf(m).count as number) + 10 }) : null
|
||||
}
|
||||
|
||||
await withMigrations([migration1, migration2], async () => {
|
||||
|
|
@ -229,7 +244,7 @@ describe('Migration System', () => {
|
|||
version: '1.0.0',
|
||||
description: 'Increment v',
|
||||
applies: 'nouns',
|
||||
transform: (m) => typeof m.v === 'number' ? { ...m, v: (m.v as number) + 1 } : null
|
||||
transform: (m) => typeof bagOf(m).v === 'number' ? withBag(m, { v: (bagOf(m).v as number) + 1 }) : null
|
||||
}
|
||||
|
||||
await withMigrations([migration], async () => {
|
||||
|
|
@ -266,7 +281,7 @@ describe('Migration System', () => {
|
|||
version: '2.0.0',
|
||||
description: 'Add y field to entities with x',
|
||||
applies: 'nouns',
|
||||
transform: (m) => 'x' in m && !('y' in m) ? { ...m, y: 2 } : null
|
||||
transform: (m) => 'x' in bagOf(m) && !('y' in bagOf(m)) ? withBag(m, { y: 2 }) : null
|
||||
}
|
||||
|
||||
await withMigrations([migration], async () => {
|
||||
|
|
@ -290,8 +305,8 @@ describe('Migration System', () => {
|
|||
description: 'Replace original with migrated',
|
||||
applies: 'nouns',
|
||||
transform: (m) => {
|
||||
if (m.original === true) {
|
||||
return { ...m, original: false, migrated: true }
|
||||
if (bagOf(m).original === true) {
|
||||
return withBag(m, { original: false, migrated: true })
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
|
@ -323,7 +338,7 @@ describe('Migration System', () => {
|
|||
version: '4.0.0',
|
||||
description: 'Add field',
|
||||
applies: 'nouns',
|
||||
transform: (m) => 'q' in m && !('r' in m) ? { ...m, r: 2 } : null
|
||||
transform: (m) => 'q' in bagOf(m) && !('r' in bagOf(m)) ? withBag(m, { r: 2 }) : null
|
||||
}
|
||||
|
||||
await withMigrations([migration], async () => {
|
||||
|
|
@ -384,7 +399,7 @@ describe('Migration System', () => {
|
|||
version: '1.0.0',
|
||||
description: 'Auto migrate test',
|
||||
applies: 'nouns',
|
||||
transform: (m) => 'legacy' in m ? { ...m, legacy: false, upgraded: true } : null
|
||||
transform: (m) => 'legacy' in bagOf(m) ? withBag(m, { legacy: false, upgraded: true }) : null
|
||||
}
|
||||
|
||||
await withMigrations([migration], async () => {
|
||||
|
|
@ -410,7 +425,7 @@ describe('Migration System', () => {
|
|||
version: '1.0.0',
|
||||
description: 'Add y to entities with x',
|
||||
applies: 'nouns',
|
||||
transform: (m) => 'x' in m ? { ...m, y: true } : null
|
||||
transform: (m) => 'x' in bagOf(m) ? withBag(m, { y: true }) : null
|
||||
}
|
||||
|
||||
const progressCalls: any[] = []
|
||||
|
|
@ -444,7 +459,7 @@ describe('Migration System', () => {
|
|||
version: '1.0.0',
|
||||
description: 'Increment v on entities that have it',
|
||||
applies: 'nouns',
|
||||
transform: (m) => typeof m.v === 'number' ? { ...m, v: (m.v as number) + 1 } : null
|
||||
transform: (m) => typeof bagOf(m).v === 'number' ? withBag(m, { v: (bagOf(m).v as number) + 1 }) : null
|
||||
}
|
||||
|
||||
await withMigrations([migration], async () => {
|
||||
|
|
@ -477,9 +492,10 @@ describe('Migration System', () => {
|
|||
description: 'Rename strength to intensity',
|
||||
applies: 'verbs',
|
||||
transform: (m) => {
|
||||
if ('strength' in m) {
|
||||
const { strength, ...rest } = m
|
||||
return { ...rest, intensity: strength }
|
||||
const bag = bagOf(m)
|
||||
if ('strength' in bag) {
|
||||
const { strength, ...rest } = bag
|
||||
return { ...m, metadata: { ...rest, intensity: strength } }
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
|
@ -507,7 +523,7 @@ describe('Migration System', () => {
|
|||
version: '1.0.0',
|
||||
description: 'Update tag from old to new',
|
||||
applies: 'both',
|
||||
transform: (m) => m.tag === 'old' ? { ...m, tag: 'new' } : null
|
||||
transform: (m) => bagOf(m).tag === 'old' ? withBag(m, { tag: 'new' }) : null
|
||||
}
|
||||
|
||||
await withMigrations([migration], async () => {
|
||||
|
|
@ -577,11 +593,11 @@ describe('Migration System', () => {
|
|||
description: 'Transform that throws on non-number values',
|
||||
applies: 'nouns',
|
||||
transform: (m) => {
|
||||
if ('value' in m) {
|
||||
if (typeof m.value !== 'number') {
|
||||
if ('value' in bagOf(m)) {
|
||||
if (typeof bagOf(m).value !== 'number') {
|
||||
throw new Error('value must be a number')
|
||||
}
|
||||
return { ...m, value: (m.value as number) * 10 }
|
||||
return withBag(m, { value: (bagOf(m).value as number) * 10 })
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
|
@ -615,7 +631,7 @@ describe('Migration System', () => {
|
|||
description: 'Always throws',
|
||||
applies: 'nouns',
|
||||
transform: (m) => {
|
||||
if ('boom' in m) {
|
||||
if ('boom' in bagOf(m)) {
|
||||
throw new Error('deliberate failure')
|
||||
}
|
||||
return null
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ describe('find({ orderBy }) sort bug regression', () => {
|
|||
|
||||
const results = await brain.find({
|
||||
type: NounType.Concept,
|
||||
orderBy: 'createdAt',
|
||||
orderBy: 'system.createdAt',
|
||||
order: 'desc',
|
||||
limit: 1
|
||||
})
|
||||
|
|
@ -76,7 +76,7 @@ describe('find({ orderBy }) sort bug regression', () => {
|
|||
|
||||
const results = await brain.find({
|
||||
type: NounType.Concept,
|
||||
orderBy: 'createdAt',
|
||||
orderBy: 'system.createdAt',
|
||||
order: 'asc',
|
||||
limit: 1
|
||||
})
|
||||
|
|
@ -94,7 +94,7 @@ describe('find({ orderBy }) sort bug regression', () => {
|
|||
|
||||
const results = await brain.find({
|
||||
type: NounType.Concept,
|
||||
orderBy: 'createdAt',
|
||||
orderBy: 'system.createdAt',
|
||||
order: 'desc'
|
||||
})
|
||||
|
||||
|
|
@ -115,7 +115,7 @@ describe('find({ orderBy }) sort bug regression', () => {
|
|||
|
||||
const results = await brain.find({
|
||||
type: NounType.Concept,
|
||||
orderBy: 'updatedAt',
|
||||
orderBy: 'system.updatedAt',
|
||||
order: 'desc',
|
||||
limit: 1
|
||||
})
|
||||
|
|
@ -136,7 +136,7 @@ describe('find({ orderBy }) sort bug regression', () => {
|
|||
const id3 = await brain.add({ data: 'third', type: NounType.Concept })
|
||||
|
||||
const results = await brain.find({
|
||||
orderBy: 'createdAt',
|
||||
orderBy: 'system.createdAt',
|
||||
order: 'desc',
|
||||
limit: 2
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue