@bjornpagen/bumbledb 0.19.0 → 0.19.2

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.
Files changed (50) hide show
  1. package/COOKBOOK.md +2 -0
  2. package/README.md +1 -0
  3. package/dist/closed.d.ts +9 -9
  4. package/dist/closed.d.ts.map +1 -1
  5. package/dist/closed.js +27 -18
  6. package/dist/closed.js.map +1 -1
  7. package/dist/face.d.ts +18 -23
  8. package/dist/face.d.ts.map +1 -1
  9. package/dist/face.js.map +1 -1
  10. package/dist/fields.d.ts +54 -8
  11. package/dist/fields.d.ts.map +1 -1
  12. package/dist/fields.js +28 -1
  13. package/dist/fields.js.map +1 -1
  14. package/dist/index.d.ts +2 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/judgment.d.ts +13 -0
  18. package/dist/judgment.d.ts.map +1 -0
  19. package/dist/judgment.js +11 -0
  20. package/dist/judgment.js.map +1 -0
  21. package/dist/law.d.ts +2 -1
  22. package/dist/law.d.ts.map +1 -1
  23. package/dist/law.js.map +1 -1
  24. package/dist/marshal.d.ts +2 -2
  25. package/dist/marshal.d.ts.map +1 -1
  26. package/dist/marshal.js.map +1 -1
  27. package/dist/query/atom.d.ts +10 -6
  28. package/dist/query/atom.d.ts.map +1 -1
  29. package/dist/query/atom.js.map +1 -1
  30. package/dist/query/lower.d.ts.map +1 -1
  31. package/dist/query/lower.js +4 -4
  32. package/dist/query/lower.js.map +1 -1
  33. package/dist/query/scope.d.ts +38 -31
  34. package/dist/query/scope.d.ts.map +1 -1
  35. package/dist/query/scope.js +17 -13
  36. package/dist/query/scope.js.map +1 -1
  37. package/dist/statements.js +2 -2
  38. package/dist/statements.js.map +1 -1
  39. package/package.json +3 -3
  40. package/src/closed.ts +85 -51
  41. package/src/face.ts +18 -24
  42. package/src/fields.ts +85 -7
  43. package/src/index.ts +5 -0
  44. package/src/judgment.ts +25 -0
  45. package/src/law.ts +2 -1
  46. package/src/marshal.ts +3 -3
  47. package/src/query/atom.ts +6 -5
  48. package/src/query/lower.ts +17 -7
  49. package/src/query/scope.ts +79 -47
  50. package/src/statements.ts +3 -3
@@ -1,6 +1,6 @@
1
1
  import * as errors from "@superbuilders/errors"
2
2
  import { sealedFieldsOf } from "#closed.ts"
3
- import type { AnyField, ClosedRoster } from "#fields.ts"
3
+ import type { AnyClosedRoster, AnyField } from "#fields.ts"
4
4
  import { assertDeclarationOrderKey, isIntervalValue, literalShapeError, rosterOf } from "#fields.ts"
5
5
  import type { ClassRecordOf, SchemaClasses } from "#law.ts"
