@bitcobblers/wod-wiki-storage 0.44.2159 → 0.45.2166

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 (34) hide show
  1. package/dist/{chunk-MOGQICXM.mjs → chunk-7ICLFFYU.mjs} +2 -2
  2. package/dist/chunk-7ICLFFYU.mjs.map +1 -0
  3. package/dist/{chunk-IMNG2HY5.mjs → chunk-J6KVDPTL.mjs} +3 -3
  4. package/dist/chunk-J6KVDPTL.mjs.map +1 -0
  5. package/dist/{chunk-JFVXIJJI.mjs → chunk-PJ7GRYOZ.mjs} +8 -8
  6. package/dist/chunk-PJ7GRYOZ.mjs.map +1 -0
  7. package/dist/{chunk-IFLOX2TK.mjs → chunk-UN56ET7P.mjs} +3 -3
  8. package/dist/{chunk-IFLOX2TK.mjs.map → chunk-UN56ET7P.mjs.map} +1 -1
  9. package/dist/idb/index.cjs +1 -1
  10. package/dist/idb/index.cjs.map +1 -1
  11. package/dist/idb/index.d.cts +3 -3
  12. package/dist/idb/index.d.ts +3 -3
  13. package/dist/idb/index.mjs +3 -3
  14. package/dist/index.cjs +6 -6
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +3 -3
  17. package/dist/index.d.ts +3 -3
  18. package/dist/index.mjs +5 -5
  19. package/dist/{policy-DRYF6Y8i.d.cts → policy-BBwCBMtp.d.cts} +3 -2
  20. package/dist/{policy-CPscN81J.d.ts → policy-C86TCPSM.d.ts} +3 -2
  21. package/dist/sql/index.cjs +60 -44
  22. package/dist/sql/index.cjs.map +1 -1
  23. package/dist/sql/index.d.cts +3 -3
  24. package/dist/sql/index.d.ts +3 -3
  25. package/dist/sql/index.mjs +64 -48
  26. package/dist/sql/index.mjs.map +1 -1
  27. package/dist/{sync-DG4Z-szj.d.cts → sync-BDu_Q__z.d.cts} +1 -1
  28. package/dist/{sync-Bjj_c1ea.d.ts → sync-BQ0g8ctt.d.ts} +1 -1
  29. package/dist/{wire-DGYxXt3I.d.ts → wire-BO8FM1LJ.d.cts} +17 -8
  30. package/dist/{wire-DGYxXt3I.d.cts → wire-BO8FM1LJ.d.ts} +17 -8
  31. package/package.json +6 -6
  32. package/dist/chunk-IMNG2HY5.mjs.map +0 -1
  33. package/dist/chunk-JFVXIJJI.mjs.map +0 -1
  34. package/dist/chunk-MOGQICXM.mjs.map +0 -1
package/dist/index.d.cts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { Membership, Note, Session, BlockRevision } from '@bitcobblers/wod-wiki-core';
2
2
  export { AnalyticsDataPoint, Attachment, BlockEffort, BlockEquipment, BlockIndexRow, BlockRevision, EventGrain, EventRecord, Membership, Note, NoteBlock, NoteBlockKind, NoteKind, NoteTag, Page, PageNote, ResultOrigin, Session, Tag, TagType, TagTypeRecord } from '@bitcobblers/wod-wiki-core';
