@karmaniverous/jeeves-meta 0.3.2 → 0.3.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.
package/dist/cli.js CHANGED
@@ -48,20 +48,20 @@ const synthConfigSchema = z.object({
48
48
  /** Number of metas to synthesize per invocation. */
49
49
  batchSize: z.number().int().min(1).default(1),
50
50
  /**
51
- * Watcher metadata properties for live .meta/meta.json files.
52
- * Virtual rules use these to tag live metas; scan queries derive
53
- * their filter from the first domain value.
51
+ * Watcher metadata properties applied to live .meta/meta.json files.
52
+ * Virtual rules set these on every indexed live meta point.
53
+ * Discovery filter is derived from these properties.
54
+ * Default: `\{ _meta: "current" \}`
54
55
  */
55
- metaProperty: z
56
- .object({ domains: z.array(z.string()).min(1) })
57
- .default({ domains: ['meta'] }),
56
+ metaProperty: z.record(z.string(), z.unknown()).default({ _meta: 'current' }),
58
57
  /**
59
- * Watcher metadata properties for .meta/archive/** snapshots.
60
- * Virtual rules use these to tag archive files.
58
+ * Watcher metadata properties applied to .meta/archive/** snapshots.
59
+ * Virtual rules set these on every indexed archive point.
60
+ * Default: `\{ _meta: "archive" \}`
61
61
  */
62
62
  metaArchiveProperty: z
63
- .object({ domains: z.array(z.string()).min(1) })
64
- .default({ domains: ['meta-archive'] }),
63
+ .record(z.string(), z.unknown())
64
+ .default({ _meta: 'archive' }),
65
65
  });
66
66
 
67
67
  /**
@@ -373,25 +373,50 @@ async function paginatedScan(watcher, params) {
373
373
  *
374
374
  * @module discovery/discoverMetas
375
375
  */
376
+ /**
377
+ * Build a single Qdrant filter clause from a key-value pair.
378
+ *
379
+ * Arrays use `match.value` on the first element (Qdrant array membership).
380
+ * Scalars (string, number, boolean) use `match.value` directly.
381
+ * Objects and other non-filterable types are skipped with a warning.
382
+ */
383
+ function buildMatchClause(key, value) {
384
+ if (Array.isArray(value)) {
385
+ if (value.length === 0)
386
+ return null;
387
+ return { key, match: { value: value[0] } };
388
+ }
389
+ if (typeof value === 'string' ||
390
+ typeof value === 'number' ||
391
+ typeof value === 'boolean') {
392
+ return { key, match: { value } };
393
+ }
394
+ // Non-filterable value (object, null, etc.) — valid for tagging but
395
+ // cannot be expressed as a Qdrant match clause.
396
+ return null;
397
+ }
376
398
  /**
377
399
  * Build a Qdrant filter from config metaProperty.
378
400
  *
401
+ * Iterates all key-value pairs in `metaProperty` (a generic record)
402
+ * to construct `must` clauses. Always appends `file_path: meta.json`
403
+ * for deduplication.
404
+ *
379
405
  * @param config - Synth config with metaProperty.
380
406
  * @returns Qdrant filter object for scanning live metas.
381
407
  */
382
408
  function buildMetaFilter(config) {
383
- return {
384
- must: [
385
- {
386
- key: 'domains',
387
- match: { value: config.metaProperty.domains[0] },
388
- },
389
- {
390
- key: 'file_path',
391
- match: { text: 'meta.json' },
392
- },
393
- ],
394
- };
409
+ const must = [];
410
+ for (const [key, value] of Object.entries(config.metaProperty)) {
411
+ const clause = buildMatchClause(key, value);
412
+ if (clause)
413
+ must.push(clause);
414
+ }
415
+ must.push({
416
+ key: 'file_path',
417
+ match: { text: 'meta.json' },
418
+ });
419
+ return { must };
395
420
  }
396
421
  /**
397
422
  * Discover all .meta/ directories via watcher scan.
@@ -1475,6 +1500,8 @@ async function orchestrateOnce(config, executor, watcher, targetPath) {
1475
1500
  const candidates = [];
1476
1501
  for (const node of tree.nodes.values()) {
1477
1502
  const meta = metas.get(node.metaPath);
1503
+ if (!meta)
1504
+ continue; // Node not in metas map (e.g. unreadable meta.json)
1478
1505
  const staleness = actualStaleness(meta);
1479
1506
  if (staleness > 0) {
1480
1507
  candidates.push({ node, meta, actualStaleness: staleness });
package/dist/index.d.ts CHANGED
@@ -53,12 +53,8 @@ declare const synthConfigSchema: z.ZodObject<{
53
53
  defaultCritic: z.ZodString;
54
54
  skipUnchanged: z.ZodDefault<z.ZodBoolean>;
55
55
  batchSize: z.ZodDefault<z.ZodNumber>;
56
- metaProperty: z.ZodDefault<z.ZodObject<{
57
- domains: z.ZodArray<z.ZodString>;
58
- }, z.core.$strip>>;
59
- metaArchiveProperty: z.ZodDefault<z.ZodObject<{
60
- domains: z.ZodArray<z.ZodString>;
61
- }, z.core.$strip>>;
56
+ metaProperty: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
57
+ metaArchiveProperty: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
62
58
  }, z.core.$strip>;
63
59
  /** Inferred type for jeeves-meta configuration. */
