@opensaas/stack-core 0.30.0 → 0.31.0

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 (127) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +410 -0
  3. package/dist/access/access-filter.d.ts.map +1 -1
  4. package/dist/access/access-filter.js +14 -1
  5. package/dist/access/access-filter.js.map +1 -1
  6. package/dist/access/access-filter.test.js +50 -0
  7. package/dist/access/access-filter.test.js.map +1 -1
  8. package/dist/access/field-access.js +6 -6
  9. package/dist/access/field-access.js.map +1 -1
  10. package/dist/access/multi-column-read-write.test.js +46 -12
  11. package/dist/access/multi-column-read-write.test.js.map +1 -1
  12. package/dist/access/relationship-count.d.ts +60 -0
  13. package/dist/access/relationship-count.d.ts.map +1 -0
  14. package/dist/access/relationship-count.js +233 -0
  15. package/dist/access/relationship-count.js.map +1 -0
  16. package/dist/access/relationship-count.test.d.ts +2 -0
  17. package/dist/access/relationship-count.test.d.ts.map +1 -0
  18. package/dist/access/relationship-count.test.js +185 -0
  19. package/dist/access/relationship-count.test.js.map +1 -0
  20. package/dist/access/relationship-label-filter.d.ts +45 -0
  21. package/dist/access/relationship-label-filter.d.ts.map +1 -0
  22. package/dist/access/relationship-label-filter.js +93 -0
  23. package/dist/access/relationship-label-filter.js.map +1 -0
  24. package/dist/access/relationship-label-filter.test.d.ts +2 -0
  25. package/dist/access/relationship-label-filter.test.d.ts.map +1 -0
  26. package/dist/access/relationship-label-filter.test.js +126 -0
  27. package/dist/access/relationship-label-filter.test.js.map +1 -0
  28. package/dist/config/index.d.ts +1 -1
  29. package/dist/config/index.d.ts.map +1 -1
  30. package/dist/config/nav-count.d.ts +34 -0
  31. package/dist/config/nav-count.d.ts.map +1 -0
  32. package/dist/config/nav-count.js +71 -0
  33. package/dist/config/nav-count.js.map +1 -0
  34. package/dist/config/types.d.ts +305 -4
  35. package/dist/config/types.d.ts.map +1 -1
  36. package/dist/context/hook-pipeline.d.ts.map +1 -1
  37. package/dist/context/hook-pipeline.js +11 -1
  38. package/dist/context/hook-pipeline.js.map +1 -1
  39. package/dist/context/index.d.ts +28 -0
  40. package/dist/context/index.d.ts.map +1 -1
  41. package/dist/context/index.js +205 -0
  42. package/dist/context/index.js.map +1 -1
  43. package/dist/context/nested-operations.d.ts.map +1 -1
  44. package/dist/context/nested-operations.js +7 -1
  45. package/dist/context/nested-operations.js.map +1 -1
  46. package/dist/extend.d.ts +1 -0
  47. package/dist/extend.d.ts.map +1 -1
  48. package/dist/fields/index.d.ts +1 -1
  49. package/dist/fields/index.d.ts.map +1 -1
  50. package/dist/fields/index.js +166 -0
  51. package/dist/fields/index.js.map +1 -1
  52. package/dist/filter/collect.d.ts +32 -0
  53. package/dist/filter/collect.d.ts.map +1 -0
  54. package/dist/filter/collect.js +60 -0
  55. package/dist/filter/collect.js.map +1 -0
  56. package/dist/filter/filter.test.d.ts +2 -0
  57. package/dist/filter/filter.test.d.ts.map +1 -0
  58. package/dist/filter/filter.test.js +348 -0
  59. package/dist/filter/filter.test.js.map +1 -0
  60. package/dist/filter/index.d.ts +7 -0
  61. package/dist/filter/index.d.ts.map +1 -0
  62. package/dist/filter/index.js +16 -0
  63. package/dist/filter/index.js.map +1 -0
  64. package/dist/filter/map.d.ts +18 -0
  65. package/dist/filter/map.d.ts.map +1 -0
  66. package/dist/filter/map.js +60 -0
  67. package/dist/filter/map.js.map +1 -0
  68. package/dist/filter/parse.d.ts +33 -0
  69. package/dist/filter/parse.d.ts.map +1 -0
  70. package/dist/filter/parse.js +103 -0
  71. package/dist/filter/parse.js.map +1 -0
  72. package/dist/filter/serialize.d.ts +25 -0
  73. package/dist/filter/serialize.d.ts.map +1 -0
  74. package/dist/filter/serialize.js +68 -0
  75. package/dist/filter/serialize.js.map +1 -0
  76. package/dist/filter/serialize.test.d.ts +2 -0
  77. package/dist/filter/serialize.test.d.ts.map +1 -0
  78. package/dist/filter/serialize.test.js +83 -0
  79. package/dist/filter/serialize.test.js.map +1 -0
  80. package/dist/filter/types.d.ts +122 -0
  81. package/dist/filter/types.d.ts.map +1 -0
  82. package/dist/filter/types.js +24 -0
  83. package/dist/filter/types.js.map +1 -0
  84. package/dist/hooks/index.d.ts +30 -0
  85. package/dist/hooks/index.d.ts.map +1 -1
  86. package/dist/hooks/index.js +79 -59
  87. package/dist/hooks/index.js.map +1 -1
  88. package/dist/index.d.ts +6 -1
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/index.js +19 -0
  91. package/dist/index.js.map +1 -1
  92. package/dist/internal.d.ts +1 -0
  93. package/dist/internal.d.ts.map +1 -1
  94. package/dist/internal.js +4 -0
  95. package/dist/internal.js.map +1 -1
  96. package/package.json +1 -1
  97. package/src/access/access-filter.test.ts +79 -0
  98. package/src/access/access-filter.ts +15 -4
  99. package/src/access/field-access.ts +6 -6
  100. package/src/access/multi-column-read-write.test.ts +68 -19
  101. package/src/access/relationship-count.test.ts +269 -0
  102. package/src/access/relationship-count.ts +311 -0
  103. package/src/access/relationship-label-filter.test.ts +177 -0
  104. package/src/access/relationship-label-filter.ts +140 -0
  105. package/src/config/index.ts +6 -0
  106. package/src/config/nav-count.ts +85 -0
  107. package/src/config/types.ts +319 -1
  108. package/src/context/hook-pipeline.ts +19 -0
  109. package/src/context/index.ts +285 -3
  110. package/src/context/nested-operations.ts +22 -0
  111. package/src/extend.ts +9 -0
  112. package/src/fields/index.ts +168 -0
  113. package/src/filter/collect.ts +72 -0
  114. package/src/filter/filter.test.ts +400 -0
  115. package/src/filter/index.ts +25 -0
  116. package/src/filter/map.ts +68 -0
  117. package/src/filter/parse.ts +106 -0
  118. package/src/filter/serialize.test.ts +115 -0
  119. package/src/filter/serialize.ts +75 -0
  120. package/src/filter/types.ts +120 -0
  121. package/src/hooks/index.ts +87 -57
  122. package/src/index.ts +54 -0
  123. package/src/internal.ts +5 -0
  124. package/tests/context.test.ts +690 -0
  125. package/tests/multi-column-validation-ordering.test.ts +305 -0
  126. package/tests/nav-count.test.ts +189 -0
  127. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../../src/filter/parse.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { FilterToken } from './types.js';