3
- import { S as StoreName, I as IStorage, e as StoreDef, a as IReadOnlyStore, b as StoreType, c as IReadWriteStore, D as DomainQuery, f as DomainQueryResult, d as IStorageTransaction } from './wire-DGYxXt3I.cjs';
4
- export { C as COLUMN_TYPES, g as DB_VERSION, h as DOMAIN_PROJECTION_VERSION, i as DomainEntry, j as DomainOrder, k as DomainPredicate, l as IndexDef, K as KeyDTO, R as RangeDTO, m as STORE_DEFS, n as Scalar, o as StorageColumnType, p as StorageSchema, T as TxOp, q as compareKeys, r as encodeKey, s as extractKeyPart, t as isRange, u as keyFromValue, v as keyInRange, w as parseDomainQueryResult, x as rangeFromIDB } from './wire-DGYxXt3I.cjs';
5
- export { a as USER_DELETED_SEED_PREFIX, U as UserScopeContext, c as collectUserScopeContext, i as isSeedMetaKey, b as isUserOwnedRow, u as userDeletedSeedKey, d as userDeletedSeedNoteId } from './policy-DRYF6Y8i.cjs';
3
+ import { S as StoreName, I as IStorage, e as StoreDef, a as IReadOnlyStore, b as StoreType, c as IReadWriteStore, D as DomainQuery, f as DomainQueryResult, d as IStorageTransaction } from './wire-BO8FM1LJ.cjs';
4
+ export { C as COLUMN_TYPES, g as DB_VERSION, h as DOMAIN_PROJECTION_VERSION, i as DomainDetail, j as DomainOrder, k as DomainPredicate, l as IndexDef, K as KeyDTO, R as RangeDTO, m as STORE_DEFS, n as Scalar, o as StorageColumnType, p as StorageSchema, T as TxOp, q as compareKeys, r as encodeKey, s as extractKeyPart, t as isRange, u as keyFromValue, v as keyInRange, w as parseDomainQueryResult, x as rangeFromIDB } from './wire-BO8FM1LJ.cjs';
5
+ export { a as USER_DELETED_SEED_PREFIX, U as UserScopeContext, c as collectUserScopeContext, i as isSeedMetaKey, b as isUserOwnedRow, u as userDeletedSeedKey, d as userDeletedSeedNoteId } from './policy-BBwCBMtp.cjs';
6
6
  import '@bitcobblers/wod-wiki-lang';
7
7
 