64
60
  type SynthConfig = z.infer<typeof synthConfigSchema>;
@@ -347,6 +343,10 @@ interface WatcherClient {
347
343
  /**
348
344
  * Build a Qdrant filter from config metaProperty.
349
345
  *
346
+ * Iterates all key-value pairs in `metaProperty` (a generic record)
347
+ * to construct `must` clauses. Always appends `file_path: meta.json`
348
+ * for deduplication.
349
+ *
350
350
  * @param config - Synth config with metaProperty.
351
351
  * @returns Qdrant filter object for scanning live metas.
352
352
  */
package/dist/index.js CHANGED
@@ -143,20 +143,20 @@ const synthConfigSchema = z.object({
143
143
  /** Number of metas to synthesize per invocation. */
144
144
  batchSize: z.number().int().min(1).default(1),
145
145
  /**
146
- * Watcher metadata properties for live .meta/meta.json files.
147
- * Virtual rules use these to tag live metas; scan queries derive
148
- * their filter from the first domain value.
146
+ * Watcher metadata properties applied to live .meta/meta.json files.
147
+ * Virtual rules set these on every indexed live meta point.
148
+ * Discovery filter is derived from these properties.
149
+ * Default: `\{ _meta: "current" \}`
149
150
  */
150
- metaProperty: z
151
- .object({ domains: z.array(z.string()).min(1) })
152
- .default({ domains: ['meta'] }),
151
+ metaProperty: z.record(z.string(), z.unknown()).default({ _meta: 'current' }),
153
152
  /**
154
- * Watcher metadata properties for .meta/archive/** snapshots.
155
- * Virtual rules use these to tag archive files.
153
+ * Watcher metadata properties applied to .meta/archive/** snapshots.
154
+ * Virtual rules set these on every indexed archive point.
155
+ * Default: `\{ _meta: "archive" \}`
156
156
  */
157
157
  metaArchiveProperty: z
158
- .object({ domains: z.array(z.string()).min(1) })
159
- .default({ domains: ['meta-archive'] }),
158
+ .record(z.string(), z.unknown())
159
+ .default({ _meta: 'archive' }),
160
160
  });
161
161
 
162
162
  /**
@@ -358,25 +358,50 @@ async function paginatedScan(watcher, params) {
358
358
  *
359
359
  * @module discovery/discoverMetas
360
360
  */
361
+ /**
362
+ * Build a single Qdrant filter clause from a key-value pair.
363
+ *
364
+ * Arrays use `match.value` on the first element (Qdrant array membership).
365
+ * Scalars (string, number, boolean) use `match.value` directly.
366
+ * Objects and other non-filterable types are skipped with a warning.
367
+ */
368
+ function buildMatchClause(key, value) {
369
+ if (Array.isArray(value)) {
370
+ if (value.length === 0)
371
+ return null;
372
+ return { key, match: { value: value[0] } };
373
+ }
374
+ if (typeof value === 'string' ||
375
+ typeof value === 'number' ||
376
+ typeof value === 'boolean') {
377
+ return { key, match: { value } };
378
+ }
379
+ // Non-filterable value (object, null, etc.) — valid for tagging but
380
+ // cannot be expressed as a Qdrant match clause.
381
+ return null;
382
+ }
361
383
  /**
362
384
  * Build a Qdrant filter from config metaProperty.
363
385
  *
386
+ * Iterates all key-value pairs in `metaProperty` (a generic record)
387
+ * to construct `must` clauses. Always appends `file_path: meta.json`
388
+ * for deduplication.
389
+ *
364
390
  * @param config - Synth config with metaProperty.
365
391
  * @returns Qdrant filter object for scanning live metas.
366
392
  */
367
393
  function buildMetaFilter(config) {
368
- return {
369
- must: [
370
- {
371
- key: 'domains',
372
- match: { value: config.metaProperty.domains[0] },
373
- },
374
- {
375
- key: 'file_path',
376
- match: { text: 'meta.json' },
377
- },
378
- ],
379
- };
394
+ const must = [];
395
+ for (const [key, value] of Object.entries(config.metaProperty)) {
396
+ const clause = buildMatchClause(key, value);
397
+ if (clause)
398
+ must.push(clause);
399
+ }
400
+ must.push({
401
+ key: 'file_path',
402
+ match: { text: 'meta.json' },
403
+ });
404
+ return { must };
380
405
  }
381
406
  /**
382
407
  * Discover all .meta/ directories via watcher scan.
@@ -1460,6 +1485,8 @@ async function orchestrateOnce(config, executor, watcher, targetPath) {
1460
1485
  const candidates = [];
1461
1486
  for (const node of tree.nodes.values()) {
1462
1487
  const meta = metas.get(node.metaPath);
1488
+ if (!meta)
1489
+ continue; // Node not in metas map (e.g. unreadable meta.json)
1463
1490
  const staleness = actualStaleness(meta);
1464
1491
  if (staleness > 0) {
1465
1492
  candidates.push({ node, meta, actualStaleness: staleness });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@karmaniverous/jeeves-meta",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "author": "Jason Williscroft",
5
5
  "description": "Knowledge synthesis engine for the Jeeves platform",
6
6
  "license": "BSD-3-Clause",