Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/infra-sentry-node-dep.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@effect-app/infra": patch
---

Declare `@sentry/node` as a runtime dependency of `@effect-app/infra`. `errorReporter.ts` imports it statically, so `pnpm install --prod` of linked source (Docker) must install it next to the package, not only as a peer of the app.
9 changes: 9 additions & 0 deletions .changeset/native-encoded-query-json.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"effect-app": minor
"@effect-app/infra": minor
"@effect-app/vue-components": minor
---

Stop forcing Date/Map/Set Encoded shapes to JSON.

`Schema.Date` / `ReadonlySet` / `ReadonlyMap` now keep native Encoded types (`Date`, `Set`, `Map`). Use `DateFromString`, `ReadonlySetFromArray`, and `ReadonlyMapFromArray` when the Encoded form must be JSON. The query DSL accepts those native values, including array ops (`includes` / `in` / `includes-any`) on `Date[]` and `ReadonlySet` fields. Memory, Disk, SQL, and Cosmos convert Encoded Date/Map/Set through `Schema.toCodecJson` on write/read; query parameters and defaults lower the same way from the store schema. App types such as DateOnly stay native Encoded and JSON-lower via that schema — not a type registry.
8 changes: 8 additions & 0 deletions .changeset/query-map-has-ops.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"effect-app": minor
"@effect-app/infra": minor
---

Query maps as JSON arrays of `[key, value]` tuples.

`where("meta", "hasKey" | "hasValue" | "hasKeyValue", ...)` (and `not*` / `*-any` / `*-all` variants) filter `ReadonlyMap` fields. Memory, Disk, SQLite, Postgres, and Cosmos compile those ops against the encoded tuple array.
8 changes: 8 additions & 0 deletions .changeset/store-json-values.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"effect-app": minor
"@effect-app/infra": minor
---

JSON stores lower native Encoded values (Date, Map, Set, and app types such as DateOnly) through the store's document schema.