8
8
  /**
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { Membership, Note, Session, BlockRevision } from '@bitcobblers/wod-wiki-core';
2
2
  export { AnalyticsDataPoint, Attachment, BlockEffort, BlockEquipment, BlockIndexRow, BlockRevision, EventGrain, EventRecord, Membership, Note, NoteBlock, NoteBlockKind, NoteKind, NoteTag, Page, PageNote, ResultOrigin, Session, Tag, TagType, TagTypeRecord } from '@bitcobblers/wod-wiki-core';
3
- import { S as StoreName, I as IStorage, e as StoreDef, a as IReadOnlyStore, b as StoreType, c as IReadWriteStore, D as DomainQuery, f as DomainQueryResult, d as IStorageTransaction } from './wire-DGYxXt3I.js';
4
- export { C as COLUMN_TYPES, g as DB_VERSION, h as DOMAIN_PROJECTION_VERSION, i as DomainEntry, j as DomainOrder, k as DomainPredicate, l as IndexDef, K as KeyDTO, R as RangeDTO, m as STORE_DEFS, n as Scalar, o as StorageColumnType, p as StorageSchema, T as TxOp, q as compareKeys, r as encodeKey, s as extractKeyPart, t as isRange, u as keyFromValue, v as keyInRange, w as parseDomainQueryResult, x as rangeFromIDB } from './wire-DGYxXt3I.js';
5
- export { a as USER_DELETED_SEED_PREFIX, U as UserScopeContext, c as collectUserScopeContext, i as isSeedMetaKey, b as isUserOwnedRow, u as userDeletedSeedKey, d as userDeletedSeedNoteId } from './policy-CPscN81J.js';
3
+ import { S as StoreName, I as IStorage, e as StoreDef, a as IReadOnlyStore, b as StoreType, c as IReadWriteStore, D as DomainQuery, f as DomainQueryResult, d as IStorageTransaction } from './wire-BO8FM1LJ.js';
4
+ export { C as COLUMN_TYPES, g as DB_VERSION, h as DOMAIN_PROJECTION_VERSION, i as DomainDetail, j as DomainOrder, k as DomainPredicate, l as IndexDef, K as KeyDTO, R as RangeDTO, m as STORE_DEFS, n as Scalar, o as StorageColumnType, p as StorageSchema, T as TxOp, q as compareKeys, r as encodeKey, s as extractKeyPart, t as isRange, u as keyFromValue, v as keyInRange, w as parseDomainQueryResult, x as rangeFromIDB } from './wire-BO8FM1LJ.js';
5
+ export { a as USER_DELETED_SEED_PREFIX, U as UserScopeContext, c as collectUserScopeContext, i as isSeedMetaKey, b as isUserOwnedRow, u as userDeletedSeedKey, d as userDeletedSeedNoteId } from './policy-C86TCPSM.js';
6
6
  import '@bitcobblers/wod-wiki-lang';
7
7
 
8
8
  /**
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
- export { USER_DELETED_SEED_PREFIX, collectUserScopeContext, isSeedMetaKey, isUserOwnedRow, userDeletedSeedKey, userDeletedSeedNoteId } from './chunk-MOGQICXM.mjs';
2
- import { parseDomainQueryResult, bufferToOps, isDirty, bufferedGet, bufferSlot, overlayRows } from './chunk-JFVXIJJI.mjs';
3
- export { DOMAIN_PROJECTION_VERSION, parseDomainQueryResult } from './chunk-JFVXIJJI.mjs';
4
- import { STORE_DEFS, isRange, rangeFromIDB, keyFromValue, stampUserOwned, rowsEqual, encodeKey } from './chunk-IMNG2HY5.mjs';
5
- export { COLUMN_TYPES, DB_VERSION, DEFAULT_MEMBERSHIP_ID, STORE_DEFS, USER_OWNED_STORES, compareKeys, createDefaultMembership, encodeKey, extractKeyPart, isRange, keyFromValue, keyInRange, rangeFromIDB, rowsEqual, stampUserOwned, validateNoteWrite, validateSessionWrite, validateStorageRow, validateStoredPins } from './chunk-IMNG2HY5.mjs';
1
+ export { USER_DELETED_SEED_PREFIX, collectUserScopeContext, isSeedMetaKey, isUserOwnedRow, userDeletedSeedKey, userDeletedSeedNoteId } from './chunk-7ICLFFYU.mjs';
2
+ import { parseDomainQueryResult, bufferToOps, isDirty, bufferedGet, bufferSlot, overlayRows } from './chunk-PJ7GRYOZ.mjs';
3
+ export { DOMAIN_PROJECTION_VERSION, parseDomainQueryResult } from './chunk-PJ7GRYOZ.mjs';
4
+ import { STORE_DEFS, isRange, rangeFromIDB, keyFromValue, stampUserOwned, rowsEqual, encodeKey } from './chunk-J6KVDPTL.mjs';
5
+ export { COLUMN_TYPES, DB_VERSION, DEFAULT_MEMBERSHIP_ID, STORE_DEFS, USER_OWNED_STORES, compareKeys, createDefaultMembership, encodeKey, extractKeyPart, isRange, keyFromValue, keyInRange, rangeFromIDB, rowsEqual, stampUserOwned, validateNoteWrite, validateSessionWrite, validateStorageRow, validateStoredPins } from './chunk-J6KVDPTL.mjs';
6
6
  import './chunk-XRQKLSOT.mjs';
7
7
 
8
8
  // src/api.ts
@@ -1,7 +1,8 @@
1
- import { S as StoreName } from './wire-DGYxXt3I.cjs';
1
+ import { S as StoreName } from './wire-BO8FM1LJ.cjs';
2
2
 
3
3
  /**
4
- * Shared user-scope sync policy (versioned-databases-backups-review §3.4).
4
+ * Shared user-scope sync policy (the backup/restore contract in
5
+ * docs/architecture/interfaces.md).
5
6
  *
6
7
  * ONE ownership classification governs outbox journals, bootstrap
7
8
  * snapshots, the schema32 outbox cutover and cross-schema conversions. Rows
@@ -1,7 +1,8 @@
1
- import { S as StoreName } from './wire-DGYxXt3I.js';
1
+ import { S as StoreName } from './wire-BO8FM1LJ.js';
2
2
 
3
3
  /**
4
- * Shared user-scope sync policy (versioned-databases-backups-review §3.4).
4
+ * Shared user-scope sync policy (the backup/restore contract in
5
+ * docs/architecture/interfaces.md).
5
6
  *
6
7
  * ONE ownership classification governs outbox journals, bootstrap
7
8
  * snapshots, the schema32 outbox cutover and cross-schema conversions. Rows
@@ -387,7 +387,7 @@ function validateStorageRow(store, value) {
387
387
  const positive = (field) => typeof row[field] === "number" && Number.isSafeInteger(row[field]) && row[field] > 0;
388
388
  const required = (field) => typeof row[field] === "string" && row[field].length > 0;
389
389
  if (store === "notes") {
390
- if (!required("id") || typeof row.title !== "string" || typeof row.rawContent !== "string" || !positive("sourceRevision") || typeof row.createdAt !== "number" || !Number.isFinite(row.createdAt) || "segments" in row || "segmentIds" in row || row.date !== void 0 && (typeof row.date !== "number" || !Number.isFinite(row.date)) || row.seedOrigin !== void 0 && row.seedOrigin !== "seed" && row.seedOrigin !== "user" || row.seedVersion !== void 0 && !(typeof row.seedVersion === "number" && Number.isSafeInteger(row.seedVersion) && row.seedVersion >= 0) || row.tags !== void 0 && (!Array.isArray(row.tags) || !row.tags.every((tag) => typeof tag === "string")) || ["type", "sourceId", "sourcePath", "catalog", "notebookId", "seedChunkId"].some((field) => row[field] !== void 0 && typeof row[field] !== "string")) {
390
+ if (!required("id") || typeof row.title !== "string" || typeof row.rawContent !== "string" || !positive("sourceRevision") || typeof row.createdAt !== "number" || !Number.isFinite(row.createdAt) || "segments" in row || "segmentIds" in row || row.date !== void 0 && (typeof row.date !== "number" || !Number.isFinite(row.date)) || row.seedOrigin !== void 0 && row.seedOrigin !== "seed" && row.seedOrigin !== "user" || row.feedOrigin !== void 0 && row.feedOrigin !== "feed" && row.feedOrigin !== "user" || row.feedOrigin === "feed" && (row.seedOrigin === void 0 || typeof row.feedSourceId !== "string" || row.feedSourceId.length === 0) || row.feedOrigin === "user" && row.seedOrigin === "seed" || row.feedSourceId !== void 0 && row.feedOrigin !== "feed" || row.seedVersion !== void 0 && !(typeof row.seedVersion === "number" && Number.isSafeInteger(row.seedVersion) && row.seedVersion >= 0) || row.tags !== void 0 && (!Array.isArray(row.tags) || !row.tags.every((tag) => typeof tag === "string")) || ["type", "sourceId", "sourcePath", "notebookId", "seedChunkId", "feedSourceId"].some((field) => row[field] !== void 0 && typeof row[field] !== "string")) {
391
391
  throw new StorageError(400, "invalid whole Note", "invalid_note_blocks");
392
392
  }
393
393
  try {
@@ -981,16 +981,21 @@ async function initializeStore(db) {
981
981
  }
982
982
  });
983
983
  }
984
- var DOMAIN_PROJECTION_VERSION = 2;
984
+ var DOMAIN_PROJECTION_VERSION = 4;
985
985
  var TABLES = ["domain_note", "query_block"];
986
986
  var PROJECTION_GUARD_KEY = "domain_projection";
987
- function sourceItem(row) {
987
+ function sourceNote(row) {
988
988
  const item = {};
989
- for (const key of ["id", "noteId", "sourceId", "type", "seedOrigin", "seedChunkId", "sourcePath", "catalog", "notebookId"]) {
989
+ for (const key of ["id", "noteId", "sourceId", "type", "seedOrigin", "seedChunkId", "feedOrigin", "feedSourceId", "sourcePath", "notebookId"]) {
990
990
  const value = row[key];
991
991
  if (typeof value === "string") item[key] = value;
992
992
  else if (value != null) throw new StorageError(400, `${key} must be a string`);
993
993
  }
994
+ const tags = row["tags"];
995
+ if (tags !== void 0 && tags !== null) {
996
+ if (!Array.isArray(tags) || !tags.every((t) => typeof t === "string")) throw new StorageError(400, "tags must be a string array");
997
+ item.tags = tags;
998
+ }
994
999
  return item;
995
1000
  }
996
1001
  var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
@@ -1001,11 +1006,11 @@ function ensureDdl(db) {
1001
1006
  const n = db.dialect === "postgres" ? " DOUBLE PRECISION" : "";
1002
1007
  const t = db.dialect === "postgres" ? ' TEXT COLLATE "C"' : " TEXT";
1003
1008
  const ddl = [
1004
- `CREATE TABLE IF NOT EXISTS "domain_note" ("note_id"${t} PRIMARY KEY NOT NULL, "note_type"${t}, "catalog"${t}, "source_id"${t}, "source_path"${t}, "title"${t} NOT NULL, "title_search"${t} NOT NULL, "created_at"${n} NOT NULL, "date"${n}, "text_search"${t} NOT NULL, "journal"${n} NOT NULL, "payload"${t} NOT NULL)`,
1009
+ `CREATE TABLE IF NOT EXISTS "domain_note" ("note_id"${t} PRIMARY KEY NOT NULL, "note_type"${t}, "source_id"${t}, "source_path"${t}, "title"${t} NOT NULL, "title_search"${t} NOT NULL, "created_at"${n} NOT NULL, "date"${n}, "text_search"${t} NOT NULL, "journal"${n} NOT NULL, "payload"${t} NOT NULL)`,
1005
1010
  `CREATE INDEX IF NOT EXISTS "idx_domain_note_date" ON "domain_note" ("date", "note_id")`,
1006
1011
  `CREATE INDEX IF NOT EXISTS "idx_domain_note_created" ON "domain_note" ("created_at", "note_id")`,
1007
1012
  `CREATE INDEX IF NOT EXISTS "idx_domain_note_source_path" ON "domain_note" ("source_path")`,
1008
- `CREATE TABLE IF NOT EXISTS "query_block" ("occurrence_id"${t} PRIMARY KEY NOT NULL, "source_note_id"${t}, "parent_note_id"${t}, "content_id"${t}, "section_key"${t}, "ordinal"${n}, "data_type"${t}, "source_id"${t}, "source_path"${t}, "catalog"${t}, "effective_date"${n} NOT NULL, "created_at"${n} NOT NULL, "note_title"${t}, "normalized_text"${t} NOT NULL, "journal"${n} NOT NULL, "payload"${t} NOT NULL)`,
1013
+ `CREATE TABLE IF NOT EXISTS "query_block" ("occurrence_id"${t} PRIMARY KEY NOT NULL, "source_note_id"${t}, "parent_note_id"${t}, "content_id"${t}, "section_key"${t}, "ordinal"${n} NOT NULL, "data_type"${t}, "source_id"${t}, "source_path"${t}, "effective_date"${n} NOT NULL, "created_at"${n} NOT NULL, "note_title"${t}, "normalized_text"${t} NOT NULL, "journal"${n} NOT NULL, "payload"${t} NOT NULL)`,
1009
1014
  `CREATE INDEX IF NOT EXISTS "idx_query_block_type" ON "query_block" ("data_type", "effective_date", "occurrence_id")`,
1010
1015
  `CREATE INDEX IF NOT EXISTS "idx_query_block_parent" ON "query_block" ("parent_note_id", "ordinal")`,
1011
1016
  `CREATE INDEX IF NOT EXISTS "idx_query_block_content" ON "query_block" ("content_id")`,
@@ -1016,17 +1021,16 @@ function ensureDdl(db) {
1016
1021
  for (const sql of ddl) await tx.exec(sql);
1017
1022
  });
1018
1023
  }
1019
- var DOMAIN_NOTE_COLS = ["note_id", "note_type", "catalog", "source_id", "source_path", "title", "title_search", "created_at", "date", "text_search", "journal", "payload"];
1020
- var QUERY_BLOCK_COLS = ["occurrence_id", "source_note_id", "parent_note_id", "content_id", "section_key", "ordinal", "data_type", "source_id", "source_path", "catalog", "effective_date", "created_at", "note_title", "normalized_text", "journal", "payload"];
1024
+ var DOMAIN_NOTE_COLS = ["note_id", "note_type", "source_id", "source_path", "title", "title_search", "created_at", "date", "text_search", "journal", "payload"];
1025
+ var QUERY_BLOCK_COLS = ["occurrence_id", "source_note_id", "parent_note_id", "content_id", "section_key", "ordinal", "data_type", "source_id", "source_path", "effective_date", "created_at", "note_title", "normalized_text", "journal", "payload"];
1021
1026
  var conflictUpdate = (table, cols, keys) => `ON CONFLICT (${keys.map((k) => `"${k}"`).join(", ")}) DO UPDATE SET ` + cols.filter((c) => !keys.includes(c)).map((c) => `"${c}" = excluded."${c}"`).join(",") + ` WHERE ` + cols.filter((col) => !keys.includes(col)).map((col) => `"${table}"."${col}" IS DISTINCT FROM excluded."${col}"`).join(" OR ");
1022
1027
  function domainNoteRow(note) {
1023
1028
  const id = text(note.id);
1024
1029
  if (!id) return void 0;
1025
- const source = sourceItem(note);
1030
+ const source = sourceNote(note);
1026
1031
  return [
1027
1032
  id,
1028
1033
  T(note.type),
1029
- T(wodWikiWql.catalogOfItem(source)),
1030
1034
  T(note.sourceId),
1031
1035
  T(note.sourcePath),
1032
1036
  T(note.title) ?? "",
@@ -1045,7 +1049,7 @@ async function upsertDomainNotes(conn, notes) {
1045
1049
  function queryBlockRow(block, parent) {
1046
1050
  const id = text(block.id);
1047
1051
  if (!id) return void 0;
1048
- const source = sourceItem(block);
1052
+ const source = sourceNote(block);
1049
1053
  const createdAt = N(block.createdAt);
1050
1054
  const effective = parent ? parent.date ?? parent.created_at : createdAt;
1051
1055
  return [
@@ -1058,7 +1062,6 @@ function queryBlockRow(block, parent) {
1058
1062
  T(block.dataType),
1059
1063
  T(block.sourceId),
1060
1064
  T(block.sourcePath),
1061
- T(wodWikiWql.catalogOfItem(source)),
1062
1065
  effective,
1063
1066
  createdAt,
1064
1067
  parent?.note.title ?? T(block.noteTitle),
@@ -1238,11 +1241,21 @@ async function rebuildProjections(conn) {
1238
1241
  `CREATE TABLE IF NOT EXISTS "${SCHEMA_TABLE}" ("key" TEXT PRIMARY KEY, "value" TEXT NOT NULL)`
1239
1242
  );
1240
1243
  for (const t of TABLES) await conn.exec(`DELETE FROM "${t}"`);
1241
- const notes = await conn.query(`SELECT "data" FROM "notes"`);
1242
- const noteObjs = notes.map((row) => decodeRowJson(String(row.data)));
1244
+ const notes = await conn.query(`SELECT "k0", "data" FROM "notes"`);
1245
+ const noteObjs = notes.map((row) => wodWikiWql.migrateLegacyRow(decodeRowJson(String(row.data))));
1246
+ for (let i = 0; i < notes.length; i++) {
1247
+ if (wodWikiWql.hasLegacyIdentity(decodeRowJson(String(notes[i].data)))) {
1248
+ await conn.exec(`UPDATE "notes" SET "data" = ? WHERE "k0" = ?`, [encodeRowJson(noteObjs[i]), String(notes[i].k0)]);
1249
+ }
1250
+ }
1243
1251
  await upsertDomainNotes(conn, noteObjs);
1244
- const blocks = await conn.query(`SELECT "data" FROM "block_index"`);
1245
- const blockObjs = blocks.map((row) => decodeRowJson(String(row.data)));
1252
+ const blocks = await conn.query(`SELECT "k0", "data" FROM "block_index"`);
1253
+ const blockObjs = blocks.map((row) => wodWikiWql.migrateLegacyRow(decodeRowJson(String(row.data))));
1254
+ for (let i = 0; i < blocks.length; i++) {
1255
+ if (wodWikiWql.hasLegacyIdentity(decodeRowJson(String(blocks[i].data)))) {
1256
+ await conn.exec(`UPDATE "block_index" SET "data" = ? WHERE "k0" = ?`, [encodeRowJson(blockObjs[i]), String(blocks[i].k0)]);
1257
+ }
1258
+ }
1246
1259
  const index = indexNoteIdentities(noteObjs);
1247
1260
  await upsertQueryBlocks(conn, blockObjs.map((b) => [b, resolveParentFrom(index, b)]));
1248
1261
  await conn.exec(
@@ -1344,7 +1357,7 @@ var MAX_CLAUSES = 32;
1344
1357
  var MAX_VALUES = 64;
1345
1358
  var MAX_VALUE_LEN = 256;
1346
1359
  var MAX_ORDER = 4;
1347
- var listFields = ["id", "noteId", "type", "catalog", "source", "tags", "effort"];
1360
+ var listFields = ["id", "noteId", "type", "source", "tags", "effort"];
1348
1361
  var onlyKeys = (obj, allowed) => Object.keys(obj).every((k) => allowed.includes(k));
1349
1362
  function bad(msg) {
1350
1363
  return new StorageError(400, msg);
@@ -1376,6 +1389,15 @@ function parsePredicate(v, plan) {
1376
1389
  }
1377
1390
  return { field, values: strArray(obj.values, `predicate ${field}.values`), ...negate ? { negate } : {} };
1378
1391
  }
1392
+ if (field === "typedTag") {
1393
+ if (!onlyKeys(obj, ["field", "tagType", "values", "negate"])) {
1394
+ throw bad("predicate typedTag accepts only field/tagType/values/negate");
1395
+ }
1396
+ if (typeof obj.tagType !== "string" || obj.tagType.length < 1 || obj.tagType.length > MAX_VALUE_LEN) {
1397
+ throw bad(`predicate typedTag.tagType must be 1..=${MAX_VALUE_LEN} chars`);
1398
+ }
1399
+ return { field: "typedTag", tagType: obj.tagType, values: strArray(obj.values, "predicate typedTag.values"), ...negate ? { negate } : {} };
1400
+ }
1379
1401
  if (field === "text") {
1380
1402
  if (!onlyKeys(obj, ["field", "value"]) || negate) throw bad("predicate text accepts only {field, value}");
1381
1403
  if (typeof obj.value !== "string" || obj.value.length < 1 || obj.value.length > MAX_VALUE_LEN) {
@@ -1408,9 +1430,9 @@ function parsePredicate(v, plan) {
1408
1430
  }
1409
1431
  if (field === "defaultNotes") {
1410
1432
  if (plan === "blocks") throw bad("predicate defaultNotes does not apply to the blocks plan");
1411
- if (!onlyKeys(obj, ["field", "feeds"])) throw bad("predicate defaultNotes accepts only {field, feeds}");
1412
- if (typeof obj.feeds !== "boolean") throw bad("predicate defaultNotes.feeds must be boolean");
1413
- return { field: "defaultNotes", feeds: obj.feeds };
1433
+ if (!onlyKeys(obj, ["field", "nonPages"])) throw bad("predicate defaultNotes accepts only {field, nonPages}");
1434
+ if (typeof obj.nonPages !== "boolean") throw bad("predicate defaultNotes.nonPages must be boolean");
1435
+ return { field: "defaultNotes", nonPages: obj.nonPages };
1414
1436
  }
1415
1437
  throw bad(`unknown predicate field: ${field}`);
1416
1438
  }
@@ -1435,8 +1457,8 @@ function parseQuery(body) {
1435
1457
  if (body === null || typeof body !== "object" || Array.isArray(body)) throw bad("body must be a DomainQuery object");
1436
1458
  const obj = body;
1437
1459
  const plan = obj.plan;
1438
- if (plan !== "notes" && plan !== "blocks" && plan !== "entries") {
1439
- throw bad("body.plan must be notes | blocks | entries");
1460
+ if (plan !== "notes" && plan !== "blocks" && plan !== "detail") {
1461
+ throw bad("body.plan must be notes | blocks | detail");
1440
1462
  }
1441
1463
  for (const k of Object.keys(obj)) {
1442
1464
  if (!["plan", "selection", "filters", "order", "offset", "limit"].includes(k)) {
@@ -1493,25 +1515,17 @@ var Sql = class {
1493
1515
  return `${col} IN (${values.map((v) => this.bind(v)).join(", ")})`;
1494
1516
  }
1495
1517
  };
1496
- var SOURCE_ALL_KINDS = ["journal", "feeds", "guides", "playground", "dashboards", "efforts"];
1518
+ var SOURCE_ALL_KINDS = ["journal", ...wodWikiWql.WQL_SOURCE_VALUES.filter((k) => k !== "journal")];
1497
1519
  function sourceKindSql(ctx, kind, sid, typeExpr, journalCol) {
1498
- switch (kind) {
1499
- case "journal":
1500
- return `(${journalCol} = 1)`;
1501
- case "feed":
1502
- case "feeds":
1503
- return `(substr(${sid}, 1, 11) = 'collection:' OR substr(${sid}, 1, 16) = 'page:collection:')`;
1504
- case "page":
1505
- case "pages":
1506
- return `(${sid} = 'page' OR substr(${sid}, 1, 5) = 'page:' OR substr(${sid}, 1, 7) = 'guides:')`;
1507
- case "guide":
1508
- case "guides":
1509
- return `(substr(${sid}, 1, 7) = 'guides:')`;
1510
- case "playground":
1511
- return typeExpr ? `(${sid} = 'playground' OR (${typeExpr} IS NOT NULL AND ${typeExpr} = 'playground'))` : `(${sid} = 'playground')`;
1512
- default:
1513
- return `(${sid} = ${ctx.bind(kind)})`;
1520
+ if (kind === "journal") return `(${journalCol} = 1)`;
1521
+ const shape = wodWikiWql.sourceShape(kind) ?? { exact: [kind] };
1522
+ const parts = [];
1523
+ for (const v of shape.exact ?? []) parts.push(`${sid} = ${ctx.bind(v)}`);
1524
+ for (const p of shape.prefixes ?? []) parts.push(`substr(${sid}, 1, ${p.length}) = ${ctx.bind(p)}`);
1525
+ if (shape.legacyType) {
1526
+ parts.push(typeExpr ? `${typeExpr} = ${ctx.bind(shape.legacyType)}` : "FALSE");
1514
1527
  }
1528
+ return `(${parts.join(" OR ")})`;
1515
1529
  }
1516
1530
  function sourcePredicateSql(ctx, values, negate, sid, typeExpr, journalCol) {
1517
1531
  const matched = values.map((v) => sourceKindSql(ctx, v, sid, typeExpr, journalCol)).join(" OR ");
@@ -1524,7 +1538,6 @@ function predicateSql(ctx, p, plan) {
1524
1538
  const sid = blocks ? 'q."source_id"' : 'n."source_id"';
1525
1539
  const typeExpr = blocks ? null : 'n."note_type"';
1526
1540
  const typeCol = blocks ? 'q."data_type"' : 'n."note_type"';
1527
- const catalogCol = blocks ? 'q."catalog"' : 'n."catalog"';
1528
1541
  const journalCol = blocks ? 'q."journal"' : 'n."journal"';
1529
1542
  const noteId = blocks ? 'q."source_note_id"' : 'n."note_id"';
1530
1543
  switch (p.field) {
@@ -1534,8 +1547,6 @@ function predicateSql(ctx, p, plan) {
1534
1547
  return wrap(p.negate === true, ctx.inList(noteId, p.values));
1535
1548
  case "type":
1536
1549
  return wrap(p.negate === true, ctx.inList(typeCol, p.values));
1537
- case "catalog":
1538
- return wrap(p.negate === true, ctx.inList(catalogCol, p.values));
1539
1550
  case "source":
1540
1551
  return sourcePredicateSql(ctx, p.values, p.negate === true, sid, typeExpr, journalCol);
1541
1552
  case "tags":
@@ -1543,6 +1554,11 @@ function predicateSql(ctx, p, plan) {
1543
1554
  p.negate === true,
1544
1555
  `EXISTS (SELECT 1 FROM "note_tags" nt JOIN "tags" t ON t."k0" = nt."i_by_tag_0" WHERE nt."i_by_note_0" = ${noteId} AND t."i_by_label_0" IN (${p.values.map((v) => ctx.bind(v)).join(", ")}))`
1545
1556
  );
1557
+ case "typedTag":
1558
+ return wrap(
1559
+ p.negate === true,
1560
+ `EXISTS (SELECT 1 FROM "note_tags" nt JOIN "tags" t ON t."k0" = nt."i_by_tag_0" WHERE nt."i_by_note_0" = ${noteId} AND t."i_by_type_0" = ${ctx.bind(p.tagType)} AND t."i_by_label_0" IN (${p.values.map((v) => ctx.bind(v)).join(", ")}))`
1561
+ );
1546
1562
  case "effort":
1547
1563
  return wrap(
1548
1564
  p.negate === true,
@@ -1564,7 +1580,7 @@ function predicateSql(ctx, p, plan) {
1564
1580
  return `(${sid} IS NULL OR ${sid} = '' OR ${all})`;
1565
1581
  }
1566
1582
  case "defaultNotes":
1567
- return p.feeds ? `(n."note_type" IS NULL OR n."note_type" <> 'page')` : `(n."note_type" = 'note' OR (substr(COALESCE(n."source_id", ''), 1, 5) <> 'page:' AND substr(COALESCE(n."source_id", ''), 1, 7) <> 'guides:' AND COALESCE(n."note_type", '') NOT IN ('feed','syntax','behavior','analytics','dashboard','home','page')))`;
1583
+ return p.nonPages ? `(n."note_type" IS NULL OR n."note_type" <> 'page')` : `(n."note_type" = 'note' OR (substr(COALESCE(n."source_id", ''), 1, 5) <> 'page:' AND substr(COALESCE(n."source_id", ''), 1, 7) <> 'guides:' AND COALESCE(n."note_type", '') NOT IN ('feed','syntax','behavior','analytics','dashboard','home','page')))`;
1568
1584
  }
1569
1585
  }
1570
1586
  function clausesSql(ctx, clauses, plan) {
@@ -1615,7 +1631,7 @@ async function execute(conn, q, pg) {
1615
1631
  const matchedCount = q.filters.length === 0 ? selectedCount : await count(fullWhere, allParams);
1616
1632
  const ordering = orderSql(q.order, plan);
1617
1633
  const paging = pagingSql(q, pg);
1618
- if (plan !== "entries") {
1634
+ if (plan !== "detail") {
1619
1635
  const pageSql2 = `SELECT "payload" FROM ${table}${fullWhere} ORDER BY ${ordering}${paging}`;
1620
1636
  const rows2 = (await conn.query(pageSql2, allParams)).map((r) => decodeRowJson(String(r.payload)));
1621
1637
  const result = { projectionVersion: DOMAIN_PROJECTION_VERSION, selectedCount, matchedCount, plan, rows: rows2 };