@gettersethya/mira-collection 0.1.0-alpha.2

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 (111) hide show
  1. package/LICENSE +21 -0
  2. package/dist/src/collection/auth.d.ts +79 -0
  3. package/dist/src/collection/auth.d.ts.map +1 -0
  4. package/dist/src/collection/auth.js +50 -0
  5. package/dist/src/collection/auth.js.map +1 -0
  6. package/dist/src/collection/base.d.ts +64 -0
  7. package/dist/src/collection/base.d.ts.map +1 -0
  8. package/dist/src/collection/base.js +40 -0
  9. package/dist/src/collection/base.js.map +1 -0
  10. package/dist/src/collection/bytes.d.ts +51 -0
  11. package/dist/src/collection/bytes.d.ts.map +1 -0
  12. package/dist/src/collection/bytes.js +51 -0
  13. package/dist/src/collection/bytes.js.map +1 -0
  14. package/dist/src/collection/errors.d.ts +29 -0
  15. package/dist/src/collection/errors.d.ts.map +1 -0
  16. package/dist/src/collection/errors.js +23 -0
  17. package/dist/src/collection/errors.js.map +1 -0
  18. package/dist/src/collection/field.d.ts +300 -0
  19. package/dist/src/collection/field.d.ts.map +1 -0
  20. package/dist/src/collection/field.js +175 -0
  21. package/dist/src/collection/field.js.map +1 -0
  22. package/dist/src/collection/index-builder.d.ts +59 -0
  23. package/dist/src/collection/index-builder.d.ts.map +1 -0
  24. package/dist/src/collection/index-builder.js +43 -0
  25. package/dist/src/collection/index-builder.js.map +1 -0
  26. package/dist/src/collection/index.d.ts +22 -0
  27. package/dist/src/collection/index.d.ts.map +1 -0
  28. package/dist/src/collection/index.js +21 -0
  29. package/dist/src/collection/index.js.map +1 -0
  30. package/dist/src/collection/serialize.d.ts +24 -0
  31. package/dist/src/collection/serialize.d.ts.map +1 -0
  32. package/dist/src/collection/serialize.js +127 -0
  33. package/dist/src/collection/serialize.js.map +1 -0
  34. package/dist/src/collection/types.d.ts +119 -0
  35. package/dist/src/collection/types.d.ts.map +1 -0
  36. package/dist/src/collection/types.js +2 -0
  37. package/dist/src/collection/types.js.map +1 -0
  38. package/dist/src/collection/view.d.ts +57 -0
  39. package/dist/src/collection/view.d.ts.map +1 -0
  40. package/dist/src/collection/view.js +49 -0
  41. package/dist/src/collection/view.js.map +1 -0
  42. package/dist/src/filter/builder.d.ts +127 -0
  43. package/dist/src/filter/builder.d.ts.map +1 -0
  44. package/dist/src/filter/builder.js +72 -0
  45. package/dist/src/filter/builder.js.map +1 -0
  46. package/dist/src/filter/compiler.d.ts +48 -0
  47. package/dist/src/filter/compiler.d.ts.map +1 -0
  48. package/dist/src/filter/compiler.js +132 -0
  49. package/dist/src/filter/compiler.js.map +1 -0
  50. package/dist/src/filter/index.d.ts +6 -0
  51. package/dist/src/filter/index.d.ts.map +1 -0
  52. package/dist/src/filter/index.js +4 -0
  53. package/dist/src/filter/index.js.map +1 -0
  54. package/dist/src/filter/types.d.ts +96 -0
  55. package/dist/src/filter/types.d.ts.map +1 -0
  56. package/dist/src/filter/types.js +44 -0
  57. package/dist/src/filter/types.js.map +1 -0
  58. package/dist/src/index.d.ts +5 -0
  59. package/dist/src/index.d.ts.map +1 -0
  60. package/dist/src/index.js +4 -0
  61. package/dist/src/index.js.map +1 -0
  62. package/dist/src/rule/builder.d.ts +97 -0
  63. package/dist/src/rule/builder.d.ts.map +1 -0
  64. package/dist/src/rule/builder.js +126 -0
  65. package/dist/src/rule/builder.js.map +1 -0
  66. package/dist/src/rule/index.d.ts +3 -0
  67. package/dist/src/rule/index.d.ts.map +1 -0
  68. package/dist/src/rule/index.js +3 -0
  69. package/dist/src/rule/index.js.map +1 -0
  70. package/dist/src/rule/types.d.ts +129 -0
  71. package/dist/src/rule/types.d.ts.map +1 -0
  72. package/dist/src/rule/types.js +2 -0
  73. package/dist/src/rule/types.js.map +1 -0
  74. package/dist/tests/collection/auth.test.d.ts +2 -0
  75. package/dist/tests/collection/auth.test.d.ts.map +1 -0
  76. package/dist/tests/collection/auth.test.js +44 -0
  77. package/dist/tests/collection/auth.test.js.map +1 -0
  78. package/dist/tests/collection/base.test.d.ts +2 -0
  79. package/dist/tests/collection/base.test.d.ts.map +1 -0
  80. package/dist/tests/collection/base.test.js +70 -0
  81. package/dist/tests/collection/base.test.js.map +1 -0
  82. package/dist/tests/collection/bytes.test.d.ts +2 -0
  83. package/dist/tests/collection/bytes.test.d.ts.map +1 -0
  84. package/dist/tests/collection/bytes.test.js +9 -0
  85. package/dist/tests/collection/bytes.test.js.map +1 -0
  86. package/dist/tests/collection/field.test.d.ts +2 -0
  87. package/dist/tests/collection/field.test.d.ts.map +1 -0
  88. package/dist/tests/collection/field.test.js +140 -0
  89. package/dist/tests/collection/field.test.js.map +1 -0
  90. package/dist/tests/collection/index-type.test.d.ts +2 -0
  91. package/dist/tests/collection/index-type.test.d.ts.map +1 -0
  92. package/dist/tests/collection/index-type.test.js +28 -0
  93. package/dist/tests/collection/index-type.test.js.map +1 -0
  94. package/dist/tests/collection/serialize.test.d.ts +2 -0
  95. package/dist/tests/collection/serialize.test.d.ts.map +1 -0
  96. package/dist/tests/collection/serialize.test.js +165 -0
  97. package/dist/tests/collection/serialize.test.js.map +1 -0
  98. package/dist/tests/collection/view.test.d.ts +2 -0
  99. package/dist/tests/collection/view.test.d.ts.map +1 -0
  100. package/dist/tests/collection/view.test.js +66 -0
  101. package/dist/tests/collection/view.test.js.map +1 -0
  102. package/dist/tests/filter/compiler.test.d.ts +2 -0
  103. package/dist/tests/filter/compiler.test.d.ts.map +1 -0
  104. package/dist/tests/filter/compiler.test.js +156 -0
  105. package/dist/tests/filter/compiler.test.js.map +1 -0
  106. package/dist/tests/rule/builder.test.d.ts +2 -0
  107. package/dist/tests/rule/builder.test.d.ts.map +1 -0
  108. package/dist/tests/rule/builder.test.js +290 -0
  109. package/dist/tests/rule/builder.test.js.map +1 -0
  110. package/dist/tsconfig.tsbuildinfo +1 -0
  111. package/package.json +36 -0