2
+ /**
3
+ * Serialise {@link FilterToken}s back into a filter query string — the exact
4
+ * inverse of {@link parseFilterQuery} (ADR-0017). This is the grammar-producing
5
+ * half the Filter builder UI relies on: the builder turns UI state into tokens
6
+ * and serialises them into the `?search=` URL param the engine already consumes,
7
+ * so the same quoting and operator-prefix rules live next to the parser and can
8
+ * never drift.
9
+ *
10
+ * Pure and field-agnostic (no DB/Prisma imports). Tokens are joined with the
11
+ * grammar's implicit AND (a single space). A token with an empty value carries
12
+ * no filter and is omitted, so `serializeFilterQuery` never emits a dangling
13
+ * `field:` or a stray quote pair.
14
+ *
15
+ * @example
16
+ * serializeFilterQuery([
17
+ * { field: 'role', operator: 'eq', value: 'Editor', raw: '' },
18
+ * { field: 'orders', operator: 'gt', value: '5', raw: '' },
19
+ * { field: 'name', operator: 'eq', value: 'Ada Lovelace', raw: '' },
20
+ * { field: null, operator: 'eq', value: 'beta', raw: '' },
21
+ * ])
22
+ * // 'role:Editor orders:>5 name:"Ada Lovelace" beta'
23
+ */
24
+ export declare function serializeFilterQuery(tokens: FilterToken[]): string;
25
+ //# sourceMappingURL=serialize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../../src/filter/serialize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,WAAW,EAAE,MAAM,YAAY,CAAA;AAgC7D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAoBlE"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * The comparison-operator prefix each {@link FilterOperator} serialises to. The
3
+ * exact inverse of the prefixes {@link parseFilterQuery} recognises (`eq` has no
4
+ * prefix — a plain `field:value`).
5
+ */
6
+ const OPERATOR_PREFIX = {
7
+ eq: '',
8
+ gt: '>',
9
+ gte: '>=',
10
+ lt: '<',
11
+ lte: '<=',
12
+ };
13
+ /**
14
+ * Whether a value must be wrapped in double quotes to survive re-parsing.
15
+ *
16
+ * A value is quoted when it contains whitespace (so `parseFilterQuery` keeps it
17
+ * as one token) or an embedded quote. When `guardOperatorChars` is set (an `eq`
18
+ * field token, where a leading `>`/`<` would otherwise be read as a comparison
19
+ * operator), a value starting with `<`/`>` is also quoted so it round-trips as a
20
+ * literal value rather than an operator.
21
+ */
22
+ function quoteIfNeeded(value, guardOperatorChars) {
23
+ const needsQuote = /\s/.test(value) ||
24
+ value.includes('"') ||
25
+ (guardOperatorChars && (value.startsWith('>') || value.startsWith('<')));
26
+ return needsQuote ? `"${value}"` : value;
27
+ }
28
+ /**
29
+ * Serialise {@link FilterToken}s back into a filter query string — the exact
30
+ * inverse of {@link parseFilterQuery} (ADR-0017). This is the grammar-producing
31
+ * half the Filter builder UI relies on: the builder turns UI state into tokens
32
+ * and serialises them into the `?search=` URL param the engine already consumes,
33
+ * so the same quoting and operator-prefix rules live next to the parser and can
34
+ * never drift.
35
+ *
36
+ * Pure and field-agnostic (no DB/Prisma imports). Tokens are joined with the
37
+ * grammar's implicit AND (a single space). A token with an empty value carries
38
+ * no filter and is omitted, so `serializeFilterQuery` never emits a dangling
39
+ * `field:` or a stray quote pair.
40
+ *
41
+ * @example
42
+ * serializeFilterQuery([
43
+ * { field: 'role', operator: 'eq', value: 'Editor', raw: '' },
44
+ * { field: 'orders', operator: 'gt', value: '5', raw: '' },
45
+ * { field: 'name', operator: 'eq', value: 'Ada Lovelace', raw: '' },
46
+ * { field: null, operator: 'eq', value: 'beta', raw: '' },
47
+ * ])
48
+ * // 'role:Editor orders:>5 name:"Ada Lovelace" beta'
49
+ */
50
+ export function serializeFilterQuery(tokens) {
51
+ const parts = [];
52
+ for (const token of tokens) {
53
+ // An empty value is not a filter — skip it rather than emit `field:` / `""`.
54
+ if (token.value === '')
55
+ continue;
56
+ if (token.field === null) {
57
+ // Bare free-text word. A leading `>`/`<` stays literal for bare words
58
+ // (the parser only treats them as operators after a `field:`), so no
59
+ // operator guard is needed here.
60
+ parts.push(quoteIfNeeded(token.value, false));
61
+ continue;
62
+ }
63
+ const prefix = OPERATOR_PREFIX[token.operator];
64
+ parts.push(`${token.field}:${prefix}${quoteIfNeeded(token.value, prefix === '')}`);
65
+ }
66
+ return parts.join(' ');
67
+ }
68
+ //# sourceMappingURL=serialize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize.js","sourceRoot":"","sources":["../../src/filter/serialize.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,eAAe,GAAmC;IACtD,EAAE,EAAE,EAAE;IACN,EAAE,EAAE,GAAG;IACP,GAAG,EAAE,IAAI;IACT,EAAE,EAAE,GAAG;IACP,GAAG,EAAE,IAAI;CACV,CAAA;AAED;;;;;;;;GAQG;AACH,SAAS,aAAa,CAAC,KAAa,EAAE,kBAA2B;IAC/D,MAAM,UAAU,GACd,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;QAChB,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC;QACnB,CAAC,kBAAkB,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;IAC1E,OAAO,UAAU,CAAC,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,KAAK,CAAA;AAC1C,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAqB;IACxD,MAAM,KAAK,GAAa,EAAE,CAAA;IAE1B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,6EAA6E;QAC7E,IAAI,KAAK,CAAC,KAAK,KAAK,EAAE;YAAE,SAAQ;QAEhC,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;YACzB,sEAAsE;YACtE,qEAAqE;YACrE,iCAAiC;YACjC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAA;YAC7C,SAAQ;QACV,CAAC;QAED,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;QAC9C,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,IAAI,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,KAAK,EAAE,CAAC,EAAE,CAAC,CAAA;IACpF,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACxB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=serialize.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize.test.d.ts","sourceRoot":"","sources":["../../src/filter/serialize.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,83 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { parseFilterQuery } from './parse.js';
3
+ import { serializeFilterQuery } from './serialize.js';
4
+ // ─────────────────────────────────────────────────────────────
5
+ // serializeFilterQuery — the pure tokens → query-string boundary
6
+ // (ADR-0017). The exact inverse of parseFilterQuery; this is the
7
+ // grammar the Filter builder UI produces into the `?search=` param.
8
+ // ─────────────────────────────────────────────────────────────
9
+ const token = (partial) => ({
10
+ field: null,
11
+ operator: 'eq',
12
+ value: '',
13
+ raw: '',
14
+ ...partial,
15
+ });
16
+ describe('serializeFilterQuery', () => {
17
+ it('serialises a bare free-text word', () => {
18
+ expect(serializeFilterQuery([token({ field: null, value: 'beta' })])).toBe('beta');
19
+ });
20
+ it('serialises a field:value token with the default eq operator', () => {
21
+ expect(serializeFilterQuery([token({ field: 'role', value: 'Editor' })])).toBe('role:Editor');
22
+ });
23
+ it('serialises each comparison operator as its prefix', () => {
24
+ expect(serializeFilterQuery([token({ field: 'orders', operator: 'gt', value: '5' })])).toBe('orders:>5');
25
+ expect(serializeFilterQuery([token({ field: 'orders', operator: 'gte', value: '5' })])).toBe('orders:>=5');
26
+ expect(serializeFilterQuery([token({ field: 'orders', operator: 'lt', value: '5' })])).toBe('orders:<5');
27
+ expect(serializeFilterQuery([token({ field: 'orders', operator: 'lte', value: '5' })])).toBe('orders:<=5');
28
+ });
29
+ it('quotes a field value containing whitespace', () => {
30
+ expect(serializeFilterQuery([token({ field: 'name', value: 'Ada Lovelace' })])).toBe('name:"Ada Lovelace"');
31
+ });
32
+ it('quotes a bare word containing whitespace', () => {
33
+ expect(serializeFilterQuery([token({ field: null, value: 'multi word' })])).toBe('"multi word"');
34
+ });
35
+ it('quotes an eq value that would otherwise read as a comparison operator', () => {
36
+ // Without the guard, `field:>5` would re-parse as gt, not eq of ">5".
37
+ expect(serializeFilterQuery([token({ field: 'note', operator: 'eq', value: '>5' })])).toBe('note:">5"');
38
+ });
39
+ it('does NOT quote a leading > on a bare word (parser keeps it literal)', () => {
40
+ expect(serializeFilterQuery([token({ field: null, value: '>5' })])).toBe('>5');
41
+ });
42
+ it('joins tokens with the grammar implicit AND (a single space)', () => {
43
+ expect(serializeFilterQuery([
44
+ token({ field: 'role', value: 'Editor' }),
45
+ token({ field: 'orders', operator: 'gt', value: '5' }),
46
+ token({ field: null, value: 'beta' }),
47
+ ])).toBe('role:Editor orders:>5 beta');
48
+ });
49
+ it('omits tokens with an empty value (no dangling field: or stray quotes)', () => {
50
+ expect(serializeFilterQuery([
51
+ token({ field: 'role', value: '' }),
52
+ token({ field: null, value: '' }),
53
+ token({ field: 'name', value: 'Ada' }),
54
+ ])).toBe('name:Ada');
55
+ });
56
+ });
57
+ // ─────────────────────────────────────────────────────────────
58
+ // Round-trip: parse ∘ serialize is stable, and serialize ∘ parse
59
+ // reproduces the meaning the engine assigns.
60
+ // ─────────────────────────────────────────────────────────────
61
+ describe('parse/serialize round-trip', () => {
62
+ const queries = [
63
+ 'beta',
64
+ 'role:Editor',
65
+ 'orders:>5',
66
+ 'orders:>=5',
67
+ 'orders:<10',
68
+ 'name:"Ada Lovelace"',
69
+ 'role:Editor orders:>5 name:"Ada Lovelace" beta',
70
+ '"multi word" gamma',
71
+ ];
72
+ it.each(queries)('serialize(parse(%s)) reproduces the query', (query) => {
73
+ expect(serializeFilterQuery(parseFilterQuery(query))).toBe(query);
74
+ });
75
+ it.each(queries)('parse is idempotent through a serialize round-trip: %s', (query) => {
76
+ const once = parseFilterQuery(query);
77
+ const twice = parseFilterQuery(serializeFilterQuery(once));
78
+ // Compare on the semantic fields (raw differs after re-serialisation).
79
+ const strip = (tokens) => tokens.map(({ field, operator, value }) => ({ field, operator, value }));
80
+ expect(strip(twice)).toEqual(strip(once));
81
+ });
82
+ });
83
+ //# sourceMappingURL=serialize.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize.test.js","sourceRoot":"","sources":["../../src/filter/serialize.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Filter engine types (ADR-0017).
3
+ *
4
+ * The admin UI's Filter builder turns a URL query string into scoped,
5
+ * server-executed filtering. The grammar is an AND-only URL contract:
6
+ * implicit-AND tokens, quoted multi-word values, comparison operators on
7
+ * numeric/date fields, and bare words as free text. Fields participate by
8
+ * declaring a {@link FilterSpec} (a self-contained field-builder method, peer
9
+ * of `getPrismaType` et al.); a field without one is not filterable.
10
+ *
11
+ * These types are the pure boundary — no Prisma / DB imports — so the
12
+ * `parse → tokens` and `tokens + specs → conditions` seam is unit-testable.
13
+ */
14
+ /**
15
+ * A Filter operator. `eq` is the default operator for a plain `field:value`
16
+ * token (each field's spec decides what equality means — `contains` for text,
17
+ * `equals` for a select); the comparisons are produced by the `>`/`>=`/`<`/`<=`
18
+ * prefixes on numeric and date tokens.
19
+ */
20
+ export type FilterOperator = 'eq' | 'gt' | 'gte' | 'lt' | 'lte';
21
+ /**
22
+ * One parsed unit of a filter query — a field, an operator, and a value.
23
+ *
24
+ * A bare free-text word parses to a token with `field: null`. `raw` is the
25
+ * original source substring, retained so a token that can't map to a condition
26
+ * degrades to free text without re-tokenising.
27
+ */
28
+ export interface FilterToken {
29
+ /** Field key the token targets, or `null` for a bare free-text word. */
30
+ field: string | null;
31
+ /** The operator; `eq` for a plain `field:value`, comparisons for prefixed values. */
32
+ operator: FilterOperator;
33
+ /** The unquoted value. */
34
+ value: string;
35
+ /** The original source substring for this token (used when degrading to free text). */
36
+ raw: string;
37
+ }
38
+ /**
39
+ * A Prisma `where` fragment produced by a Filter spec's `toCondition` mapper.
40
+ * Kept as an opaque record so this module carries no Prisma type dependency;
41
+ * the secured context ANDs it with the access filter when the fragment reaches
42
+ * `context.db.*`.
43
+ */
44
+ export type FilterCondition = Record<string, unknown>;
45
+ /**
46
+ * Serializable description of what the suggestion dropdown may offer for a
47
+ * field's values. Structure only — never data-derived values for unbounded
48
+ * fields (ADR-0017): `none` for text/number/date, enumerated `options` for
49
+ * select/checkbox, and `relationship` for label search via the existing
50
+ * access-controlled lookup.
51
+ */
52
+ export type FilterValueSource = {
53
+ kind: 'none';
54
+ } | {
55
+ kind: 'enum';
56
+ options: Array<{
57
+ value: string;
58
+ label: string;
59
+ }>;
60
+ } | {
61
+ kind: 'relationship';
62
+ listKey: string;
63
+ many: boolean;
64
+ };
65
+ /**
66
+ * Marker key a to-many relationship's Filter spec emits for a count comparison
67
+ * (`orders:>5`). Prisma cannot express a relation-count comparison in a `where`
68
+ * (there is no `{ orders: { _count: { gt: 5 } } }`), so the pure spec can only
69
+ * emit a structured marker; `resolveRelationshipCountFilters` later turns each
70
+ * marker into an access-scoped `{ id: { in | notIn } }` before the query runs.
71
+ * Kept here (the pure boundary) so the field builder and the resolver agree on
72
+ * the shape without depending on each other.
73
+ */
74
+ export declare const RELATIONSHIP_COUNT_FILTER_KEY: '_countFilter';
75
+ /**
76
+ * The payload a {@link RELATIONSHIP_COUNT_FILTER_KEY} marker carries: the
77
+ * numeric comparison to apply to a to-many relationship's access-visible count.
78
+ */
79
+ export interface RelationshipCountFilterMarker {
80
+ operator: FilterOperator;
81
+ value: number;
82
+ }
83
+ /**
84
+ * A field's self-declared filtering capability. Returned by the optional
85
+ * `getFilterSpec` field-builder method.
86
+ */
87
+ export interface FilterSpec {
88
+ /** Operators this field supports. A token using any other operator degrades to free text. */
89
+ operators: FilterOperator[];
90
+ /**
91
+ * Whether this field participates in bare-word free-text search. Bare words
92
+ * are OR-matched across every free-text field via each field's `eq` mapping.
93
+ */
94
+ freeText?: boolean;
95
+ /**
96
+ * Pure map from a supported `(operator, value)` to a Prisma `where` fragment,
97
+ * or `null` when the value can't be interpreted (e.g. a non-numeric value on
98
+ * a numeric field, or an unknown select option) — in which case the token
99
+ * degrades to free text. MUST stay free of DB/Prisma imports.
100
+ */
101
+ toCondition: (operator: FilterOperator, value: string) => FilterCondition | null;
102
+ /** Serializable suggestion metadata for the client. */
103
+ suggestions: {
104
+ valueSource: FilterValueSource;
105
+ };
106
+ }
107
+ /**
108
+ * Collected, client-serializable suggestion metadata for one filterable field.
109
+ * The union of every list field's Filter spec suggestions; carries no
110
+ * functions, so it crosses the server/client boundary.
111
+ */
112
+ export interface FilterFieldSuggestion {
113
+ /** The field key. */
114
+ field: string;
115
+ /** Operators the field supports. */
116
+ operators: FilterOperator[];
117
+ /** Whether the field participates in bare-word free-text search. */
118
+ freeText: boolean;
119
+ /** How the suggestion dropdown may offer values for this field. */
120
+ valueSource: FilterValueSource;
121
+ }
122
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/filter/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,CAAA;AAE/D;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,wEAAwE;IACxE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,qFAAqF;IACrF,QAAQ,EAAE,cAAc,CAAA;IACxB,0BAA0B;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,uFAAuF;IACvF,GAAG,EAAE,MAAM,CAAA;CACZ;AAED;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAErD;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GACzB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,GAClE;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAA;AAE5D;;;;;;;;GAQG;AACH,eAAO,MAAM,6BAA6B,EAAG,cAAuB,CAAA;AAEpE;;;GAGG;AACH,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,cAAc,CAAA;IACxB,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,6FAA6F;IAC7F,SAAS,EAAE,cAAc,EAAE,CAAA;IAC3B;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;OAKG;IACH,WAAW,EAAE,CAAC,QAAQ,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,KAAK,eAAe,GAAG,IAAI,CAAA;IAChF,uDAAuD;IACvD,WAAW,EAAE;QACX,WAAW,EAAE,iBAAiB,CAAA;KAC/B,CAAA;CACF;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAA;IACb,oCAAoC;IACpC,SAAS,EAAE,cAAc,EAAE,CAAA;IAC3B,oEAAoE;IACpE,QAAQ,EAAE,OAAO,CAAA;IACjB,mEAAmE;IACnE,WAAW,EAAE,iBAAiB,CAAA;CAC/B"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Filter engine types (ADR-0017).
3
+ *
4
+ * The admin UI's Filter builder turns a URL query string into scoped,
5
+ * server-executed filtering. The grammar is an AND-only URL contract:
6
+ * implicit-AND tokens, quoted multi-word values, comparison operators on
7
+ * numeric/date fields, and bare words as free text. Fields participate by
8
+ * declaring a {@link FilterSpec} (a self-contained field-builder method, peer
9
+ * of `getPrismaType` et al.); a field without one is not filterable.
10
+ *
11
+ * These types are the pure boundary — no Prisma / DB imports — so the
12
+ * `parse → tokens` and `tokens + specs → conditions` seam is unit-testable.
13
+ */
14
+ /**
15
+ * Marker key a to-many relationship's Filter spec emits for a count comparison
16
+ * (`orders:>5`). Prisma cannot express a relation-count comparison in a `where`
17
+ * (there is no `{ orders: { _count: { gt: 5 } } }`), so the pure spec can only
18
+ * emit a structured marker; `resolveRelationshipCountFilters` later turns each
19
+ * marker into an access-scoped `{ id: { in | notIn } }` before the query runs.
20
+ * Kept here (the pure boundary) so the field builder and the resolver agree on
21
+ * the shape without depending on each other.
22
+ */
23
+ export const RELATIONSHIP_COUNT_FILTER_KEY = '_countFilter';
24
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/filter/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAgDH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,cAAuB,CAAA"}
@@ -232,8 +232,38 @@ export declare function executeFieldAfterTransactionHooks(outcome: TransactionOu
232
232
  /**
233
233
  * Execute field-level resolveInput hooks
234
234
  * Allows fields to transform their input values before database write
235
+ *
236
+ * NOTE (#789): multi-column fields (e.g. storage image()/file() in
237
+ * Keystone-parity mode) are NOT split here. This phase only resolves each
238
+ * field's value under its LOGICAL key, so that phases 2-3 (list/field
239
+ * `validate` → `validateFieldRules`) run against the same shape a
240
+ * single-column field would present — including an unrecognised/invalid value
241
+ * a field's `resolveInput` chose to pass through for validation to catch. The
242
+ * split into physical columns happens strictly AFTER validation passes; see
243
+ * {@link splitMultiColumnFields}.
235
244
  */
236
245
  export declare function executeFieldResolveInputHooks(inputData: Record<string, any>, resolvedData: Record<string, any>, fields: Record<string, FieldConfig>, operation: 'create' | 'update', context: AccessContext, listKey: string, item?: any): Promise<Record<string, unknown>>;
246
+ /**
247
+ * Split multi-column fields' resolved LOGICAL values into their physical
248
+ * per-part columns (e.g. storage image()/file() in Keystone-parity mode — see
249
+ * ADR-0006).
250
+ *
251
+ * Runs AFTER `validateFieldRules` has passed (#789): a multi-column field's
252
+ * `getZodSchema` gets a genuine chance to reject an unrecognised/invalid
253
+ * logical value BEFORE it is split into `null`/`undefined` physical columns
254
+ * and silently written. Previously this split ran inline inside
255
+ * `executeFieldResolveInputHooks` (Phase 1.5, BEFORE validation), which let an
256
+ * unrecognised value bypass validation entirely.
257
+ *
258
+ * Preserves the field-level write-access gate exactly as before: the raw
259
+ * per-part column keys are not declared in `fieldConfigs`, so
260
+ * `filterWritableFields`'s undeclared-key reject cannot enforce this field's
261
+ * own write access — enforce it HERE, using the canonical field-access
262
+ * evaluator with the same arguments the write pipeline uses. A denied field
263
+ * drops its logical key and contributes NONE of its per-part columns (sudo
264
+ * bypasses via `checkFieldAccess`).
265
+ */
266
+ export declare function splitMultiColumnFields(inputData: Record<string, any>, resolvedData: Record<string, any>, fields: Record<string, FieldConfig>, operation: 'create' | 'update', context: AccessContext, item?: any): Promise<Record<string, unknown>>;
237
267
  /**
238
268
  * Execute field-level validate hooks
239
269
  * Allows fields to perform custom validation after resolveInput but before database write
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"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"}
@@ -241,6 +241,15 @@ export async function executeFieldAfterTransactionHooks(outcome, inputData, fiel
241
241
  /**
242
242
  * Execute field-level resolveInput hooks
243
243
  * Allows fields to transform their input values before database write
244
+ *
245
+ * NOTE (#789): multi-column fields (e.g. storage image()/file() in
246
+ * Keystone-parity mode) are NOT split here. This phase only resolves each
247
+ * field's value under its LOGICAL key, so that phases 2-3 (list/field
248
+ * `validate` → `validateFieldRules`) run against the same shape a
249
+ * single-column field would present — including an unrecognised/invalid value
250
+ * a field's `resolveInput` chose to pass through for validation to catch. The
251
+ * split into physical columns happens strictly AFTER validation passes; see
252
+ * {@link splitMultiColumnFields}.
244
253
  */
245
254
  export async function executeFieldResolveInputHooks(
246
255
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -251,69 +260,80 @@ resolvedData, fields, operation, context, listKey,
251
260
  item) {
252
261
  let result = { ...resolvedData };
253
262
  for (const [fieldKey, fieldConfig] of Object.entries(fields)) {
254
- // Skip if field not in data
263
+ // Skip if field not in data, or if there's nothing to resolve
255
264
  if (!(fieldKey in result))
256
265
  continue;
257
- // A field's resolveInput produces its resolved value; for most fields that
258
- // value is stored back under the same key. Multi-column fields additionally
259
- // split that value across their physical columns below.
260
- let resolvedValue = result[fieldKey];
261
- if (fieldConfig.hooks?.resolveInput) {
262
- // Execute field hook
263
- // Type assertion is safe here because hooks are typed correctly in field definitions
264
- // and we're working with runtime values that match those types
265
- resolvedValue = await fieldConfig.hooks.resolveInput({
266
- listKey,
267
- fieldKey,
268
- operation,
269
- inputData,
270
- item,
271
- resolvedData: { ...result }, // Pass a copy to avoid mutation affecting recorded args
272
- context,
273
- });
274
- }
275
- else if (!fieldConfig.splitColumns) {
276
- // No resolveInput and not a multi-column field — nothing to do.
266
+ if (!fieldConfig.hooks?.resolveInput)
267
+ continue;
268
+ // Execute field hook
269
+ // Type assertion is safe here because hooks are typed correctly in field definitions
270
+ // and we're working with runtime values that match those types
271
+ const resolvedValue = await fieldConfig.hooks.resolveInput({
272
+ listKey,
273
+ fieldKey,
274
+ operation,
275
+ inputData,
276
+ item,
277
+ resolvedData: { ...result }, // Pass a copy to avoid mutation affecting recorded args
278
+ context,
279
+ });
280
+ // Create new object with updated field to avoid mutating the passed reference
281
+ result = { ...result, [fieldKey]: resolvedValue };
282
+ }
283
+ return result;
284
+ }
285
+ /**
286
+ * Split multi-column fields' resolved LOGICAL values into their physical
287
+ * per-part columns (e.g. storage image()/file() in Keystone-parity mode — see
288
+ * ADR-0006).
289
+ *
290
+ * Runs AFTER `validateFieldRules` has passed (#789): a multi-column field's
291
+ * `getZodSchema` gets a genuine chance to reject an unrecognised/invalid
292
+ * logical value BEFORE it is split into `null`/`undefined` physical columns
293
+ * and silently written. Previously this split ran inline inside
294
+ * `executeFieldResolveInputHooks` (Phase 1.5, BEFORE validation), which let an
295
+ * unrecognised value bypass validation entirely.
296
+ *
297
+ * Preserves the field-level write-access gate exactly as before: the raw
298
+ * per-part column keys are not declared in `fieldConfigs`, so
299
+ * `filterWritableFields`'s undeclared-key reject cannot enforce this field's
300
+ * own write access — enforce it HERE, using the canonical field-access
301
+ * evaluator with the same arguments the write pipeline uses. A denied field
302
+ * drops its logical key and contributes NONE of its per-part columns (sudo
303
+ * bypasses via `checkFieldAccess`).
304
+ */
305
+ export async function splitMultiColumnFields(
306
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
307
+ inputData,
308
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
309
+ resolvedData, fields, operation, context,
310
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
311
+ item) {
312
+ let result = { ...resolvedData };
313
+ for (const [fieldKey, fieldConfig] of Object.entries(fields)) {
314
+ if (!fieldConfig.splitColumns)
315
+ continue;
316
+ if (!(fieldKey in result))
317
+ continue;
318
+ const resolvedValue = result[fieldKey];
319
+ const canWrite = await checkFieldAccess(fieldConfig.access, operation, {
320
+ session: context.session,
321
+ item,
322
+ context,
323
+ inputData,
324
+ });
325
+ // Drop the logical key (it is not a real column) regardless of outcome —
326
+ // a denied field must not leave its logical key behind either.
327
+ const next = { ...result };
328
+ delete next[fieldKey];
329
+ result = next;
330
+ if (!canWrite) {
331
+ // Denied: write none of its per-part columns — exactly as
332
+ // filterWritableFields drops a denied single-column field.
277
333
  continue;
278
334
  }
279
- if (fieldConfig.splitColumns) {
280
- // Multi-column field (e.g. storage image()/file() in Keystone-parity
281
- // mode): replace the single logical key with its per-part columns so the
282
- // write payload targets the live columns instead of a single one.
283
- //
284
- // The split removes the logical key from the payload BEFORE the
285
- // canonical writable-field filter (`filterWritableFields`) runs, and the
286
- // raw per-part column keys are not in `fieldConfigs` — so that later
287
- // filter cannot enforce this field's own write access. Enforce it HERE,
288
- // using the canonical field-access evaluator with the SAME arguments the
289
- // write pipeline uses. A single-column field denied by `update`/`create`
290
- // is simply omitted from the write; a denied multi-column field must
291
- // likewise contribute NONE of its per-part columns. (sudo bypasses via
292
- // `checkFieldAccess`.)
293
- const canWrite = await checkFieldAccess(fieldConfig.access, operation, {
294
- session: context.session,
295
- item,
296
- context,
297
- inputData,
298
- });
299
- if (!canWrite) {
300
- // Denied: drop the logical key and write none of its columns — exactly
301
- // as filterWritableFields drops a denied single-column field.
302
- const next = { ...result };
303
- delete next[fieldKey];
304
- result = next;
305
- continue;
306
- }
307
- const columns = fieldConfig.splitColumns(fieldKey, resolvedValue);
308
- // Drop the logical key (it is not a real column) and merge the columns.
309
- const next = { ...result, ...columns };
310
- delete next[fieldKey];
311
- result = next;
312
- }
313
- else {
314
- // Create new object with updated field to avoid mutating the passed reference
315
- result = { ...result, [fieldKey]: resolvedValue };
316
- }
335
+ const columns = fieldConfig.splitColumns(fieldKey, resolvedValue);
336
+ result = { ...result, ...columns };
317
337
  }
318
338
  return result;
319
339
  }