@openparachute/vault 0.6.4-rc.6 → 0.6.4-rc.7
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/README.md +2 -6
- package/package.json +1 -1
- package/src/routes.ts +19 -29
- package/src/subscribe.test.ts +23 -2
- package/src/vault.test.ts +43 -17
package/README.md
CHANGED
|
@@ -586,16 +586,12 @@ Range params require content in the response — with `include_content=false` (o
|
|
|
586
586
|
|
|
587
587
|
### Incremental rebuilds: "what changed since X"
|
|
588
588
|
|
|
589
|
-
The SSG / sync pattern.
|
|
589
|
+
The SSG / sync pattern. Bracket-style is the query-string date filter. (The flat `date_field` / `date_from` / `date_to` params were removed in 0.6.4 — vault#288 — and are now ignored.)
|
|
590
590
|
|
|
591
591
|
```bash
|
|
592
|
-
# Bracket-style (
|
|
592
|
+
# Bracket-style (the query-string date filter)
|
|
593
593
|
curl -H "Authorization: Bearer $VAULT_TOKEN" \
|
|
594
594
|
"http://localhost:1940/vault/default/api/notes?meta[updated_at][gte]=2026-04-01T00:00:00Z"
|
|
595
|
-
|
|
596
|
-
# Flat form (DEPRECATED in 0.4.3; planned removal in a later 0.x per vault#288)
|
|
597
|
-
curl -H "Authorization: Bearer $VAULT_TOKEN" \
|
|
598
|
-
"http://localhost:1940/vault/default/api/notes?date_field=updated_at&date_from=2026-04-01T00:00:00Z"
|
|
599
595
|
```
|
|
600
596
|
|
|
601
597
|
```jsonc
|
package/package.json
CHANGED
package/src/routes.ts
CHANGED
|
@@ -328,7 +328,7 @@ function parseMetaBrackets(url: URL): {
|
|
|
328
328
|
return {
|
|
329
329
|
error: json(
|
|
330
330
|
{
|
|
331
|
-
error: `bracket-date filter on \`${field}\` supports only \`gte\` (inclusive lower bound) and \`lt\` (exclusive upper bound). Got: \`${op}\`. The dateFilter contract
|
|
331
|
+
error: `bracket-date filter on \`${field}\` supports only \`gte\` (inclusive lower bound) and \`lt\` (exclusive upper bound). Got: \`${op}\`. The dateFilter contract is half-open by design.`,
|
|
332
332
|
code: "INVALID_QUERY",
|
|
333
333
|
},
|
|
334
334
|
400,
|
|
@@ -623,20 +623,13 @@ export function parseNotesQueryOpts(url: URL): {
|
|
|
623
623
|
lastUpdatedBy: parseQuery(url, "last_updated_by") ?? undefined,
|
|
624
624
|
createdVia: parseQuery(url, "created_via") ?? undefined,
|
|
625
625
|
lastUpdatedVia: parseQuery(url, "last_updated_via") ?? undefined,
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
to: parseQuery(url, "date_to") ?? undefined,
|
|
634
|
-
},
|
|
635
|
-
}
|
|
636
|
-
: {
|
|
637
|
-
dateFrom: parseQuery(url, "date_from") ?? undefined,
|
|
638
|
-
dateTo: parseQuery(url, "date_to") ?? undefined,
|
|
639
|
-
}),
|
|
626
|
+
// Date-range filtering on the query string is bracket-style only:
|
|
627
|
+
// `?meta[created_at][gte]=…&meta[created_at][lt]=…` (see
|
|
628
|
+
// `parseMetaBrackets`). The flat `date_field` / `date_from` / `date_to`
|
|
629
|
+
// params were removed in 0.6.4 (vault#288, breaking) — they are now
|
|
630
|
+
// ignored. The MCP `date_from` / `date_to` shorthand is a separate,
|
|
631
|
+
// supported convenience and is unaffected.
|
|
632
|
+
...(bracket.dateFilter ? { dateFilter: bracket.dateFilter } : {}),
|
|
640
633
|
sort: (parseQuery(url, "sort") as "asc" | "desc") ?? undefined,
|
|
641
634
|
orderBy: parseQuery(url, "order_by") ?? undefined,
|
|
642
635
|
limit: parseInt10(parseQuery(url, "limit")) ?? 50,
|
|
@@ -907,7 +900,7 @@ async function handleNotesInner(
|
|
|
907
900
|
|
|
908
901
|
// Structured query
|
|
909
902
|
//
|
|
910
|
-
//
|
|
903
|
+
// Date-range filtering on the query string uses one syntax:
|
|
911
904
|
//
|
|
912
905
|
// - **Bracket-style** (canonical, vault#285 friction point 1.3):
|
|
913
906
|
// `?meta[field][op]=value` / `?meta[created_at][gte]=…`. Exposes
|
|
@@ -915,21 +908,18 @@ async function handleNotesInner(
|
|
|
915
908
|
// not_in/exists) and the dateFilter bridge through one consistent
|
|
916
909
|
// shape. See `parseMetaBrackets` for the grammar.
|
|
917
910
|
//
|
|
918
|
-
//
|
|
919
|
-
//
|
|
920
|
-
//
|
|
921
|
-
//
|
|
922
|
-
//
|
|
923
|
-
//
|
|
924
|
-
// `meta[created_at][gte]=X` and `
|
|
925
|
-
// the bracket form is the dateFilter the engine sees; the flat
|
|
926
|
-
// params are silently dropped. We don't error — the bracket form is
|
|
927
|
-
// documented as canonical, and rejecting the overlap would block a
|
|
928
|
-
// realistic migration path where a caller half-converted their code.
|
|
911
|
+
// The flat date params (`?date_field=created_at&date_from=…&date_to=…`
|
|
912
|
+
// and the legacy bare `?date_from=…&date_to=…`) were REMOVED in 0.6.4
|
|
913
|
+
// (vault#288, breaking change). They are now silently ignored — a
|
|
914
|
+
// request that passes only flat date params comes back unfiltered.
|
|
915
|
+
// Bracket-style is functionally complete (full operator set), so no
|
|
916
|
+
// capability was lost. Migrate `date_field=created_at&date_from=X` to
|
|
917
|
+
// `meta[created_at][gte]=X` (and `date_to=Y` to `meta[created_at][lt]=Y`).
|
|
929
918
|
//
|
|
930
919
|
// Surface asymmetry: REST flattens to a query string; MCP takes a
|
|
931
|
-
// nested `date_filter: { field, from, to }` object directly
|
|
932
|
-
//
|
|
920
|
+
// nested `date_filter: { field, from, to }` object directly (plus a
|
|
921
|
+
// top-level `date_from` / `date_to` shorthand, which is unaffected by
|
|
922
|
+
// this removal). Both lower to the same store-level `dateFilter` shape.
|
|
933
923
|
// Structured-query parsing is shared with the live `/subscribe` route
|
|
934
924
|
// (see `parseNotesQueryOpts`) so both endpoints lower an identical query
|
|
935
925
|
// string to the same `QueryOpts` — predicate parity by construction.
|
package/src/subscribe.test.ts
CHANGED
|
@@ -416,9 +416,12 @@ describe("handleSubscribe — rejected query shapes", () => {
|
|
|
416
416
|
expect(body.error).toContain("has_links");
|
|
417
417
|
});
|
|
418
418
|
|
|
419
|
-
it("date filter → 400 (M1) —
|
|
419
|
+
it("date filter → 400 (M1) — bracket date_filter on created_at", async () => {
|
|
420
|
+
// The flat `date_from` param was removed in 0.6.4 (vault#288) and is now
|
|
421
|
+
// ignored, so it no longer reaches the M1 date guard. Bracket-style is the
|
|
422
|
+
// only query-string date filter; assert it's still rejected for live subs.
|
|
420
423
|
const res = await handleSubscribe(
|
|
421
|
-
subscribeReq("
|
|
424
|
+
subscribeReq("meta%5Bcreated_at%5D%5Bgte%5D=2026-01-01"),
|
|
422
425
|
store,
|
|
423
426
|
VAULT,
|
|
424
427
|
unscopedScope(),
|
|
@@ -430,6 +433,24 @@ describe("handleSubscribe — rejected query shapes", () => {
|
|
|
430
433
|
expect(body.error).toContain("date");
|
|
431
434
|
});
|
|
432
435
|
|
|
436
|
+
it("flat date_from is ignored → subscription created (vault#288 removal)", async () => {
|
|
437
|
+
// Documents the breaking change: the removed flat param no longer reaches
|
|
438
|
+
// the date guard, so a sub that ONLY carries it is now a valid (unfiltered)
|
|
439
|
+
// subscription rather than a 400.
|
|
440
|
+
const res = await handleSubscribe(
|
|
441
|
+
subscribeReq("date_from=2026-01-01"),
|
|
442
|
+
store,
|
|
443
|
+
VAULT,
|
|
444
|
+
unscopedScope(),
|
|
445
|
+
manager,
|
|
446
|
+
);
|
|
447
|
+
expect(res.status).toBe(200);
|
|
448
|
+
expect(res.headers.get("Content-Type")).toBe("text/event-stream");
|
|
449
|
+
const r = sseReader(res);
|
|
450
|
+
await r.pump();
|
|
451
|
+
await r.close();
|
|
452
|
+
});
|
|
453
|
+
|
|
433
454
|
it("date filter → 400 (M1) — bracket date_filter on updated_at", async () => {
|
|
434
455
|
const res = await handleSubscribe(
|
|
435
456
|
subscribeReq("meta%5Bupdated_at%5D%5Bgte%5D=2026-01-01"),
|
package/src/vault.test.ts
CHANGED
|
@@ -1862,13 +1862,15 @@ describe("HTTP /notes", async () => {
|
|
|
1862
1862
|
expect(body.map((n) => n.content)).toEqual(["plain"]);
|
|
1863
1863
|
});
|
|
1864
1864
|
|
|
1865
|
-
// ---- updated_at filter via
|
|
1865
|
+
// ---- updated_at filter via bracket date (vault#285 friction point 1.5) ----
|
|
1866
1866
|
//
|
|
1867
|
-
// HTTP plumbing routes `
|
|
1868
|
-
//
|
|
1869
|
-
//
|
|
1870
|
-
//
|
|
1871
|
-
|
|
1867
|
+
// HTTP plumbing routes `meta[updated_at][gte]=…` straight to the core
|
|
1868
|
+
// `dateFilter` resolver, which recognizes `updated_at` as a real column.
|
|
1869
|
+
// Smoke-tests the end-to-end HTTP path; the engine-side semantics are
|
|
1870
|
+
// exercised in core.test.ts. (The flat `date_field=updated_at&date_from=…`
|
|
1871
|
+
// shape was removed in 0.6.4 — vault#288 — bracket-style is the only
|
|
1872
|
+
// query-string date filter.)
|
|
1873
|
+
test("GET /notes?meta[updated_at][gte]=… filters by last-write time", async () => {
|
|
1872
1874
|
const a = await store.createNote("untouched", { id: "ua", path: "ua" });
|
|
1873
1875
|
const b = await store.createNote("modified", { id: "ub", path: "ub" });
|
|
1874
1876
|
// Bump b's updated_at into the test window, leave a's at its createdAt.
|
|
@@ -1878,7 +1880,7 @@ describe("HTTP /notes", async () => {
|
|
|
1878
1880
|
.run("2026-04-25T00:00:00.000Z", b.id);
|
|
1879
1881
|
|
|
1880
1882
|
const res = await handleNotes(
|
|
1881
|
-
mkReq("GET", "/notes?
|
|
1883
|
+
mkReq("GET", "/notes?meta[updated_at][gte]=2026-04-01&include_content=true"),
|
|
1882
1884
|
store,
|
|
1883
1885
|
"",
|
|
1884
1886
|
);
|
|
@@ -2880,7 +2882,7 @@ describe("HTTP /notes", async () => {
|
|
|
2880
2882
|
});
|
|
2881
2883
|
|
|
2882
2884
|
// ---- Bridge: created_at / updated_at via brackets route to dateFilter ----
|
|
2883
|
-
test("`meta[created_at][gte]=…` routes to dateFilter
|
|
2885
|
+
test("`meta[created_at][gte]=…` routes to dateFilter", async () => {
|
|
2884
2886
|
await store.createNote("old", { created_at: "2026-01-15T00:00:00.000Z" });
|
|
2885
2887
|
await store.createNote("new", { created_at: "2026-04-15T00:00:00.000Z" });
|
|
2886
2888
|
|
|
@@ -2890,14 +2892,37 @@ describe("HTTP /notes", async () => {
|
|
|
2890
2892
|
"",
|
|
2891
2893
|
);
|
|
2892
2894
|
const bracketBody = await bracketRes.json() as any[];
|
|
2893
|
-
|
|
2895
|
+
expect(bracketBody.map((n) => n.content)).toEqual(["new"]);
|
|
2896
|
+
});
|
|
2897
|
+
|
|
2898
|
+
// ---- Removed: flat date params are now ignored (vault#288, breaking) ----
|
|
2899
|
+
// The flat `date_field` / `date_from` / `date_to` query params were
|
|
2900
|
+
// removed in 0.6.4. A request that passes ONLY the flat shape is no
|
|
2901
|
+
// longer date-filtered — it comes back unfiltered. Use bracket-style
|
|
2902
|
+
// (`meta[created_at][gte]=…`) instead. (The MCP `date_from`/`date_to`
|
|
2903
|
+
// shorthand is a separate, supported path and is unaffected.)
|
|
2904
|
+
test("flat date params (date_field/date_from/date_to) are ignored", async () => {
|
|
2905
|
+
await store.createNote("old", { created_at: "2026-01-15T00:00:00.000Z" });
|
|
2906
|
+
await store.createNote("new", { created_at: "2026-04-15T00:00:00.000Z" });
|
|
2907
|
+
|
|
2908
|
+
const targetedRes = await handleNotes(
|
|
2894
2909
|
mkReq("GET", "/notes?date_field=created_at&date_from=2026-04-01&include_content=true"),
|
|
2895
2910
|
store,
|
|
2896
2911
|
"",
|
|
2897
2912
|
);
|
|
2898
|
-
const
|
|
2899
|
-
expect(
|
|
2900
|
-
|
|
2913
|
+
const targetedBody = await targetedRes.json() as any[];
|
|
2914
|
+
expect(targetedRes.status).toBe(200);
|
|
2915
|
+
// Both notes returned — the flat param did not filter.
|
|
2916
|
+
expect(targetedBody.map((n) => n.content).sort()).toEqual(["new", "old"]);
|
|
2917
|
+
|
|
2918
|
+
const bareRes = await handleNotes(
|
|
2919
|
+
mkReq("GET", "/notes?date_from=2026-04-01&include_content=true"),
|
|
2920
|
+
store,
|
|
2921
|
+
"",
|
|
2922
|
+
);
|
|
2923
|
+
const bareBody = await bareRes.json() as any[];
|
|
2924
|
+
expect(bareRes.status).toBe(200);
|
|
2925
|
+
expect(bareBody.map((n) => n.content).sort()).toEqual(["new", "old"]);
|
|
2901
2926
|
});
|
|
2902
2927
|
|
|
2903
2928
|
test("`meta[updated_at][gte]=…` routes to dateFilter on n.updated_at", async () => {
|
|
@@ -3047,13 +3072,14 @@ describe("HTTP /notes", async () => {
|
|
|
3047
3072
|
expect(body.error).toContain("not_in");
|
|
3048
3073
|
});
|
|
3049
3074
|
|
|
3050
|
-
// ----
|
|
3051
|
-
test("
|
|
3075
|
+
// ---- Bracket applies; co-passed flat params are ignored (vault#288) ----
|
|
3076
|
+
test("bracket date filter applies even when removed flat params are also passed", async () => {
|
|
3052
3077
|
await store.createNote("old", { created_at: "2026-01-15T00:00:00.000Z" });
|
|
3053
3078
|
await store.createNote("new", { created_at: "2026-04-15T00:00:00.000Z" });
|
|
3054
|
-
// Bracket says "from 2026-04-01"; flat
|
|
3055
|
-
// flat
|
|
3056
|
-
//
|
|
3079
|
+
// Bracket says "from 2026-04-01"; the (removed) flat params say "from
|
|
3080
|
+
// 2020-01-01". The flat params are now inert, so only the bracket
|
|
3081
|
+
// filter applies — back comes only the post-April note. (Pre-0.6.4
|
|
3082
|
+
// this exercised bracket-wins precedence; flat is now simply ignored.)
|
|
3057
3083
|
const res = await handleNotes(
|
|
3058
3084
|
mkReq(
|
|
3059
3085
|
"GET",
|