@@ -0,0 +1,132 @@
1
+ import { Effect } from "effect";
2
+ import { ValidationError } from "../collection/errors.js";
3
+ /**
4
+ * Compile a `FilterNode` AST into a `WhereClause` (`{ sql, params }`) suitable
5
+ * for splicing into an Effect SQL template literal via `unsafeFragment`.
6
+ *
7
+ * Validates each referenced field against the collection schema. Unknown fields
8
+ * produce a `ValidationError` in the Effect error channel.
9
+ *
10
+ * Field names are SQL-identifier-quoted (double-quoted as `t."fieldName"`).
11
+ * Values use `?`-style positional placeholders with params in the returned array.
12
+ *
13
+ * Edge cases:
14
+ * - `IN ()` with an empty array produces `1 = 0` (always false) instead of
15
+ * invalid SQL like `IN ()`.
16
+ * - The `like` pattern is passed through verbatim — the caller is responsible
17
+ * for adding `%` wildcards.
18
+ * - All column references use the hardcoded table alias `t`.
19
+ *
20
+ * @param node - The filter AST to compile
21
+ * @param schema - The collection schema (used for field validation)
22
+ * @param collectionName - Collection name (used only in error messages)
23
+ * @returns Effect that resolves to a WhereClause, or fails with ValidationError
24
+ * if any field name in the filter does not exist in the schema
25
+ *
26
+ * @example
27
+ * import { Filter, filterNodeToWhereClause } from "@gettersethya/mira-collection"
28
+ * import type { CollectionSchema } from "@gettersethya/mira-collection"
29
+ *
30
+ * const schema: CollectionSchema = {
31
+ * type: "object",
32
+ * properties: { title: { type: "string" } }
33
+ * } as CollectionSchema
34
+ *
35
+ * const effect = filterNodeToWhereClause(
36
+ * Filter.field("title").eq("hello"),
37
+ * schema,
38
+ * "posts"
39
+ * )
40
+ * // Resolves to: { sql: 't."title" = ?', params: ["hello"] }
41
+ *
42
+ * @see FilterNode — the AST type consumed by this function
43
+ * @see WhereClause — the output type
44
+ */
45
+ export function filterNodeToWhereClause(node, schema, collectionName) {
46
+ return compileNode(node, schema, collectionName);
47
+ }
48
+ function compileNode(node, schema, collection) {
49
+ switch (node.op) {
50
+ case "eq":
51
+ case "neq":
52
+ case "gt":
53
+ case "gte":
54
+ case "lt":
55
+ case "lte":
56
+ return compileComparison(node, collection, schema);
57
+ case "in":
58
+ return compileIn(node, collection, schema);
59
+ case "like":
60
+ return compileLike(node, collection, schema);
61
+ case "null":
62
+ case "not_null":
63
+ return compileNull(node, collection, schema);
64
+ case "and":
65
+ case "or":
66
+ return compileLogical(node, schema, collection);
67
+ case "not":
68
+ return compileNot(node, schema, collection);
69
+ }
70
+ }
71
+ function validateField(field, collection, schema) {
72
+ if (!schema.properties[field]) {
73
+ return Effect.fail(new ValidationError({
74
+ collection,
75
+ issues: [`Unknown field in filter: "${field}"`]
76
+ }));
77
+ }
78
+ return Effect.void;
79
+ }
80
+ function compileComparison(node, collection, schema) {
81
+ const opMap = {
82
+ eq: "=",
83
+ neq: "!=",
84
+ gt: ">",
85
+ gte: ">=",
86
+ lt: "<",
87
+ lte: "<="
88
+ };
89
+ return Effect.gen(function* () {
90
+ yield* validateField(node.field, collection, schema);
91
+ return { sql: `t."${node.field}" ${opMap[node.op]} ?`, params: [node.value] };
92
+ });
93
+ }
94
+ function compileIn(node, collection, schema) {
95
+ return Effect.gen(function* () {
96
+ yield* validateField(node.field, collection, schema);
97
+ if (node.values.length === 0)
98
+ return { sql: "1 = 0", params: [] };
99
+ const placeholders = node.values.map(() => "?").join(", ");
100
+ return { sql: `t."${node.field}" IN (${placeholders})`, params: [...node.values] };
101
+ });
102
+ }
103
+ function compileLike(node, collection, schema) {
104
+ return Effect.gen(function* () {
105
+ yield* validateField(node.field, collection, schema);
106
+ return { sql: `t."${node.field}" LIKE ?`, params: [node.value] };
107
+ });
108
+ }
109
+ function compileNull(node, collection, schema) {
110
+ return Effect.gen(function* () {
111
+ yield* validateField(node.field, collection, schema);
112
+ if (node.op === "null") {
113
+ return { sql: `t."${node.field}" IS NULL`, params: [] };
114
+ }
115
+ return { sql: `t."${node.field}" IS NOT NULL`, params: [] };
116
+ });
117
+ }
118
+ function compileLogical(node, schema, collection) {
119
+ return Effect.gen(function* () {
120
+ const left = yield* compileNode(node.left, schema, collection);
121
+ const right = yield* compileNode(node.right, schema, collection);
122
+ const op = node.op.toUpperCase();
123
+ return { sql: `(${left.sql}) ${op} (${right.sql})`, params: [...left.params, ...right.params] };
124
+ });
125
+ }
126
+ function compileNot(node, schema, collection) {
127
+ return Effect.gen(function* () {
128
+ const inner = yield* compileNode(node.node, schema, collection);
129
+ return { sql: `NOT (${inner.sql})`, params: inner.params };
130
+ });
131
+ }
132
+ //# sourceMappingURL=compiler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compiler.js","sourceRoot":"","sources":["../../../src/filter/compiler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export { Filter } from "./builder.js";
2
+ export { filterNodeToWhereClause } from "./compiler.js";
3
+ export { FilterNodeSchema } from "./types.js";
4
+ export type { FilterBuilder, FieldFilterOperand } from "./builder.js";
5
+ export type { FilterNode, WhereClause } from "./types.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/filter/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAC7C,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AACrE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA"}
@@ -0,0 +1,4 @@
1
+ export { Filter } from "./builder.js";
2
+ export { filterNodeToWhereClause } from "./compiler.js";
3
+ export { FilterNodeSchema } from "./types.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/filter/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA"}
@@ -0,0 +1,96 @@
1
+ import { Schema } from "effect";
2
+ /**
3
+ * A pre-compiled SQL WHERE fragment with positional `?` placeholders.
4
+ *
5
+ * Produced by `filterNodeToWhereClause()` and consumed by the repository layer.
6
+ * The `sql` string uses `?`-style placeholders; `params` supplies the values
7
+ * in positional order. Pass to `unsafeFragment(w.sql, w.params)` from
8
+ * `@effect/sql/Statement` to splice into a template literal.
9
+ *
10
+ * @example
11
+ * // Output of compiler for Filter.field("title").eq("hello"):
12
+ * const w: WhereClause = { sql: 't."title" = ?', params: ["hello"] }
13
+ *
14
+ * @see FilterNode — the AST type that compiles to WhereClause
15
+ * @see filterNodeToWhereClause — produces WhereClause from FilterNode
16
+ */
17
+ export type WhereClause = {
18
+ sql: string;
19
+ params: ReadonlyArray<unknown>;
20
+ };
21
+ /**
22
+ * AST node for filter expressions. Discriminated union with 6 variant groups:
23
+ *
24
+ * | Variants | Fields | Description |
25
+ * |---|---|---|
26
+ * | `eq`, `neq`, `gt`, `gte`, `lt`, `lte` | `field`, `value` | Value comparisons |
27
+ * | `in` | `field`, `values` | Membership test |
28
+ * | `like` | `field`, `value` | SQL LIKE pattern match |
29
+ * | `null`, `not_null` | `field` | Null checks (no value operand) |
30
+ * | `and`, `or` | `left`, `right` | Binary logical combinators |
31
+ * | `not` | `node` | Unary negation |
32
+ *
33
+ * Construct via `Filter.field(name).eq(value)` or `Filter.and(left, right)`.
34
+ * Consumed by `filterNodeToWhereClause()` to produce SQL WHERE clauses.
35
+ *
36
+ * @example
37
+ * import { Filter } from "@gettersethya/mira-collection"
38
+ *
39
+ * // Single comparison
40
+ * const node: FilterNode = Filter.field("title").eq("hello")
41
+ *
42
+ * // Logical combination
43
+ * const complex: FilterNode = Filter.and(
44
+ * Filter.field("published").eq(true),
45
+ * Filter.field("views").gte(100)
46
+ * )
47
+ *
48
+ * // Null check
49
+ * const deleted: FilterNode = Filter.field("deletedAt").notNull()
50
+ *
51
+ * @see Filter — builder DSL for constructing FilterNode values
52
+ * @see filterNodeToWhereClause — compiles FilterNode to SQL
53
+ */
54
+ export type FilterNode = {
55
+ op: "eq" | "neq" | "gt" | "gte" | "lt" | "lte";
56
+ field: string;
57
+ value: unknown;
58
+ } | {
59
+ op: "in";
60
+ field: string;
61
+ values: ReadonlyArray<unknown>;
62
+ } | {
63
+ op: "like";
64
+ field: string;
65
+ value: string;
66
+ } | {
67
+ op: "null" | "not_null";
68
+ field: string;
69
+ } | {
70
+ op: "and" | "or";
71
+ left: FilterNode;
72
+ right: FilterNode;
73
+ } | {
74
+ op: "not";
75
+ node: FilterNode;
76
+ };
77
+ declare let _filterNodeSchema: Schema.Schema<FilterNode>;
78
+ /**
79
+ * Effect Schema for `FilterNode`. Used to decode filter query parameters
80
+ * from HTTP request JSON bodies.
81
+ *
82
+ * The schema is self-referential (recursive via `Schema.suspend`) to handle
83
+ * nested `and`/`or`/`not` nodes. This is why the underlying `let` variable
84
+ * exists — the recursive `suspend` needs a forward reference.
85
+ *
86
+ * @example
87
+ * import { Schema } from "effect"
88
+ *
89
+ * const raw = { op: "eq", field: "title", value: "hello" }
90
+ * const filter = Schema.decodeSync(FilterNodeSchema)(raw)
91
+ * // filter: FilterNode — { op: "eq", field: "title", value: "hello" }
92
+ *
93
+ * @see FilterNode — the TypeScript type this schema decodes to
94
+ */
95
+ export { _filterNodeSchema as FilterNodeSchema };
96
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/filter/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE/B;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,CAAA;CAC/B,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,MAAM,UAAU,GAClB;IAAE,EAAE,EAAE,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GACjF;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,CAAA;CAAE,GAC3D;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC5C;IAAE,EAAE,EAAE,MAAM,GAAG,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,EAAE,EAAE,KAAK,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,GACzD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAAA;AAEnC,QAAA,IAAI,iBAAiB,EAAE,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;AAiChD;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,iBAAiB,IAAI,gBAAgB,EAAE,CAAA"}
@@ -0,0 +1,44 @@
1
+ import { Schema } from "effect";
2
+ let _filterNodeSchema;
3
+ _filterNodeSchema = Schema.Union(Schema.Struct({
4
+ op: Schema.Literal("eq", "neq", "gt", "gte", "lt", "lte"),
5
+ field: Schema.String,
6
+ value: Schema.Unknown
7
+ }), Schema.Struct({
8
+ op: Schema.Literal("in"),
9
+ field: Schema.String,
10
+ values: Schema.Array(Schema.Unknown)
11
+ }), Schema.Struct({
12
+ op: Schema.Literal("like"),
13
+ field: Schema.String,
14
+ value: Schema.String
15
+ }), Schema.Struct({
16
+ op: Schema.Literal("null", "not_null"),
17
+ field: Schema.String
18
+ }), Schema.Struct({
19
+ op: Schema.Literal("and", "or"),
20
+ left: Schema.suspend(() => _filterNodeSchema),
21
+ right: Schema.suspend(() => _filterNodeSchema)
22
+ }), Schema.Struct({
23
+ op: Schema.Literal("not"),
24
+ node: Schema.suspend(() => _filterNodeSchema)
25
+ }));
26
+ /**
27
+ * Effect Schema for `FilterNode`. Used to decode filter query parameters
28
+ * from HTTP request JSON bodies.
29
+ *
30
+ * The schema is self-referential (recursive via `Schema.suspend`) to handle
31
+ * nested `and`/`or`/`not` nodes. This is why the underlying `let` variable
32
+ * exists — the recursive `suspend` needs a forward reference.
33
+ *
34
+ * @example
35
+ * import { Schema } from "effect"
36
+ *
37
+ * const raw = { op: "eq", field: "title", value: "hello" }
38
+ * const filter = Schema.decodeSync(FilterNodeSchema)(raw)
39
+ * // filter: FilterNode — { op: "eq", field: "title", value: "hello" }
40
+ *
41
+ * @see FilterNode — the TypeScript type this schema decodes to
42
+ */
43
+ export { _filterNodeSchema as FilterNodeSchema };
44
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/filter/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AA+D/B,IAAI,iBAA4C,CAAA;AAEhD,iBAAiB,GAAG,MAAM,CAAC,KAAK,CAC9B,MAAM,CAAC,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC;IACzD,KAAK,EAAE,MAAM,CAAC,MAAM;IACpB,KAAK,EAAE,MAAM,CAAC,OAAO;CACtB,CAAC,EACF,MAAM,CAAC,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC,MAAM;IACpB,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC;CACrC,CAAC,EACF,MAAM,CAAC,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC,MAAM;IACpB,KAAK,EAAE,MAAM,CAAC,MAAM;CACrB,CAAC,EACF,MAAM,CAAC,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,UAAU,CAAC;IACtC,KAAK,EAAE,MAAM,CAAC,MAAM;CACrB,CAAC,EACF,MAAM,CAAC,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC;IAC/B,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,iBAAiB,CAAC;IAC7C,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,iBAAiB,CAAC;CAC/C,CAAC,EACF,MAAM,CAAC,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,iBAAiB,CAAC;CAC9C,CAAC,CACH,CAAA;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,iBAAiB,IAAI,gBAAgB,EAAE,CAAA"}
@@ -0,0 +1,5 @@
1
+ export * from "./collection/index.js";
2
+ export * from "./filter/index.js";
3
+ export * from "./rule/index.js";
4
+ export type * from "./collection/types.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAA;AACrC,cAAc,mBAAmB,CAAA;AACjC,cAAc,iBAAiB,CAAA;AAC/B,mBAAmB,uBAAuB,CAAA"}
@@ -0,0 +1,4 @@
1
+ export * from "./collection/index.js";
2
+ export * from "./filter/index.js";
3
+ export * from "./rule/index.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAA;AACrC,cAAc,mBAAmB,CAAA;AACjC,cAAc,iBAAiB,CAAA"}
@@ -0,0 +1,97 @@
1
+ import type { AnyCollectionDef, FieldsMap, InferFieldValue } from "../collection/types.js";
2
+ import type { ExprNode, OperandNode } from "./types.js";
3
+ type FieldOperandMethods<K extends string, V = unknown> = {
4
+ eq(right: OperandNode<any, V>): ExprNode<K>;
5
+ neq(right: OperandNode<any, V>): ExprNode<K>;
6
+ gt(right: OperandNode<any, V>): ExprNode<K>;
7
+ gte(right: OperandNode<any, V>): ExprNode<K>;
8
+ lt(right: OperandNode<any, V>): ExprNode<K>;
9
+ lte(right: OperandNode<any, V>): ExprNode<K>;
10
+ in(right: OperandNode<any, V | ReadonlyArray<V>>): ExprNode<K>;
11
+ startsWith(right: OperandNode<any, V>): ExprNode<K>;
12
+ contains(right: OperandNode<any, V>): ExprNode<K>;
13
+ };
14
+ /** An operand node with fluent comparison methods attached. */
15
+ export type FieldOperand<K extends string, V = unknown> = OperandNode<K, V> & FieldOperandMethods<K, V>;
16
+ /**
17
+ * A narrowed version of the Rule builder where `field()` is constrained to field
18
+ * names defined in `F` and comparison operands are typed to match each field's value type.
19
+ * Used as the argument type for the `rules` callback in collection definers.
20
+ */
21
+ export type RuleBuilder<F extends FieldsMap> = Omit<typeof Rule, "field"> & {
22
+ field<K extends keyof F & string>(name: K): FieldOperand<K, InferFieldValue<F[K]>>;
23
+ };
24
+ /**
25
+ * Rule builder API.
26
+ *
27
+ * Constructs access rule expressions using a fluent builder pattern.
28
+ * Rules are compiled to SQL WHERE clauses at runtime by the rule compiler.
29
+ *
30
+ * @example
31
+ * // Simple field comparison
32
+ * Rule.field("ownerId").eq(Rule.authId(Users))
33
+ *
34
+ * // Boolean combination
35
+ * Rule.or(
36
+ * Rule.field("isPublic").eq(Rule.literal(true)),
37
+ * Rule.field("ownerId").eq(Rule.authId(Users))
38
+ * )
39
+ *
40
+ * // Date-based rule
41
+ * Rule.field("createdAt").gte(Rule.dateAdd(Rule.now(), -30, "day"))
42
+ *
43
+ * // Subquery
44
+ * const memberTeams = Rule.subquery(Members, "teamId").where(
45
+ * Rule.field("userId").eq(Rule.authId(Users))
46
+ * )
47
+ * Rule.field("teamId").in(memberTeams)
48
+ *
49
+ * // Always allow
50
+ * Rule.public()
51
+ */
52
+ export declare const Rule: {
53
+ /** Reference a record field by name. Returns a chainable operand. Value type is unknown; use via RuleBuilder for typed comparisons. */
54
+ field: <const K extends string>(name: K) => FieldOperand<K, unknown>;
55
+ /** Reference a value from the incoming request (header, query param, or body field). */
56
+ request: (source: "header" | "query" | "body", key: string) => FieldOperand<string, string>;
57
+ /** Reference a field from the authenticated user's record. */
58
+ auth: <C extends AnyCollectionDef, K extends keyof C["fields"] & string>(_collection: C, field: K) => FieldOperand<string, string>;
59
+ /** Reference the ID of the currently authenticated user. */
60
+ authId: <C extends AnyCollectionDef>(_collection: C) => OperandNode<any, string>;
61
+ /** A literal value for comparison. The value type `V` is inferred from the argument. */
62
+ literal: <V>(value: V) => OperandNode<any, V>;
63
+ /**
64
+ * Create a subquery operand into another collection.
65
+ * Call `.where()` on the result to finish the subquery.
66
+ *
67
+ * @example
68
+ * Rule.subquery(Members, "teamId").where(
69
+ * Rule.field("userId").eq(Rule.authId(Users))
70
+ * )
71
+ */
72
+ subquery: <C extends AnyCollectionDef, K extends keyof C["fields"] & string>(_collection: C, field: K) => {
73
+ where: (expr: ExprNode<any>) => OperandNode<any, string>;
74
+ };
75
+ /** Current timestamp operand. Compiles to `datetime('now')` in SQLite. */
76
+ now: () => OperandNode<any, string>;
77
+ /** Add a duration to a date operand for date arithmetic. */
78
+ dateAdd: (operand: OperandNode<any>, amount: number, unit: "day" | "hour" | "minute") => OperandNode<any, string>;
79
+ /** Compute the difference between two date operands. */
80
+ dateDiff: (left: OperandNode<any>, right: OperandNode<any>, unit: "day" | "hour" | "minute") => OperandNode<any, number>;
81
+ /** Logical AND of expressions. */
82
+ and: <K extends string = string>(...exprs: Array<ExprNode<K>>) => ExprNode<K>;
83
+ /** Logical OR of expressions. */
84
+ or: <K extends string = string>(...exprs: Array<ExprNode<K>>) => ExprNode<K>;
85
+ /** Logical negation of a sub-expression. */
86
+ not: <K extends string = string>(expr: ExprNode<K>) => ExprNode<K>;
87
+ /** Always-true rule that allows all access. */
88
+ public: <K extends string = string>() => ExprNode<K>;
89
+ };
90
+ /**
91
+ * Creates a typed `RuleBuilder<F>` that constrains `field()` to names in `F`
92
+ * and types each comparison operand to the field's value type.
93
+ * Used internally by collection definers.
94
+ */
95
+ export declare function makeRuleBuilder<F extends FieldsMap>(): RuleBuilder<F>;
96
+ export {};
97
+ //# sourceMappingURL=builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../../src/rule/builder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,126 @@
1
+ function toChainable(operand) {
2
+ const methods = {
3
+ eq(right) { return { op: "eq", left: operand, right }; },
4
+ neq(right) { return { op: "neq", left: operand, right }; },
5
+ gt(right) { return { op: "gt", left: operand, right }; },
6
+ gte(right) { return { op: "gte", left: operand, right }; },
7
+ lt(right) { return { op: "lt", left: operand, right }; },
8
+ lte(right) { return { op: "lte", left: operand, right }; },
9
+ in(right) { return { op: "in", left: operand, right }; },
10
+ startsWith(right) { return { op: "startsWith", left: operand, right }; },
11
+ contains(right) { return { op: "contains", left: operand, right }; }
12
+ };
13
+ return Object.assign({}, operand, methods);
14
+ }
15
+ /**
16
+ * Rule builder API.
17
+ *
18
+ * Constructs access rule expressions using a fluent builder pattern.
19
+ * Rules are compiled to SQL WHERE clauses at runtime by the rule compiler.
20
+ *
21
+ * @example
22
+ * // Simple field comparison
23
+ * Rule.field("ownerId").eq(Rule.authId(Users))
24
+ *
25
+ * // Boolean combination
26
+ * Rule.or(
27
+ * Rule.field("isPublic").eq(Rule.literal(true)),
28
+ * Rule.field("ownerId").eq(Rule.authId(Users))
29
+ * )
30
+ *
31
+ * // Date-based rule
32
+ * Rule.field("createdAt").gte(Rule.dateAdd(Rule.now(), -30, "day"))
33
+ *
34
+ * // Subquery
35
+ * const memberTeams = Rule.subquery(Members, "teamId").where(
36
+ * Rule.field("userId").eq(Rule.authId(Users))
37
+ * )
38
+ * Rule.field("teamId").in(memberTeams)
39
+ *
40
+ * // Always allow
41
+ * Rule.public()
42
+ */
43
+ export const Rule = {
44
+ /** Reference a record field by name. Returns a chainable operand. Value type is unknown; use via RuleBuilder for typed comparisons. */
45
+ field: (name) => toChainable({ kind: "field", field: name }),
46
+ /** Reference a value from the incoming request (header, query param, or body field). */
47
+ request: (source, key) => toChainable({ kind: "request", source, key }),
48
+ /** Reference a field from the authenticated user's record. */
49
+ auth: (_collection, field) => toChainable({ kind: "auth", collection: _collection.name, field }),
50
+ /** Reference the ID of the currently authenticated user. */
51
+ authId: (_collection) => ({
52
+ kind: "authId",
53
+ collection: _collection.name
54
+ }),
55
+ /** A literal value for comparison. The value type `V` is inferred from the argument. */
56
+ literal: (value) => ({
57
+ kind: "literal",
58
+ value
59
+ }),
60
+ /**
61
+ * Create a subquery operand into another collection.
62
+ * Call `.where()` on the result to finish the subquery.
63
+ *
64
+ * @example
65
+ * Rule.subquery(Members, "teamId").where(
66
+ * Rule.field("userId").eq(Rule.authId(Users))
67
+ * )
68
+ */
69
+ subquery: (_collection, field) => ({
70
+ where: (expr) => ({
71
+ kind: "subquery",
72
+ collection: _collection.name,
73
+ field,
74
+ where: expr
75
+ })
76
+ }),
77
+ /** Current timestamp operand. Compiles to `datetime('now')` in SQLite. */
78
+ now: () => ({ kind: "now" }),
79
+ /** Add a duration to a date operand for date arithmetic. */
80
+ dateAdd: (operand, amount, unit) => ({
81
+ kind: "dateAdd",
82
+ operand,
83
+ amount,
84
+ unit
85
+ }),
86
+ /** Compute the difference between two date operands. */
87
+ dateDiff: (left, right, unit) => ({
88
+ kind: "dateDiff",
89
+ left,
90
+ right,
91
+ unit
92
+ }),
93
+ /** Logical AND of expressions. */
94
+ and: (...exprs) => {
95
+ if (exprs.length === 0)
96
+ return { op: "public" };
97
+ if (exprs.length === 1)
98
+ return exprs[0];
99
+ return exprs.slice(1).reduce((acc, curr) => ({ op: "and", left: acc, right: curr }), exprs[0]);
100
+ },
101
+ /** Logical OR of expressions. */
102
+ or: (...exprs) => {
103
+ if (exprs.length === 0)
104
+ return { op: "public" };
105
+ if (exprs.length === 1)
106
+ return exprs[0];
107
+ return exprs.slice(1).reduce((acc, curr) => ({ op: "or", left: acc, right: curr }), exprs[0]);
108
+ },
109
+ /** Logical negation of a sub-expression. */
110
+ not: (expr) => ({ op: "not", expr }),
111
+ /** Always-true rule that allows all access. */
112
+ public: () => ({ op: "public" })
113
+ };
114
+ /**
115
+ * Creates a typed `RuleBuilder<F>` that constrains `field()` to names in `F`
116
+ * and types each comparison operand to the field's value type.
117
+ * Used internally by collection definers.
118
+ */
119
+ export function makeRuleBuilder() {
120
+ function field(name) {
121
+ return toChainable({ kind: "field", field: name });
122
+ }
123
+ const builder = { ...Rule, field };
124
+ return builder;
125
+ }
126
+ //# sourceMappingURL=builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder.js","sourceRoot":"","sources":["../../../src/rule/builder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export * from "./types.js";
2
+ export * from "./builder.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/rule/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA"}
@@ -0,0 +1,3 @@
1
+ export * from "./types.js";
2
+ export * from "./builder.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/rule/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA"}