@ai-matrx/associations 0.2.0
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/CHANGELOG.md +122 -0
- package/LICENSE +21 -0
- package/README.md +115 -0
- package/dist/core/index.cjs +3191 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +648 -0
- package/dist/core/index.d.ts +648 -0
- package/dist/core/index.js +3168 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index.cjs +1479 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +11165 -0
- package/dist/index.d.ts +11165 -0
- package/dist/index.js +1456 -0
- package/dist/index.js.map +1 -0
- package/package.json +98 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Changelog — @ai-matrx/associations
|
|
2
|
+
|
|
3
|
+
## 0.2.0 — 2026-08-29 (unreleased; W2 of the extraction)
|
|
4
|
+
|
|
5
|
+
Wave 2: the headless owns-persistence `/core` subpath — the service
|
|
6
|
+
chokepoint, guards, the loud result funnel, the registry merge engine, the
|
|
7
|
+
subscribable cache store, titles/favorites/categories/candidates/entity-rows,
|
|
8
|
+
and the falsifiable `assertDemandedSchema` probe. Behavior ported VERBATIM
|
|
9
|
+
from the matrx-frontend originals (census appendix, re-read in full
|
|
10
|
+
2026-08-29); only the measured seams inverted onto the W1 ports.
|
|
11
|
+
|
|
12
|
+
- **`createAssociationsStore(cfg)`** (`src/core/store.ts`) — the one runtime
|
|
13
|
+
object a host constructs. Required ports (dataSource/identity/errorSink)
|
|
14
|
+
throw at construction, each with a named message. Replaces matrx-frontend's
|
|
15
|
+
~530 lines of Redux fragments with verbatim cache behavior: per-key
|
|
16
|
+
in-flight dedup, ready short-circuit unless `force`, pending retains prior
|
|
17
|
+
edges AND the visible error, add/remove reload BOTH endpoints, setTargets
|
|
18
|
+
reloads the source only, category echo-insert then authoritative reload,
|
|
19
|
+
stable idle defaults (`useSyncExternalStore` contract), `subscribe`/
|
|
20
|
+
`subscribeCategories`/`invalidate`/`invalidateAll`. The "never dispatch
|
|
21
|
+
appContextSlice" law is now STRUCTURAL (no host-store import path exists)
|
|
22
|
+
and proven by test: a write notifies only the two endpoint keys.
|
|
23
|
+
- **Services** as sink-bound factories over the `dataSource` port, each
|
|
24
|
+
method returning the never-throw envelope: `associationsService` (incl. the
|
|
25
|
+
stricter file→conversation `conversation_file_*` routing and the
|
|
26
|
+
`agent_resource_*` pair), `categoriesService`, `favoritesService`,
|
|
27
|
+
`candidates` (generic `reference_search_candidates` read + overlay
|
|
28
|
+
override + universal cross-token search), `titles` (chunked batch resolve,
|
|
29
|
+
prime, fallback), `entityRows` (registry-convention create/rename),
|
|
30
|
+
`helpers` (linkEdges/linkOneToMany/linkManyToOne/replaceTargets/
|
|
31
|
+
linkCreated/unlinkEdges), and the pure edge classifier
|
|
32
|
+
(`isContentSourceEdge` exclusion-list law).
|
|
33
|
+
- **`rpcResult`**: `createRpcResultHelpers(errorSink)` — the original mapper
|
|
34
|
+
verbatim PLUS the new PGRST202 → `demanded_schema_violation` branch that
|
|
35
|
+
ALWAYS reaches the errorSink and names the demanded-schema contract.
|
|
36
|
+
- **`assertDemandedSchema(dataSource, { selfTest })`**: probes all 23
|
|
37
|
+
demanded RPCs with ZERO side effects (every uuid arg is an unparseable
|
|
38
|
+
sentinel, so PostgREST resolves the function but Postgres fails the cast
|
|
39
|
+
before any body executes — verified against the live SECURITY DEFINER
|
|
40
|
+
sources 2026-08-29). `selfTest: true` probes a fabricated name that MUST
|
|
41
|
+
come back PGRST202, so a dataSource that cannot fail is refused
|
|
42
|
+
(falsifiability rule).
|
|
43
|
+
- **Registry merge engine** (`src/core/registry.ts`): generated metadata +
|
|
44
|
+
host `entityOverlay` port, convention owner/org columns, reads-tolerant
|
|
45
|
+
alias resolution (`cld_file` → `file`), curated/listable token sets with
|
|
46
|
+
the pickable-but-unlistable scream.
|
|
47
|
+
- **Seam inversions (deliberate, per the packaging standard)**: supabase
|
|
48
|
+
singleton → `dataSource`; `requireUserId` store read → `identity`; every
|
|
49
|
+
`console.warn`/`console.error` scream → `errorSink` events (plain
|
|
50
|
+
PostgREST result errors still do NOT double-report — hosts capture those
|
|
51
|
+
at the dataSource layer — except PGRST202, which always screams);
|
|
52
|
+
`isTransportFailure` re-implemented in-package (the `@ai-matrx/data/net`
|
|
53
|
+
original's NetError-class branches cannot occur here; the plain-object/
|
|
54
|
+
TypeError/navigator branches ported verbatim) to keep `/core` at zero
|
|
55
|
+
runtime dependencies; lucide `DEFAULT_ICON` → `Icon: null` (C19 — the
|
|
56
|
+
consumer renders its default); the module-level entity-title Map →
|
|
57
|
+
store-instance state (no module-level mutable state exists anywhere in
|
|
58
|
+
`/core`, so there is nothing for dual-loaded graphs to split and no
|
|
59
|
+
globalThis slot is needed); `favoritesService` + `favoritesCore` (two host
|
|
60
|
+
files split only by the singleton coupling) collapse back into ONE
|
|
61
|
+
implementation.
|
|
62
|
+
- **Port addition (additive)**: `AssociationsDataSource` gains OPTIONAL
|
|
63
|
+
structural `from`/`schema` members — entity-row create/rename write TABLES
|
|
64
|
+
(verbatim original behavior), not RPCs. An rpc-only dataSource still
|
|
65
|
+
satisfies everything else; entity-row calls against it return a screamed
|
|
66
|
+
error naming the missing capability.
|
|
67
|
+
- **Defects found in the originals**: none requiring a behavior change; two
|
|
68
|
+
latent strictness holes hardened in the port (unchecked `failures[0]` in
|
|
69
|
+
the helper combiner and unchecked `list[cursor]` in the search worker —
|
|
70
|
+
both unreachable in practice, both now guarded under
|
|
71
|
+
`noUncheckedIndexedAccess`).
|
|
72
|
+
- Tests: 97 (from 26) — every rpcResult mapping branch incl. PGRST202,
|
|
73
|
+
guards + alias recovery, every service branch (file→conversation boundary,
|
|
74
|
+
id-echo checks, dedupe/empty short-circuits), registry merge/degrade,
|
|
75
|
+
candidates/titles/entity-rows incl. chunking and capability screams, the
|
|
76
|
+
full store behavior suite incl. the never-touches-context invariant, and
|
|
77
|
+
the probe proven falsifiable both ways.
|
|
78
|
+
- Gate: `pnpm typecheck && pnpm test && pnpm check:package` green; the
|
|
79
|
+
tarball canary now imports AND requires `/core`, constructs a store
|
|
80
|
+
against fake ports, proves required-port throw, and bans "use client" in
|
|
81
|
+
all four dist files.
|
|
82
|
+
|
|
83
|
+
## 0.1.0 — 2026-08-29 (unreleased; W1 of the extraction)
|
|
84
|
+
|
|
85
|
+
Wave 1 of the associations extraction
|
|
86
|
+
(`common-docs/projects/npm-package-extraction/ASSOCIATIONS-PACKAGE-DESIGN.md`,
|
|
87
|
+
ratified C18): the types root.
|
|
88
|
+
|
|
89
|
+
- **Entity-type vocabulary in-package**: `src/entity-types.generated.ts`
|
|
90
|
+
(651 tokens, generated live from `platform.entity_types` on 2026-08-29) plus
|
|
91
|
+
the generator (`scripts/generate-entity-types.ts`, ported from matrx-frontend
|
|
92
|
+
`scripts/generate-entity-types.ts`). The app-side ENTITY_OVERLAY duplication
|
|
93
|
+
guard stays in matrx-frontend's `check:entity-types` until the W5 elimination
|
|
94
|
+
pass (the overlay is host code). Skew policy per C18: writes strict, reads
|
|
95
|
+
tolerant; registry changes ship as patch releases.
|
|
96
|
+
- **Demanded RPC contract**: `src/demanded-rpcs.generated.ts` — the 23-function
|
|
97
|
+
surface (`DemandedRpcSignatures` / `DemandedRpcName` / `DEMANDED_RPC_NAMES`),
|
|
98
|
+
sliced from the live-generated platform DB contract by
|
|
99
|
+
`scripts/extract-demanded-rpcs.ts`. The legacy `assoc_link`/`assoc_list`/
|
|
100
|
+
`assoc_unlink` trio is deliberately not demanded (zero client callers;
|
|
101
|
+
flagged for server-side retirement).
|
|
102
|
+
- **Edge/result types** ported verbatim from matrx-frontend
|
|
103
|
+
`features/scopes/types.ts` (`AssociationEdge`, `AssociationTargetEdge`,
|
|
104
|
+
`AssociationSourceEdge`, `AssociationsEntry`, `UserEntityState`,
|
|
105
|
+
`PlatformCategory`, `CategoriesEntry`, `ASSOCIATION_TARGET_TYPES`) with two
|
|
106
|
+
deliberate seam inversions, per the packaging standard:
|
|
107
|
+
- the host contract's `Json` type became `unknown` (JSON-boundary rule);
|
|
108
|
+
- `ScopesRpcError`/`ScopesRpcResult` became `AssociationsRpcError`/
|
|
109
|
+
`AssociationsRpcResult`, and the error-code union gained
|
|
110
|
+
`demanded_schema_violation` (the PGRST202 → demanded-schema scream that
|
|
111
|
+
`/core`'s `rpcResult` will map in W2; `template_missing`, a scopes-only
|
|
112
|
+
code, was not carried over).
|
|
113
|
+
- **The seven ports** (`src/ports.ts`): `AssociationsDataSource`,
|
|
114
|
+
`AssociationsIdentity`, `ErrorSink` (required) + `EntityOverlayEntry`,
|
|
115
|
+
`AssociationsNotifier`, `WindowShellPort`, `CapturePort`,
|
|
116
|
+
`PickerOverrideMap`, `EntityDoorsPort` (optional, defined degradations),
|
|
117
|
+
combined in `AssociationsConfig`. Shapes distilled from the measured
|
|
118
|
+
matrx-frontend call sites (census 2026-08-29), nothing speculative.
|
|
119
|
+
- Gate: `pnpm typecheck && pnpm test && pnpm check:package` (build, publint,
|
|
120
|
+
attw on the packed tarball, empty-project ESM import + CJS require canary,
|
|
121
|
+
pure-root "use client" ban) — all green; `check:entity-types` drift gate
|
|
122
|
+
proven falsifiable against the live registry.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AI Matrix Engine
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# @ai-matrx/associations
|
|
2
|
+
|
|
3
|
+
THE one way to relate two entities on the AI Matrx platform: association edges,
|
|
4
|
+
categories, favorites/recents, and the generated entity-type vocabulary — shipped
|
|
5
|
+
as one package a Matrx client or enterprise app installs.
|
|
6
|
+
|
|
7
|
+
**v0.2 ships the types root and the headless `/core`** — the service
|
|
8
|
+
chokepoint, guards, the never-throw result funnel, the registry merge engine,
|
|
9
|
+
and the subscribable cache store. The React faces (`/react`: hooks, cards,
|
|
10
|
+
list, pickers, capture toolbar) arrive in the next waves. Everything below
|
|
11
|
+
about the schema is binding — it is the package's product.
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import {
|
|
15
|
+
type EntityTypeToken, // 651-token union generated from platform.entity_types
|
|
16
|
+
ENTITY_TYPE_METADATA, // per-token schema/table/label/titleColumn/contentRole
|
|
17
|
+
isEntityTypeToken, // runtime guard, narrows to the union
|
|
18
|
+
type AssociationEdge, // one assoc_for_entity row (both directions)
|
|
19
|
+
type AssociationsRpcResult, // the never-throw result envelope
|
|
20
|
+
type DemandedRpcName, // the 23-function demanded RPC surface
|
|
21
|
+
type AssociationsConfig, // the seven ports a host binds
|
|
22
|
+
} from "@ai-matrx/associations";
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import {
|
|
27
|
+
createAssociationsStore, // the ONE runtime object a host constructs
|
|
28
|
+
assertDemandedSchema, // boot probe: every demanded RPC must answer
|
|
29
|
+
} from "@ai-matrx/associations/core";
|
|
30
|
+
|
|
31
|
+
const store = createAssociationsStore({
|
|
32
|
+
dataSource: supabase, // structural subset of SupabaseClient
|
|
33
|
+
identity: { requireUserId }, // throws when unauthenticated
|
|
34
|
+
errorSink: (e) => report(e), // recovery layers scream here
|
|
35
|
+
entityOverlay: { file: { hrefFor: (id) => `/files/f/${id}` } }, // optional
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
await store.load("task", taskId); // lazy, deduped, subscribable
|
|
39
|
+
store.getEdges("task", taskId); // sync, stable idle default
|
|
40
|
+
await store.add({ sourceType: "note", sourceId, targetType: "task", targetId });
|
|
41
|
+
store.titles.prime("task", taskId, "Renamed");
|
|
42
|
+
await store.favorites.setFavorite("note", noteId, true);
|
|
43
|
+
|
|
44
|
+
// Dev/CI boot check for any new environment (zero side effects; selfTest
|
|
45
|
+
// proves the probe can fail before you trust its green):
|
|
46
|
+
await assertDemandedSchema(supabase, { selfTest: true });
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## The demanded schema — read this first
|
|
50
|
+
|
|
51
|
+
This package is **opinionated like the Python `matrx-*` packages**: it does not
|
|
52
|
+
adapt to your database — it DEMANDS ours. A consumer who won't run our schema is
|
|
53
|
+
not this package's consumer. It ships no DDL (the platform DB owns that; managed
|
|
54
|
+
consumers run on our infrastructure); it ships the demand three ways —
|
|
55
|
+
documented (here), typed (`DemandedRpcSignatures`, generated from the live DB),
|
|
56
|
+
and probed (`assertDemandedSchema` in `/core`). Every violation
|
|
57
|
+
**screams, never degrades**.
|
|
58
|
+
|
|
59
|
+
1. **The edge.** `platform.associations(source_type, source_id, target_type,
|
|
60
|
+
target_id, organization_id, label, metadata, role, position, created_by,
|
|
61
|
+
created_at, deleted_at, …)` — one polymorphic table, unique on the 5-tuple
|
|
62
|
+
`(source_type, source_id, target_type, target_id, role)` `NULLS NOT DISTINCT`.
|
|
63
|
+
Type columns are FK-validated against `platform.entity_types.token`.
|
|
64
|
+
2. **RPC-only.** The client holds **no grant** on the table. Every operation goes
|
|
65
|
+
through the 23 SECURITY DEFINER functions in `DEMANDED_RPC_NAMES` (`assoc_*`
|
|
66
|
+
×8, `conversation_file*` ×3, `agent_resource_*` ×2, `cat_*` ×5, `ues_*` ×4,
|
|
67
|
+
`reference_search_candidates`). Those readers consume the live-only
|
|
68
|
+
`associations_live` view, so tombstone semantics (a trashed entity
|
|
69
|
+
soft-removes its edges; restore revives them) hold by construction — this
|
|
70
|
+
package never writes a `.is("deleted_at", null)` filter. The DB's legacy
|
|
71
|
+
`assoc_link`/`assoc_list`/`assoc_unlink` trio is deliberately NOT demanded.
|
|
72
|
+
3. **Direction is registry-owned: little→big.** The resource is the SOURCE, the
|
|
73
|
+
container the TARGET (`task → organization`, `file → scope`). The DB trigger
|
|
74
|
+
`trg_associations_auto_orient` rejects reversed writes — this package
|
|
75
|
+
surfaces that error verbatim and never flips an edge for you.
|
|
76
|
+
4. **A link is never a grant.** Access lives in `iam.permissions` /
|
|
77
|
+
`iam.memberships`. The ONLY sanctioned association→access bridge is DB-side
|
|
78
|
+
conveyance (`platform.association_types.conveys_max` → reachability). This
|
|
79
|
+
package makes **zero access decisions**.
|
|
80
|
+
5. **A missing function screams.** A PostgREST PGRST202 on any demanded RPC maps
|
|
81
|
+
to the `demanded_schema_violation` result code and always reaches your
|
|
82
|
+
errorSink: a database without our schema produces one unambiguous scream, not
|
|
83
|
+
a mystery toast.
|
|
84
|
+
|
|
85
|
+
## The token vocabulary — writes strict, reads tolerant
|
|
86
|
+
|
|
87
|
+
`EntityTypeToken` mirrors `platform.entity_types` 1:1 (651 active tokens),
|
|
88
|
+
regenerated by `pnpm gen:entity-types` inside this package. Skew policy:
|
|
89
|
+
|
|
90
|
+
- **Writes are STRICT** against the installed vocabulary: a token this version
|
|
91
|
+
doesn't know fails at the guard, in code, before Postgres — exactly like a
|
|
92
|
+
missing DB type. The fix is `pnpm up @ai-matrx/associations` (registry changes
|
|
93
|
+
ship as **patch releases**), never a raw-string widening.
|
|
94
|
+
- **Reads are TOLERANT**: an edge arriving with a token this version doesn't
|
|
95
|
+
know renders through the fallback (default icon, raw label, no door) instead
|
|
96
|
+
of crashing.
|
|
97
|
+
|
|
98
|
+
## The seven ports
|
|
99
|
+
|
|
100
|
+
One `AssociationsConfig`, bound once per host. `dataSource` (a structural subset
|
|
101
|
+
of `SupabaseClient` answering the demanded RPCs), `identity`, and `errorSink`
|
|
102
|
+
are **required** — construction throws without them. `entityOverlay` (icons /
|
|
103
|
+
routes / candidate overrides per token), `notifier`, `windowShell`, `capture`,
|
|
104
|
+
`pickerOverrides`, and `entityDoors` are optional, each with a defined
|
|
105
|
+
degradation — never a dead button, never a silent failure.
|
|
106
|
+
|
|
107
|
+
## Versioning
|
|
108
|
+
|
|
109
|
+
Consumed by Matrx repos as `latest` (`workspace:*` inside the aidream monorepo)
|
|
110
|
+
per THE LATEST LAW — never pinned. Registry token changes → regenerate → patch.
|
|
111
|
+
Contract changes → minor/major per semver.
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
MIT
|