@effect-app/infra 2.9.3 → 2.9.5

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.
@@ -151,7 +151,9 @@ export function makeRepoInternal<
151
151
  return pipe(
152
152
  encodeId({ [idKey]: id } as any),
153
153
  Effect.orDie,
154
- Effect.map((_) => (_ as any)[idKey]), // we will have idKey because the transform is undone again by the encode schema mumbo jumbo above
154
+ // we will have idKey because the transform is undone again by the encode schema mumbo jumbo above
155
+ // TODO: make reliable. (Security: isin: PrimaryKey(ISIN), idKey: "isin", does end up with "id")
156
+ Effect.map((_) => (_ as any)[idKey] ?? (_ as any).id),
155
157
  Effect.flatMap(findEId)
156
158
  )
157
159
  }
@@ -489,6 +491,54 @@ export interface Repos<
489
491
 
490
492
  export type GetRepoType<T> = T extends { type: infer R } ? R : never
491
493
 
494
+ export interface RepositoryOptions<
495
+ IdKey extends keyof T,
496
+ Encoded extends {
497
+ id: string
498
+ },
499
+ T,
500
+ Evt = never,
501
+ RPublish = never,
502
+ E = never,
503
+ RInitial = never,
504
+ RCtx = never
505
+ > {
506
+ /**
507
+ * Specify the idKey of the Type side, if it's different from the default "id".
508
+ * Does not change the Encoded side, which is always "id" to support database drivers.
509
+ * At this time as queries are operating on the Encoded side, the queries must still specify "id" regardless.
510
+ */
511
+ idKey: IdKey
512
+ /**
513
+ * just in time Migration: for complex migrations that aren't just default simple values
514
+ * use the config.defaultValues instead for simple default values
515
+ */
516
+ jitM?: (pm: Encoded) => Encoded
517
+ config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
518
+ partitionValue?: (a: Encoded) => string
519
+ }
520
+ /**
521
+ * Optional handler to be able to publish events after successfull save.
522
+ */
523
+ publishEvents?: (evt: NonEmptyReadonlyArray<Evt>) => Effect<void, never, RPublish>
524
+ /**
525
+ * Optional creator for initial data in the table when it's created for the first itme.
526
+ */
527
+ makeInitial?: Effect<readonly T[], E, RInitial>
528
+ /**
529
+ * Optional context to be provided to Schema decode/encode.
530
+ * Useful for effectful transformations like XWithItems, where items is a transformation retrieving elements from another database table or other source.
531
+ */
532
+ schemaContext?: Context.Context<RCtx>
533
+ }
534
+
535
+ /**
536
+ * Create a repository instance.
537
+ * @param itemType an identifier used for the table name and e.g NotFoundError
538
+ * @param schema the Schema used for this Repository
539
+ * @param options @see RepositoryOptions
540
+ * @returns a Repository
541
+ */
492
542
  export const makeRepo: {
493
543
  <
494
544
  ItemType extends string,
@@ -504,16 +554,7 @@ export const makeRepo: {
504
554
  >(
505
555
  itemType: ItemType,
506
556
  schema: S.Schema<T, Encoded, RSchema>,
507
- options: {
508
- idKey: IdKey
509
- jitM?: (pm: Encoded) => Encoded
510
- config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
511
- partitionValue?: (a: Encoded) => string
512
- }
513
- publishEvents?: (evt: NonEmptyReadonlyArray<Evt>) => Effect<void, never, RPublish>
514
- makeInitial?: Effect<readonly T[], E, RInitial>
515
- schemaContext?: Context.Context<RCtx>
516
- }
557
+ options: RepositoryOptions<IdKey, Encoded, T, Evt, RPublish, E, RInitial, RCtx>
517
558
  ): Effect.Effect<
518
559
  ExtendedRepository<T, Encoded, Evt, ItemType, IdKey, Exclude<RSchema, RCtx>, RPublish>,
519
560
  E,
@@ -532,15 +573,7 @@ export const makeRepo: {
532
573
  >(
533
574
  itemType: ItemType,
534
575
  schema: S.Schema<T, Encoded, RSchema>,
535
- options: {
536
- jitM?: (pm: Encoded) => Encoded
537
- config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
538
- partitionValue?: (a: Encoded) => string
539
- }
540
- publishEvents?: (evt: NonEmptyReadonlyArray<Evt>) => Effect<void, never, RPublish>
541
- makeInitial?: Effect<readonly T[], E, RInitial>
542
- schemaContext?: Context.Context<RCtx>
543
- }
576
+ options: Omit<RepositoryOptions<"id", Encoded, T, Evt, RPublish, E, RInitial, RCtx>, "idKey">
544
577
  ): Effect.Effect<
545
578
  ExtendedRepository<T, Encoded, Evt, ItemType, "id", Exclude<RSchema, RCtx>, RPublish>,
546
579
  E,
@@ -554,22 +587,13 @@ export const makeRepo: {
554
587
  IdKey extends keyof T,
555
588
  E = never,
556
589
  RInitial = never,
557
- R2 = never,
590
+ RPublish = never,
558
591
  Evt = never,
559
592
  RCtx = never
560
593
  >(
561
594
  itemType: ItemType,
562
595
  schema: S.Schema<T, Encoded, R>,
563
- options: {
564
- idKey?: IdKey
565
- jitM?: (pm: Encoded) => Encoded
566
- config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
567
- partitionValue?: (a: Encoded) => string
568
- }
569
- publishEvents?: (evt: NonEmptyReadonlyArray<Evt>) => Effect<void, never, R2>
570
- makeInitial?: Effect<readonly T[], E, RInitial>
571
- schemaContext?: Context.Context<RCtx>
572
- }
596
+ options: Omit<RepositoryOptions<IdKey, Encoded, T, Evt, RPublish, E, RInitial, RCtx>, "idKey"> & { idKey?: IdKey }
573
597
  ) =>
574
598
  Effect.gen(function*() {
575
599
  const mkRepo = makeRepoInternal<Evt>()(
@@ -579,7 +603,7 @@ export const makeRepo: {
579
603
  (e, _etag) => ({ ...e, _etag }),
580
604
  options.idKey ?? "id" as any
581
605
  )
582
- const r = yield* mkRepo.make<RInitial, E, R2, RCtx>(options as any)
606
+ const r = yield* mkRepo.make<RInitial, E, RPublish, RCtx>(options as any)
583
607
  const repo = extendRepo(r)
584
608
  return repo
585
609
  })
@@ -272,7 +272,12 @@ function makeCosmosStore({ prefix }: StorageConfig) {
272
272
  .pipe(Effect
273
273
  .withSpan("Cosmos.find [effect-app/infra/Store]", {
274
274
  captureStackTrace: false,
275
- attributes: { "repository.container_id": containerId, "repository.model_name": name }
275
+ attributes: {
276
+ "repository.container_id": containerId,
277
+ "repository.model_name": name,
278
+ partitionValue: config?.partitionValue({ id } as Encoded),
279
+ id
280
+ }
276
281
  })),
277
282
  set: (e) =>
278
283
  Option
@@ -322,7 +327,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
322
327
  Effect
323
328
  .withSpan("Cosmos.set [effect-app/infra/Store]", {
324
329
  captureStackTrace: false,
325
- attributes: { "repository.container_id": containerId, "repository.model_name": name }
330
+ attributes: { "repository.container_id": containerId, "repository.model_name": name, id: e.id }
326
331
  })
327
332
  ),
328
333
  batchSet,
@@ -333,7 +338,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
333
338
  .pipe(Effect
334
339
  .withSpan("Cosmos.remove [effect-app/infra/Store]", {
335
340
  captureStackTrace: false,
336
- attributes: { "repository.container_id": containerId, "repository.model_name": name }
341
+ attributes: { "repository.container_id": containerId, "repository.model_name": name, id: e.id }
337
342
  }))
338
343
  }
339
344
 
@@ -7,12 +7,28 @@ import type { FilterResult } from "../Model/filter/filterApi.js"
7
7
  import type { FieldValues } from "../Model/filter/types.js"
8
8
  import type { FieldPath } from "../Model/filter/types/path/index.js"
9
9
 
10
- export type StoreConfig<E> = {
11
- uniqueKeys?: UniqueKey[]
12
- maxBulkSize?: number
10
+ export interface StoreConfig<E> {
13
11
  partitionValue: (e: E) => string | undefined
12
+ /**
13
+ * Primarily used for testing, creating namespaces in the database to separate data e.g to run multiple tests in isolation within the same database
14
+ * currently only supported in disk/memory. CosmosDB is TODO.
15
+ */
14
16
  allowNamespace?: (namespace: string) => boolean
17
+ /**
18
+ * just in time migrations, supported by the database driver, supporting queries, for simple default values
19
+ */
15
20
  defaultValues?: Partial<E>
21
+
22
+ /**
23
+ * How many items can be processed in one batch at a time.
24
+ * Defaults to 100 for CosmosDB.
25
+ */
26
+ maxBulkSize?: number
27
+
28
+ /**
29
+ * Unique indexes, mainly for CosmosDB
30
+ */
31
+ uniqueKeys?: UniqueKey[]
16
32
  }
17
33
 
18
34
  export type SupportedValues = string | boolean | number | null
@@ -357,6 +357,7 @@ export const makeRouter = <
357
357
  } as any
358
358
  : req,
359
359
  (req) =>
360
+ // TODO: render more data... similar to console?
360
361
  Effect
361
362
  .annotateCurrentSpan(
362
363
  "requestInput",
@@ -342,6 +342,7 @@ export const makeRouter = <
342
342
  } as any
343
343
  : req,
344
344
  (req) =>
345
+ // TODO: render more data... similar to console?
345
346
  Effect
346
347
  .annotateCurrentSpan(
347
348
  "requestInput",
@@ -0,0 +1,37 @@
1
+ // packages/infra/vitest.config.ts
2
+ import { defineConfig } from "file:///Users/patrickroza/pj/effect-app/libs/node_modules/.pnpm/vite@5.2.6_@types+node@20.11.30/node_modules/vite/dist/node/index.js";
3
+
4
+ // vite.config.base.ts
5
+ import path from "path";
6
+ import fs from "fs";
7
+ var __vite_injected_original_dirname = "/Users/patrickroza/pj/effect-app/libs";
8
+ function makeConfig(dirName) {
9
+ const prefix = path.resolve(__vite_injected_original_dirname, "packages");
10
+ const packages = fs.readdirSync(prefix).map((f) => prefix + "/" + f).filter((f) => fs.lstatSync(f).isDirectory());
11
+ const cfg = {
12
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
13
+ //plugins: [autoImport],
14
+ test: {
15
+ include: ["./test/**/*.test.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
16
+ reporters: "verbose",
17
+ globals: true
18
+ },
19
+ resolve: {
20
+ alias: packages.reduce((acc, cur) => {
21
+ acc[JSON.parse(fs.readFileSync(cur + "/package.json", "utf-8")).name] = path.resolve(cur, cur.endsWith("core") ? "dist" : "src");
22
+ return acc;
23
+ }, {})
24
+ // "@effect-app/core/Prelude": path.join(__dirname, "packages/core/src/Prelude.code.ts")
25
+ }
26
+ };
27
+ console.log(cfg);
28
+ return cfg;
29
+ }
30
+
31
+ // packages/infra/vitest.config.ts
32
+ var __vite_injected_original_dirname2 = "/Users/patrickroza/pj/effect-app/libs/packages/infra";
33
+ var vitest_config_default = defineConfig(makeConfig(__vite_injected_original_dirname2));
34
+ export {
35
+ vitest_config_default as default
36
+ };
37
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsicGFja2FnZXMvaW5mcmEvdml0ZXN0LmNvbmZpZy50cyIsICJ2aXRlLmNvbmZpZy5iYXNlLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyJjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZGlybmFtZSA9IFwiL1VzZXJzL3BhdHJpY2tyb3phL3BqL2VmZmVjdC1hcHAvbGlicy9wYWNrYWdlcy9pbmZyYVwiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiL1VzZXJzL3BhdHJpY2tyb3phL3BqL2VmZmVjdC1hcHAvbGlicy9wYWNrYWdlcy9pbmZyYS92aXRlc3QuY29uZmlnLnRzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ltcG9ydF9tZXRhX3VybCA9IFwiZmlsZTovLy9Vc2Vycy9wYXRyaWNrcm96YS9wai9lZmZlY3QtYXBwL2xpYnMvcGFja2FnZXMvaW5mcmEvdml0ZXN0LmNvbmZpZy50c1wiOy8vLyA8cmVmZXJlbmNlIHR5cGVzPVwidml0ZXN0XCIgLz5cbmltcG9ydCB7IGRlZmluZUNvbmZpZyB9IGZyb20gXCJ2aXRlXCJcbmltcG9ydCBtYWtlQ29uZmlnIGZyb20gXCIuLi8uLi92aXRlLmNvbmZpZy5iYXNlXCJcblxuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKG1ha2VDb25maWcoX19kaXJuYW1lKSlcbiIsICJjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZGlybmFtZSA9IFwiL1VzZXJzL3BhdHJpY2tyb3phL3BqL2VmZmVjdC1hcHAvbGlic1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiL1VzZXJzL3BhdHJpY2tyb3phL3BqL2VmZmVjdC1hcHAvbGlicy92aXRlLmNvbmZpZy5iYXNlLnRzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ltcG9ydF9tZXRhX3VybCA9IFwiZmlsZTovLy9Vc2Vycy9wYXRyaWNrcm96YS9wai9lZmZlY3QtYXBwL2xpYnMvdml0ZS5jb25maWcuYmFzZS50c1wiOy8vLyA8cmVmZXJlbmNlIHR5cGVzPVwidml0ZXN0XCIgLz5cbmltcG9ydCBwYXRoIGZyb20gXCJwYXRoXCJcbmltcG9ydCBmcyBmcm9tIFwiZnNcIlxuaW1wb3J0IEF1dG9JbXBvcnQgZnJvbSBcInVucGx1Z2luLWF1dG8taW1wb3J0L3ZpdGVcIlxuaW1wb3J0IHsgZGVmaW5lQ29uZmlnIH0gZnJvbSBcInZpdGVzdC9jb25maWdcIlxuXG4vLyBjb25zdCBhdXRvSW1wb3J0ID0gQXV0b0ltcG9ydCh7XG4vLyAgIGR0czogXCIuL3Rlc3QvYXV0by1pbXBvcnRzLmQudHNcIixcbi8vICAgLy8gaW5jbHVkZTogW1xuLy8gICAvLyAgIC9cXC50ZXN0XFwuW3RqXXN4PyQvIC8vIC50cywgLnRzeCwgLmpzLCAuanN4XG4vLyAgIC8vIF0sXG4vLyAgIGltcG9ydHM6IFtcbi8vICAgICBcInZpdGVzdFwiXG4vLyAgIF1cbi8vIH0pXG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIG1ha2VDb25maWcoZGlyTmFtZT86IHN0cmluZykge1xuICBjb25zdCBwcmVmaXggPSBwYXRoLnJlc29sdmUoX19kaXJuYW1lLCBcInBhY2thZ2VzXCIpXG4gIGNvbnN0IHBhY2thZ2VzID0gZnMucmVhZGRpclN5bmMocHJlZml4KS5tYXAoZiA9PiBwcmVmaXggKyBcIi9cIiArIGYpLmZpbHRlcihmID0+IGZzLmxzdGF0U3luYyhmKS5pc0RpcmVjdG9yeSgpIClcbiAgY29uc3QgY2ZnID0ge1xuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tdmFyLXJlcXVpcmVzXG4gICAgLy9wbHVnaW5zOiBbYXV0b0ltcG9ydF0sXG4gICAgdGVzdDoge1xuICAgICAgaW5jbHVkZTogIFtcIi4vdGVzdC8qKi8qLnRlc3Que2pzLG1qcyxjanMsdHMsbXRzLGN0cyxqc3gsdHN4fVwiXSxcbiAgICAgIHJlcG9ydGVyczogXCJ2ZXJib3NlXCIsXG4gICAgICBnbG9iYWxzOiB0cnVlXG4gICAgfSxcbiAgICByZXNvbHZlOiB7XG4gICAgICBhbGlhczogcGFja2FnZXMucmVkdWNlKChhY2MsIGN1cikgPT4geyAvLyB3b3JrYXJvdW5kIGZvciAvUHJlbHVkZSBpc3N1ZVxuICAgICAgYWNjW0pTT04ucGFyc2UoZnMucmVhZEZpbGVTeW5jKGN1ciArIFwiL3BhY2thZ2UuanNvblwiLCBcInV0Zi04XCIpKS5uYW1lXSA9IHBhdGgucmVzb2x2ZShjdXIsIGN1ci5lbmRzV2l0aChcImNvcmVcIikgPyBcImRpc3RcIiA6IFwic3JjXCIpXG4gICAgICByZXR1cm4gYWNjXG4gICAgfSwgeyB9KSAvLyBcIkBlZmZlY3QtYXBwL2NvcmUvUHJlbHVkZVwiOiBwYXRoLmpvaW4oX19kaXJuYW1lLCBcInBhY2thZ2VzL2NvcmUvc3JjL1ByZWx1ZGUuY29kZS50c1wiKVxuICB9XG4gIH1cbiAgY29uc29sZS5sb2coY2ZnKVxuICByZXR1cm4gY2ZnXG59XG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQ0EsU0FBUyxvQkFBb0I7OztBQ0E3QixPQUFPLFVBQVU7QUFDakIsT0FBTyxRQUFRO0FBRmYsSUFBTSxtQ0FBbUM7QUFnQjFCLFNBQVIsV0FBNEIsU0FBa0I7QUFDbkQsUUFBTSxTQUFTLEtBQUssUUFBUSxrQ0FBVyxVQUFVO0FBQ2pELFFBQU0sV0FBVyxHQUFHLFlBQVksTUFBTSxFQUFFLElBQUksT0FBSyxTQUFTLE1BQU0sQ0FBQyxFQUFFLE9BQU8sT0FBSyxHQUFHLFVBQVUsQ0FBQyxFQUFFLFlBQVksQ0FBRTtBQUM3RyxRQUFNLE1BQU07QUFBQTtBQUFBO0FBQUEsSUFHVixNQUFNO0FBQUEsTUFDSixTQUFVLENBQUMsa0RBQWtEO0FBQUEsTUFDN0QsV0FBVztBQUFBLE1BQ1gsU0FBUztBQUFBLElBQ1g7QUFBQSxJQUNBLFNBQVM7QUFBQSxNQUNQLE9BQU8sU0FBUyxPQUFPLENBQUMsS0FBSyxRQUFRO0FBQ3JDLFlBQUksS0FBSyxNQUFNLEdBQUcsYUFBYSxNQUFNLGlCQUFpQixPQUFPLENBQUMsRUFBRSxJQUFJLElBQUksS0FBSyxRQUFRLEtBQUssSUFBSSxTQUFTLE1BQU0sSUFBSSxTQUFTLEtBQUs7QUFDL0gsZUFBTztBQUFBLE1BQ1QsR0FBRyxDQUFFLENBQUM7QUFBQTtBQUFBLElBQ1I7QUFBQSxFQUNBO0FBQ0EsVUFBUSxJQUFJLEdBQUc7QUFDZixTQUFPO0FBQ1Q7OztBRHBDQSxJQUFNQSxvQ0FBbUM7QUFJekMsSUFBTyx3QkFBUSxhQUFhLFdBQVdDLGlDQUFTLENBQUM7IiwKICAibmFtZXMiOiBbIl9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lIiwgIl9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lIl0KfQo=