`makeRepo` already passes that schema. Adapters encode documents, query parameters, and defaults with `Schema.toCodecJson(toEncoded(schema))` at the field path. No type registry. Schemaless stores still lower Date/Map/Set structurally.
9 changes: 5 additions & 4 deletions packages/effect-app/src/Model/Repository/internal/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,7 @@ export function makeRepoInternal<
.pipe(
Effect.andThen(
(items) =>
S.decodeEffectConcurrently(S.Array(a.schema ?? schema))(items).pipe(
S.decodeEffectConcurrently(S.Array(S.toCodecJson(a.schema ?? schema)))(items).pipe(
provideRctx,
timeSchema("decode", name, "aggregate", items.length)
)
Expand All @@ -593,7 +593,7 @@ export function makeRepoInternal<
.pipe(
Effect.andThen(
(items) =>
S.decodeEffectConcurrently(S.Array(a.schema ?? schema))(items).pipe(
S.decodeEffectConcurrently(S.Array(S.toCodecJson(a.schema ?? schema)))(items).pipe(
provideRctx,
timeSchema("decode", name, "project", items.length)
)
Expand All @@ -604,7 +604,7 @@ export function makeRepoInternal<
// TODO: mapFrom but need to support per field and dependencies
.pipe(
Effect.flatMap((items) =>
S.decodeEffectConcurrently(S.Array(a.schema))(items).pipe(
S.decodeEffectConcurrently(S.Array(S.toCodecJson(a.schema)))(items).pipe(
Effect.map(Array.getSomes),
provideRctx,
timeSchema("decode", name, "collect", items.length)
Expand Down Expand Up @@ -737,7 +737,7 @@ export function makeRepoInternal<
queryRaw<A, Out, QR>(schema: S.Codec<A, Out, QR>, q: Q.RawQuery<Encoded, Out>) {
return store.queryRaw(q).pipe(
Effect.flatMap((items) =>
S.decodeEffectConcurrently(S.Array(schema))(items).pipe(
S.decodeEffectConcurrently(S.Array(S.toCodecJson(schema)))(items as readonly S.Json[]).pipe(
timeSchema("decode", name, undefined, items.length)
)
),
Expand Down Expand Up @@ -887,6 +887,7 @@ export function makeStore<Encoded extends FieldValues>() {
: undefined,
{
...config,
schema,
partitionValue: config?.partitionValue
?? ((_) => "primary") /*(isIntegrationEvent(r) ? r.companyId : r.id*/
}
Expand Down
20 changes: 19 additions & 1 deletion packages/effect-app/src/Model/filter/filterApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,24 @@ export type OtherOps =
| "notIncludes-any"
| "includes-all"
| "notIncludes-all"
| "hasKey"
| "notHasKey"
| "hasValue"
| "notHasValue"
| "hasKeyValue"
| "notHasKeyValue"
| "hasKey-any"
| "notHasKey-any"
| "hasKey-all"
| "notHasKey-all"
| "hasValue-any"
| "notHasValue-any"
| "hasValue-all"
| "notHasValue-all"
| "hasKeyValue-any"
| "notHasKeyValue-any"
| "hasKeyValue-all"
| "notHasKeyValue-all"
| "eq"
| "neq"
| "gt"
Expand Down Expand Up @@ -44,7 +62,7 @@ export type FilterR = {
op: Ops

path: string
value: string // ToDO: Value[]
value: unknown
}

export type FilterResult =
Expand Down
195 changes: 188 additions & 7 deletions packages/effect-app/src/Model/query/dsl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1157,7 +1157,15 @@ export const aggregate: {
return new Project({ current, schema, mode: "aggregate", aggregateMap } as any)
}

type GetArV<T> = T extends readonly (infer R)[] ? R : never
type GetArV<T> = T extends ReadonlySet<infer R> ? R
: T extends readonly (infer R)[] ? R
: never

type GetMapK<T> = T extends ReadonlyMap<infer K, infer _V> ? K : never
type GetMapV<T> = T extends ReadonlyMap<infer _K, infer V> ? V : never
type GetMapEntry<T> = T extends ReadonlyMap<infer K, infer V> ? readonly [K, V] : never

type InValues<T> = readonly T[] | ReadonlySet<T>

export type FilterContinuations<IsCurrentInitial extends boolean = false> = {
<
Expand Down Expand Up @@ -1207,13 +1215,12 @@ export type FilterContinuations<IsCurrentInitial extends boolean = false> = {
<
TFieldValues extends FieldValues,
TFieldName extends FieldPath<TFieldValues>,
const V extends readonly FieldPathValue<TFieldValues, TFieldName>[],
TFieldValuesRefined extends TFieldValues = TFieldValues,
E extends boolean = false
>(
path: TFieldName,
op: "in" | "notIn",
value: V
value: InValues<FieldPathValue<TFieldValues, TFieldName>>
): (
current: IsCurrentInitial extends true ? Query<TFieldValues>
: QueryWhere<TFieldValues, TFieldValuesRefined, E>
Expand Down Expand Up @@ -1249,7 +1256,97 @@ export type FilterContinuations<IsCurrentInitial extends boolean = false> = {
| "notIncludes-any"
| "includes-all"
| "notIncludes-all",
value: readonly GetArV<V>[]
value: InValues<GetArV<V>>
): (
current: IsCurrentInitial extends true ? Query<TFieldValues>
: QueryWhere<TFieldValues, TFieldValuesRefined, E>
) => IsCurrentInitial extends true ? QueryWhere<TFieldValues>
: QueryWhere<TFieldValues, TFieldValuesRefined, E>
<
TFieldValues extends FieldValues,
TFieldName extends FieldPath<TFieldValues>,
V extends FieldPathValue<TFieldValues, TFieldName>,
TFieldValuesRefined extends TFieldValues = TFieldValues,
E extends boolean = false
>(
path: TFieldName,
op: "hasKey" | "notHasKey",
value: GetMapK<V>
): (
current: IsCurrentInitial extends true ? Query<TFieldValues>
: QueryWhere<TFieldValues, TFieldValuesRefined, E>
) => IsCurrentInitial extends true ? QueryWhere<TFieldValues>
: QueryWhere<TFieldValues, TFieldValuesRefined, E>
<
TFieldValues extends FieldValues,
TFieldName extends FieldPath<TFieldValues>,
V extends FieldPathValue<TFieldValues, TFieldName>,
TFieldValuesRefined extends TFieldValues = TFieldValues,
E extends boolean = false
>(
path: TFieldName,
op: "hasValue" | "notHasValue",
value: GetMapV<V>
): (
current: IsCurrentInitial extends true ? Query<TFieldValues>
: QueryWhere<TFieldValues, TFieldValuesRefined, E>
) => IsCurrentInitial extends true ? QueryWhere<TFieldValues>
: QueryWhere<TFieldValues, TFieldValuesRefined, E>
<
TFieldValues extends FieldValues,
TFieldName extends FieldPath<TFieldValues>,
V extends FieldPathValue<TFieldValues, TFieldName>,
TFieldValuesRefined extends TFieldValues = TFieldValues,
E extends boolean = false
>(
path: TFieldName,
op: "hasKeyValue" | "notHasKeyValue",
value: GetMapEntry<V>
): (
current: IsCurrentInitial extends true ? Query<TFieldValues>
: QueryWhere<TFieldValues, TFieldValuesRefined, E>
) => IsCurrentInitial extends true ? QueryWhere<TFieldValues>
: QueryWhere<TFieldValues, TFieldValuesRefined, E>
<
TFieldValues extends FieldValues,
TFieldName extends FieldPath<TFieldValues>,
V extends FieldPathValue<TFieldValues, TFieldName>,
TFieldValuesRefined extends TFieldValues = TFieldValues,
E extends boolean = false
>(
path: TFieldName,
op: "hasKey-any" | "notHasKey-any" | "hasKey-all" | "notHasKey-all",
value: InValues<GetMapK<V>>
): (
current: IsCurrentInitial extends true ? Query<TFieldValues>
: QueryWhere<TFieldValues, TFieldValuesRefined, E>
) => IsCurrentInitial extends true ? QueryWhere<TFieldValues>
: QueryWhere<TFieldValues, TFieldValuesRefined, E>
<
TFieldValues extends FieldValues,
TFieldName extends FieldPath<TFieldValues>,
V extends FieldPathValue<TFieldValues, TFieldName>,
TFieldValuesRefined extends TFieldValues = TFieldValues,
E extends boolean = false
>(
path: TFieldName,
op: "hasValue-any" | "notHasValue-any" | "hasValue-all" | "notHasValue-all",
value: InValues<GetMapV<V>>
): (
current: IsCurrentInitial extends true ? Query<TFieldValues>
: QueryWhere<TFieldValues, TFieldValuesRefined, E>
) => IsCurrentInitial extends true ? QueryWhere<TFieldValues>
: QueryWhere<TFieldValues, TFieldValuesRefined, E>
<
TFieldValues extends FieldValues,
TFieldName extends FieldPath<TFieldValues>,
V extends FieldPathValue<TFieldValues, TFieldName>,
TFieldValuesRefined extends TFieldValues = TFieldValues,
E extends boolean = false
>(
path: TFieldName,
op: "hasKeyValue-any" | "notHasKeyValue-any" | "hasKeyValue-all" | "notHasKeyValue-all",
value: InValues<GetMapEntry<V>>
): (
current: IsCurrentInitial extends true ? Query<TFieldValues>
: QueryWhere<TFieldValues, TFieldValuesRefined, E>
Expand Down Expand Up @@ -1318,12 +1415,12 @@ export type FilterContinuationsWithSubpath = {
TFieldName extends FieldPath<TFieldValues>,
TFieldValuesSub extends TFieldValues[TFieldName][number],
TFieldNameSub extends FieldPath<TFieldValuesSub>,
const V extends readonly FieldPathValue<TFieldValuesSub, TFieldNameSub>[]
V extends FieldPathValue<TFieldValuesSub, TFieldNameSub>
>(
subPath: TFieldName,
restPath: TFieldNameSub,
op: "in" | "notIn",
value: V
value: InValues<V>
): (
current: Query<TFieldValues>
) => QueryWhere<TFieldValues>
Expand Down Expand Up @@ -1357,7 +1454,91 @@ export type FilterContinuationsWithSubpath = {
| "notIncludes-any"
| "includes-all"
| "notIncludes-all",
value: readonly GetArV<V>[]
value: InValues<GetArV<V>>
): (
current: Query<TFieldValues>
) => QueryWhere<TFieldValues>
<
TFieldValues extends FieldValues,
TFieldName extends FieldPath<TFieldValues>,
TFieldValuesSub extends TFieldValues[TFieldName][number],
TFieldNameSub extends FieldPath<TFieldValuesSub>,
V extends FieldPathValue<TFieldValuesSub, TFieldNameSub>
>(
subPath: TFieldName,
restPath: TFieldNameSub,
op: "hasKey" | "notHasKey",
value: GetMapK<V>
): (
current: Query<TFieldValues>
) => QueryWhere<TFieldValues>
<
TFieldValues extends FieldValues,
TFieldName extends FieldPath<TFieldValues>,
TFieldValuesSub extends TFieldValues[TFieldName][number],
TFieldNameSub extends FieldPath<TFieldValuesSub>,
V extends FieldPathValue<TFieldValuesSub, TFieldNameSub>
>(
subPath: TFieldName,
restPath: TFieldNameSub,
op: "hasValue" | "notHasValue",
value: GetMapV<V>
): (
current: Query<TFieldValues>
) => QueryWhere<TFieldValues>
<
TFieldValues extends FieldValues,
TFieldName extends FieldPath<TFieldValues>,
TFieldValuesSub extends TFieldValues[TFieldName][number],
TFieldNameSub extends FieldPath<TFieldValuesSub>,
V extends FieldPathValue<TFieldValuesSub, TFieldNameSub>
>(
subPath: TFieldName,
restPath: TFieldNameSub,
op: "hasKeyValue" | "notHasKeyValue",
value: GetMapEntry<V>
): (
current: Query<TFieldValues>
) => QueryWhere<TFieldValues>
<
TFieldValues extends FieldValues,
TFieldName extends FieldPath<TFieldValues>,
TFieldValuesSub extends TFieldValues[TFieldName][number],
TFieldNameSub extends FieldPath<TFieldValuesSub>,
V extends FieldPathValue<TFieldValuesSub, TFieldNameSub>
>(
subPath: TFieldName,
restPath: TFieldNameSub,
op: "hasKey-any" | "notHasKey-any" | "hasKey-all" | "notHasKey-all",
value: InValues<GetMapK<V>>
): (
current: Query<TFieldValues>
) => QueryWhere<TFieldValues>
<
TFieldValues extends FieldValues,
TFieldName extends FieldPath<TFieldValues>,
TFieldValuesSub extends TFieldValues[TFieldName][number],
TFieldNameSub extends FieldPath<TFieldValuesSub>,
V extends FieldPathValue<TFieldValuesSub, TFieldNameSub>
>(
subPath: TFieldName,
restPath: TFieldNameSub,
op: "hasValue-any" | "notHasValue-any" | "hasValue-all" | "notHasValue-all",
value: InValues<GetMapV<V>>
): (
current: Query<TFieldValues>
) => QueryWhere<TFieldValues>
<
TFieldValues extends FieldValues,
TFieldName extends FieldPath<TFieldValues>,
TFieldValuesSub extends TFieldValues[TFieldName][number],
TFieldNameSub extends FieldPath<TFieldValuesSub>,
V extends FieldPathValue<TFieldValuesSub, TFieldNameSub>
>(
subPath: TFieldName,
restPath: TFieldNameSub,
op: "hasKeyValue-any" | "notHasKeyValue-any" | "hasKeyValue-all" | "notHasKeyValue-all",
value: InValues<GetMapEntry<V>>
): (
current: Query<TFieldValues>
) => QueryWhere<TFieldValues>
Expand Down
Loading
Loading