@openparachute/vault 0.4.3 → 0.4.4-rc.12

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.
@@ -47,7 +47,17 @@ import { Database } from "bun:sqlite";
47
47
  // ---------------------------------------------------------------------------
48
48
 
49
49
  export interface SchemaField {
50
- type?: "string" | "number" | "boolean" | "array" | "object";
50
+ /**
51
+ * Declared type for the field's metadata value. `"integer"` is distinct
52
+ * from `"number"` only at validation time — JSON has no separate integer
53
+ * type, so a JSON number with zero fractional part (`5`, `5.0`,
54
+ * `Number.isInteger(n) === true`) is accepted as integer and a non-zero
55
+ * fractional value (`5.5`) is rejected. This matches the indexed-fields
56
+ * `"integer"` storage type (TYPE_MAP) and removes the false-positive
57
+ * `type_mismatch` warning that previously fired on every integer-shaped
58
+ * field because the validator had no `"integer"` case. See vault#310.
59
+ */
60
+ type?: "string" | "number" | "integer" | "boolean" | "array" | "object";
51
61
  enum?: string[];
52
62
  description?: string;
53
63
  }
@@ -270,6 +280,14 @@ function valueMatchesType(value: unknown, type: SchemaField["type"]): boolean {
270
280
  return typeof value === "string";
271
281
  case "number":
272
282
  return typeof value === "number" && Number.isFinite(value);
283
+ case "integer":
284
+ // JSON has no separate integer type — `5.0` and `5` decode to the
285
+ // same JS Number. Accept any finite Number whose fractional part is
286
+ // zero; reject `5.5`, `NaN`, `Infinity`, and non-Number types.
287
+ // vault#310 (Gitcoin Brain drift detector emits JSON for diffs;
288
+ // without this case, every integer-typed field warned
289
+ // `type_mismatch` and buried the real warnings).
290
+ return typeof value === "number" && Number.isInteger(value);
273
291
  case "boolean":
274
292
  return typeof value === "boolean";
275
293
  case "array":
package/core/src/store.ts CHANGED
@@ -202,6 +202,19 @@ export class BunSqliteStore implements Store {
202
202
  }
203
203
  }
204
204
 
205
+ async restoreNoteTimestamps(id: string, createdAt: string, updatedAt: string): Promise<void> {
206
+ // Import-only: direct UPDATE so the importer can restore a note's
207
+ // historical `created_at`/`updated_at` from the portable-md export
208
+ // bytes. `updateNote` either bumps `updated_at` to wall-clock-now or
209
+ // (with `skipUpdatedAt: true`) leaves it untouched — neither lets
210
+ // the importer write a specific historical timestamp. Skips hooks
211
+ // by design: this isn't a user-edit, it's a state restoration.
212
+ // See vault#308 PR 2.
213
+ this.db
214
+ .prepare("UPDATE notes SET created_at = ?, updated_at = ? WHERE id = ?")
215
+ .run(createdAt, updatedAt, id);
216
+ }
217
+
205
218
  async deleteNote(id: string): Promise<void> {
206
219
  // Read before delete so we can invalidate config caches on the way out.
207
220
  const existing = noteOps.getNote(this.db, id);
package/core/src/types.ts CHANGED
@@ -143,6 +143,21 @@ export interface Store {
143
143
  getNoteByPath(path: string): Promise<Note | null>;
144
144
  getNotes(ids: string[]): Promise<Note[]>;
145
145
  updateNote(id: string, updates: { content?: string; append?: string; prepend?: string; path?: string; metadata?: Record<string, unknown>; created_at?: string; skipUpdatedAt?: boolean; if_updated_at?: string }): Promise<Note>;
146
+ /**
147
+ * Set a note's `created_at` and `updated_at` explicitly. Import-only:
148
+ * used by the portable-md round-trip path to restore timestamps from
149
+ * the export bytes. The regular `updateNote` either bumps `updated_at`
150
+ * to wall-clock-now or (with `skipUpdatedAt: true`) leaves it
151
+ * untouched — neither shape lets the importer write a specific
152
+ * historical timestamp. Bypasses hooks. See vault#308 PR 2.
153
+ */
154
+ restoreNoteTimestamps(id: string, createdAt: string, updatedAt: string): Promise<void>;
155
+ /**
156
+ * Sync wikilinks for every note in the vault. Cheap O(n) walk; used
157
+ * after bulk-imports to rebuild link rows from `[[brackets]]` in
158
+ * content. Returns counts for caller logging.
159
+ */
160
+ syncAllWikilinks(): Promise<{ synced: number; totalAdded: number; totalRemoved: number }>;
146
161
  deleteNote(id: string): Promise<void>;
147
162
  queryNotes(opts: QueryOpts): Promise<Note[]>;
148
163
  searchNotes(query: string, opts?: { tags?: string[]; limit?: number }): Promise<Note[]>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openparachute/vault",
3
- "version": "0.4.3",
3
+ "version": "0.4.4-rc.12",
4
4
  "description": "Agent-native knowledge graph. Notes, tags, links over MCP.",
5
5
  "module": "src/cli.ts",
6
6
  "type": "module",