@openparachute/vault 0.7.0-rc.9 → 0.7.2-rc.3

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 (47) hide show
  1. package/core/src/aggregate.test.ts +260 -0
  2. package/core/src/contract-taxonomy.test.ts +26 -2
  3. package/core/src/contract-typed-index.test.ts +4 -2
  4. package/core/src/core.test.ts +737 -2
  5. package/core/src/cursor-keyset-ms.test.ts +537 -0
  6. package/core/src/cursor.ts +76 -6
  7. package/core/src/doctor.ts +23 -1
  8. package/core/src/hooks.ts +9 -0
  9. package/core/src/indexed-fields.test.ts +4 -1
  10. package/core/src/indexed-fields.ts +6 -1
  11. package/core/src/links.ts +22 -0
  12. package/core/src/mcp.ts +322 -53
  13. package/core/src/notes.ts +518 -67
  14. package/core/src/paths.ts +4 -0
  15. package/core/src/portable-md.test.ts +161 -0
  16. package/core/src/portable-md.ts +140 -9
  17. package/core/src/query-warnings.ts +60 -12
  18. package/core/src/schema-defaults.ts +7 -1
  19. package/core/src/schema.ts +128 -2
  20. package/core/src/seed-packs.ts +55 -15
  21. package/core/src/store.ts +141 -7
  22. package/core/src/tag-schemas.ts +20 -7
  23. package/core/src/txn.test.ts +100 -4
  24. package/core/src/txn.ts +119 -24
  25. package/core/src/types.ts +89 -0
  26. package/core/src/ulid.test.ts +56 -0
  27. package/core/src/ulid.ts +116 -0
  28. package/core/src/vault-projection.ts +19 -1
  29. package/core/src/wikilinks.test.ts +205 -1
  30. package/core/src/wikilinks.ts +200 -35
  31. package/package.json +1 -1
  32. package/src/aggregate-routes.test.ts +230 -0
  33. package/src/cli.ts +6 -0
  34. package/src/contract-errors.test.ts +2 -1
  35. package/src/contract-search.test.ts +17 -0
  36. package/src/mcp-link-warnings-scope.test.ts +144 -0
  37. package/src/mcp-query-notes-aggregate-scope.test.ts +183 -0
  38. package/src/mcp-tools.ts +76 -17
  39. package/src/mirror-import.ts +5 -0
  40. package/src/routes.ts +298 -24
  41. package/src/routing.test.ts +167 -0
  42. package/src/routing.ts +47 -11
  43. package/src/tag-integrity-mcp.test.ts +45 -6
  44. package/src/tag-scope.ts +10 -1
  45. package/src/vault.test.ts +557 -21
  46. package/web/ui/dist/assets/{index-B8pGeQam.js → index-CJ6NtIwh.js} +1 -1
  47. package/web/ui/dist/index.html +1 -1
package/core/src/hooks.ts CHANGED
@@ -372,6 +372,15 @@ export class HookRegistry {
372
372
 
373
373
  // Defer to a microtask so we unwind the caller's stack (and its
374
374
  // SQLite transaction, if any) before handlers run.
375
+ //
376
+ // Caveat (vault#589): under an ASYNC transaction that spans awaits (the
377
+ // atomic blow-away import wrapped in `transactionAsync`), the connection
378
+ // stays open across the microtask boundary. A handler that writes to the
379
+ // store BEFORE its first real `await` would `SAVEPOINT`-join that open
380
+ // transaction (rolled back with it). Every handler here is audited safe —
381
+ // it defers real work behind an `await` (semaphore acquire) and does no
382
+ // synchronous store write — but the invariant lives in txn.ts's header
383
+ // ("shared-connection invariant"), not just this comment; keep it true.
375
384
  queueMicrotask(() => {
376
385
  for (const hook of matches) {
377
386
  const task = this.runHandler(hook, event, note, store);
@@ -67,10 +67,13 @@ describe("indexed-fields: module", () => {
67
67
  expect(() => validateFieldName("'; DROP TABLE notes; --")).toThrow(IndexedFieldError);
68
68
  });
69
69
 
70
- it("TYPE_MAP covers string/integer/boolean", () => {
70
+ it("TYPE_MAP covers string/integer/boolean/reference", () => {
71
71
  expect(TYPE_MAP.string).toBe("TEXT");
72
72
  expect(TYPE_MAP.integer).toBe("INTEGER");
73
73
  expect(TYPE_MAP.boolean).toBe("INTEGER");
74
+ // vault#typed-reference-field: `reference` stores like `string` — the
75
+ // metadata VALUE (an id/path/title) is what's indexed here.
76
+ expect(TYPE_MAP.reference).toBe("TEXT");
74
77
  });
75
78
 
76
79
  it("declareField creates column + index on first declaration", () => {
@@ -23,12 +23,17 @@ import { Database } from "bun:sqlite";
23
23
  // ---------------------------------------------------------------------------
24
24
 
25
25
  export type SqliteType = "TEXT" | "INTEGER";
26
- export type FieldType = "string" | "integer" | "boolean";
26
+ // `reference` (vault#typed-reference-field) is stored as TEXT same as
27
+ // `string` — because the metadata VALUE (an id/path/title) is what's
28
+ // indexed here; the resolved graph link is a separate concern maintained by
29
+ // `core/src/store.ts`'s write path. See tag-schemas.ts's `VALID_FIELD_TYPES`.
30
+ export type FieldType = "string" | "integer" | "boolean" | "reference";
27
31
 
28
32
  export const TYPE_MAP: Record<FieldType, SqliteType> = {
29
33
  string: "TEXT",
30
34
  integer: "INTEGER",
31
35
  boolean: "INTEGER",
36
+ reference: "TEXT",
32
37
  };
33
38
 
34
39
  export interface IndexedField {
package/core/src/links.ts CHANGED
@@ -35,6 +35,28 @@ export function deleteLink(
35
35
  ).run(sourceId, targetId, relationship);
36
36
  }
37
37
 
38
+ /**
39
+ * Delete every outbound link from `sourceId` carrying `relationship`,
40
+ * regardless of target. Used by the `reference`-field auto-link sync
41
+ * (core/src/store.ts's `syncReferenceFieldLinks`, vault#typed-reference-field)
42
+ * to re-establish "this field's link" as a single source of truth: rather
43
+ * than tracking the PRIOR resolved target to delete exactly one edge, the
44
+ * sync clears every edge under the field's relationship name and recreates
45
+ * at most one from the field's current value. This is safe because a
46
+ * `reference` field's relationship name is field-owned — a hand-authored
47
+ * `links` entry using the SAME relationship name on the SAME note is
48
+ * expected to be superseded by the field, not preserved alongside it.
49
+ */
50
+ export function deleteLinksBySourceRelationship(
51
+ db: Database,
52
+ sourceId: string,
53
+ relationship: string,
54
+ ): void {
55
+ db.prepare(
56
+ "DELETE FROM links WHERE source_id = ? AND relationship = ?",
57
+ ).run(sourceId, relationship);
58
+ }
59
+
38
60
  export function getLinks(
39
61
  db: Database,
40
62
  noteId: string,