6
6
  import type {
@@ -58,7 +58,16 @@ import type {
58
58
  ShapeOf,
59
59
  VarsOf
60
60
  } from "#query/scope.ts"
61
- import { fieldJoins, inferred, isTerm, makeParam, makeSetParam, renderFieldKind, term } from "#query/scope.ts"
61
+ import {
62
+ fieldAntiJoins,
63
+ fieldJoins,
64
+ inferred,
65
+ isTerm,
66
+ makeParam,
67
+ makeSetParam,
68
+ renderFieldKind,
69
+ term
70
+ } from "#query/scope.ts"
62
71
  import type { AnySchema, Schema, SchemaRelations } from "#schema.ts"
63
72
 
64
73
  type QueryRelation<Rels extends SchemaRelations> = Extract<Rels[keyof Rels], MatchOwner>
@@ -469,7 +478,8 @@ function resolveBindings(
469
478
  context: ChainContext,
470
479
  label: string,
471
480
  relation: MatchOwner,
472
- bindings: Readonly<Record<string, unknown>>
481
+ bindings: Readonly<Record<string, unknown>>,
482
+ joins: (a: ClassedField, b: ClassedField) => boolean = fieldJoins
473
483
  ): ResolvedBindings {
474
484
  const entries: BindingEntry[] = []
475
485
  const vars: AnyVar[] = []
@@ -494,7 +504,7 @@ function resolveBindings(
494
504
  const ref = value
495
505
  const mint = mintSlotOf(context, ref)
496
506
  const positionSlot: ClassedField = { field: declared.field, class: fieldClass }
497
- if (!fieldJoins(mint, positionSlot)) {
507
+ if (!joins(mint, positionSlot)) {
498
508
  throw errors.new(
499
509
  `${label}: the variable ${ref.label} joins domain-unequal fields — minted at ${renderFieldKind(mint)}, reused at ${renderFieldKind(positionSlot)} (a var joins only class-equal slots; bare pairs only with bare)`
500
510
  )
@@ -648,7 +658,7 @@ function advanceWhere(context: ChainContext, state: RuleBuildState, cond: AnyCon
648
658
  return value !== undefined
649
659
  })
650
660
  )
651
- const resolved = resolveBindings(context, `negated relation ${relation.name}`, relation, bindings)
661
+ const resolved = resolveBindings(context, `negated relation ${relation.name}`, relation, bindings, fieldAntiJoins)
652
662
  return Object.freeze({
653
663
  items: Object.freeze([...state.items, Object.freeze({ kind: "negated" as const, atom: resolved.atom })]),
654
664
  bound: state.bound,
@@ -1161,7 +1171,7 @@ function makeRecRuleScope<Rels extends SchemaRelations, Classes extends SchemaCl
1161
1171
  return raw
1162
1172
  }
1163
1173
 
1164
- function renderClosedSlice(closed: ClosedRoster | undefined): string {
1174
+ function renderClosedSlice(closed: AnyClosedRoster | undefined): string {
1165
1175
  return closed === undefined ? "a bare value" : `a ${closed.name} reference`
1166
1176
  }
1167
1177
 
@@ -1177,7 +1187,7 @@ function headSignature(column: FindColumn): string {
1177
1187
  return `${column.name}:${agg.op}`
1178
1188
  }
1179
1189
 
1180
- function renderParamAnchor(roster: ClosedRoster | undefined): string {
1190
+ function renderParamAnchor(roster: AnyClosedRoster | undefined): string {
1181
1191
  return roster === undefined ? "a non-closed position" : `a ${roster.name} reference`
1182
1192
  }
1183
1193
 
@@ -1,8 +1,9 @@
1
1
  import * as errors from "@superbuilders/errors"
2
2
  import type { AnyClosed } from "#closed.ts"
3
3
  import { sealedFieldsOf } from "#closed.ts"
4
- import type { AnyField, Infer } from "#fields.ts"
5
- import { rosterOf } from "#fields.ts"
4
+ import type { AnyField, Infer, SignatureOf } from "#fields.ts"
5
+ import { rosterOf, signaturesAgree } from "#fields.ts"
6
+ import type { Same, SameLen } from "#judgment.ts"
6
7
  import type { ClassLookup, ClassRecordOf, SchemaClasses } from "#law.ts"
7
8
  import type { QueryParam } from "#native.ts"
8
9
  import type { AnyRelation, RelationFields } from "#relation.ts"
@@ -19,7 +20,7 @@ type MatchFields<R extends MatchOwner> = R extends AnyClosed
19
20
  ? RelationFields<R>
20
21
  : never
21
22
 
22
- interface Var<F extends AnyField = AnyField, RN extends string = string, K extends string = string> {
23
+ interface Var<F extends AnyField, RN extends string, K extends string> {
23
24
  readonly [term]: "var"
24
25
  readonly owner: MatchOwner & { readonly name: RN }
25
26
  readonly column: K
@@ -27,19 +28,19 @@ interface Var<F extends AnyField = AnyField, RN extends string = string, K exten
27
28
  readonly label: string
28
29
  }
29
30
 
30
- type AnyVar = Var
31
+ type AnyVar = Var<AnyField, string, string>
31
32
 
32
- interface Param<Name extends string = string> {
33
+ interface Param<Name extends string> {
33
34
  readonly [term]: "param"
34
35
  readonly name: Name
35
36
  }
36
37
 
37
- interface SetParam<Name extends string = string> {
38
+ interface SetParam<Name extends string> {
38
39
  readonly [term]: "setParam"
39
40
  readonly name: Name
40
41
  }
41
42
 
42
- type AnyTerm = Var | Param | SetParam
43
+ type AnyTerm = AnyVar | Param<string> | SetParam<string>
43
44
 
44
45
  function isTerm(value: unknown): value is AnyTerm {
45
46
  return typeof value === "object" && value !== null && term in value
@@ -147,48 +148,78 @@ type UnionToIntersection<U> = (U extends unknown ? (member: U) => void : never)
147
148
 
148
149
  type ShapeOf<U> = [U] extends [never] ? Record<never, never> : Flatten<UnionToIntersection<U>>
149
150
 
150
- type WidthOf<F extends AnyField> = F extends { readonly width: infer W } ? W : undefined
151
-
152
- type ElementOf<F extends AnyField> = F extends { readonly element: infer E } ? E : undefined
153
-
154
- type RosterOf<F extends AnyField> = F extends {
155
- readonly closed: { readonly name: infer N extends string; readonly handles: readonly (infer H extends string)[] }
156
- }
157
- ? readonly [N, H]
158
- : undefined
159
-
160
- type JoinOk<A extends ClassedField, B extends ClassedField> = [
161
- A["field"]["kind"],
162
- A["class"],
163
- WidthOf<A["field"]>,
164
- ElementOf<A["field"]>,
165
- RosterOf<A["field"]>
166
- ] extends [B["field"]["kind"], B["class"], WidthOf<B["field"]>, ElementOf<B["field"]>, RosterOf<B["field"]>]
167
- ? [B["field"]["kind"], B["class"], WidthOf<B["field"]>, ElementOf<B["field"]>, RosterOf<B["field"]>] extends [
168
- A["field"]["kind"],
169
- A["class"],
170
- WidthOf<A["field"]>,
171
- ElementOf<A["field"]>,
172
- RosterOf<A["field"]>
173
- ]
174
- ? true
175
- : false
151
+ /**
152
+ * A slot's identity for the positive-join judgment: the field's structural
153
+ * signature ({@link SignatureOf} the ONE interpreter, shared with the
154
+ * face pairing wall) plus the slot's law class.
155
+ */
156
+ type SlotSignature<S extends ClassedField> = readonly [SignatureOf<S["field"]>, S["class"]]
157
+
158
+ type JoinOk<A extends ClassedField, B extends ClassedField> = Same<SlotSignature<A>, SlotSignature<B>>
159
+
160
+ type U64Wire<F extends AnyField> = F extends { readonly kind: "u64" }
161
+ ? F extends { readonly closed: unknown }
162
+ ? false
163
+ : true
176
164
  : false
177
165
 
166
+ type ClosedHandles<F extends AnyField> = F extends {
167
+ readonly closed: { readonly handles: infer H extends readonly string[] }
168
+ }
169
+ ? H
170
+ : never
171
+
172
+ /**
173
+ * Two closed ids anti-join when their handle vectors carry the same Peano
174
+ * length ({@link SameLen}: zero equals zero, successor recurses on
175
+ * successor). A bare field has no vector and proves nothing.
176
+ */
177
+ type ClosedIdOk<A extends AnyField, B extends AnyField> = [ClosedHandles<A>] extends [never]
178
+ ? false
179
+ : [ClosedHandles<B>] extends [never]
180
+ ? false
181
+ : SameLen<ClosedHandles<A>, ClosedHandles<B>>
182
+
183
+ /**
184
+ * Anti-join class safety: class-equal slots, plus same-identity u64
185
+ * (fresh mint, foreign-key copy, or bare u64) when one side is bare,
186
+ * plus two closed-id fields whose handle tuples have the same length.
187
+ * Two distinct generators stay refused.
188
+ */
189
+ type AntiJoinOk<A extends ClassedField, B extends ClassedField> =
190
+ JoinOk<A, B> extends true
191
+ ? true
192
+ : ClosedIdOk<A["field"], B["field"]> extends true
193
+ ? true
194
+ : U64Wire<A["field"]> extends true
195
+ ? U64Wire<B["field"]> extends true
196
+ ? [A["class"]] extends [undefined]
197
+ ? true
198
+ : [B["class"]] extends [undefined]
199
+ ? true
200
+ : false
201
+ : false
202
+ : false
203
+
178
204
  function fieldJoins(a: ClassedField, b: ClassedField): boolean {
179
- const widthA = "width" in a.field ? a.field.width : undefined
180
- const widthB = "width" in b.field ? b.field.width : undefined
181
- const elementA = "element" in a.field ? a.field.element : undefined
182
- const elementB = "element" in b.field ? b.field.element : undefined
183
- const rosterA = rosterOf(a.field)
184
- const rosterB = rosterOf(b.field)
185
- return (
186
- a.field.kind === b.field.kind &&
187
- a.class === b.class &&
188
- widthA === widthB &&
189
- elementA === elementB &&
190
- rosterA === rosterB
191
- )
205
+ return a.class === b.class && signaturesAgree(a.field, b.field)
206
+ }
207
+
208
+ function u64Wire(field: AnyField): boolean {
209
+ return field.kind === "u64" && rosterOf(field) === undefined
210
+ }
211
+
212
+ function closedIdAntiJoins(a: AnyField, b: AnyField): boolean {
213
+ const rosterA = rosterOf(a)
214
+ const rosterB = rosterOf(b)
215
+ return rosterA !== undefined && rosterB !== undefined && rosterA.handles.length === rosterB.handles.length
216
+ }
217
+
218
+ function fieldAntiJoins(a: ClassedField, b: ClassedField): boolean {
219
+ if (fieldJoins(a, b) || closedIdAntiJoins(a.field, b.field)) {
220
+ return true
221
+ }
222
+ return u64Wire(a.field) && u64Wire(b.field) && (a.class === undefined || b.class === undefined)
192
223
  }
193
224
 
194
225
  function renderFieldKind(slot: ClassedField): string {
@@ -220,6 +251,7 @@ interface ParamEntry {
220
251
  }
221
252
 
222
253
  export type {
254
+ AntiJoinOk,
223
255
  AnyTerm,
224
256
  AnyVar,
225
257
  ClassedField,
@@ -241,4 +273,4 @@ export type {
241
273
  Var,
242
274
  VarsOf
243
275
  }
244
- export { fieldJoins, inferred, isTerm, makeParam, makeSetParam, renderFieldKind, term, v }
276
+ export { fieldAntiJoins, fieldJoins, inferred, isTerm, makeParam, makeSetParam, renderFieldKind, term, v }
package/src/statements.ts CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  } from "#capacity.ts"
13
13
  import { isClosedMember, sealedFieldOf } from "#closed.ts"
14
14
  import { type AnyFace, type FaceData, renderFace, type SameArity, type SameShapes } from "#face.ts"
15
- import { type ClosedRoster, rosterOf } from "#fields.ts"
15
+ import { type AnyClosedRoster, rosterOf, rostersAgree } from "#fields.ts"
16
16
  import type { AnyRelation, RelationFields } from "#relation.ts"
17
17
  import { type CapacityWindowSpec, renderCapacityWindow, renderWeight, type WeightSpec } from "#spec.ts"
18
18
 
@@ -67,7 +67,7 @@ interface KeyStatement<R extends AnyRelation, Projection extends readonly string
67
67
  readonly data: KeyData<R, Projection>
68
68
  }
69
69
 
70
- function renderRosterSide(roster: ClosedRoster | undefined): string {
70
+ function renderRosterSide(roster: AnyClosedRoster | undefined): string {
71
71
  return roster === undefined ? "a bare column" : `a ${roster.name} reference`
72
72
  }
73
73
 
@@ -96,7 +96,7 @@ function assertRosterAgreement(source: FaceData, target: FaceData, statement: St
96
96
  }
97
97
  const sourceRoster = rosterOf(sealedFieldOf(source.owner, fieldName))
98
98
  const targetRoster = rosterOf(sealedFieldOf(target.owner, targetField))
99
- if (sourceRoster !== targetRoster) {
99
+ if (!rostersAgree(sourceRoster, targetRoster)) {
100
100
  throw errors.new(
101
101
  `${source.owner.name}.${fieldName} is ${renderRosterSide(sourceRoster)} but ${target.owner.name}.${targetField} is ${renderRosterSide(targetRoster)} — closedness rides the descriptor: a closed reference is spelled with the vocabulary's own id descriptor (one meaning, one spelling), so faces pair closed-with-closed through one roster or bare-with-bare, never across — ${renderStatement(statement)}`
102
102
  )