@reactionary/source 0.0.27

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 (197) hide show
  1. package/.editorconfig +13 -0
  2. package/.github/workflows/pull-request.yml +37 -0
  3. package/.github/workflows/release.yml +49 -0
  4. package/.nvmrc +1 -0
  5. package/.prettierignore +6 -0
  6. package/.prettierrc +3 -0
  7. package/.verdaccio/config.yml +28 -0
  8. package/.vscode/extensions.json +9 -0
  9. package/README.md +39 -0
  10. package/core/README.md +11 -0
  11. package/core/eslint.config.mjs +23 -0
  12. package/core/package.json +8 -0
  13. package/core/project.json +33 -0
  14. package/core/src/cache/caching-strategy.ts +25 -0
  15. package/core/src/cache/redis-cache.ts +41 -0
  16. package/core/src/client/client.ts +39 -0
  17. package/core/src/index.ts +42 -0
  18. package/core/src/providers/analytics.provider.ts +10 -0
  19. package/core/src/providers/base.provider.ts +75 -0
  20. package/core/src/providers/cart.provider.ts +10 -0
  21. package/core/src/providers/identity.provider.ts +10 -0
  22. package/core/src/providers/inventory.provider.ts +11 -0
  23. package/core/src/providers/price.provider.ts +10 -0
  24. package/core/src/providers/product.provider.ts +11 -0
  25. package/core/src/providers/search.provider.ts +12 -0
  26. package/core/src/schemas/capabilities.schema.ts +13 -0
  27. package/core/src/schemas/models/analytics.model.ts +7 -0
  28. package/core/src/schemas/models/base.model.ts +19 -0
  29. package/core/src/schemas/models/cart.model.ts +17 -0
  30. package/core/src/schemas/models/currency.model.ts +187 -0
  31. package/core/src/schemas/models/identifiers.model.ts +45 -0
  32. package/core/src/schemas/models/identity.model.ts +15 -0
  33. package/core/src/schemas/models/inventory.model.ts +8 -0
  34. package/core/src/schemas/models/price.model.ts +17 -0
  35. package/core/src/schemas/models/product.model.ts +28 -0
  36. package/core/src/schemas/models/search.model.ts +35 -0
  37. package/core/src/schemas/mutations/analytics.mutation.ts +22 -0
  38. package/core/src/schemas/mutations/base.mutation.ts +7 -0
  39. package/core/src/schemas/mutations/cart.mutation.ts +30 -0
  40. package/core/src/schemas/mutations/identity.mutation.ts +18 -0
  41. package/core/src/schemas/mutations/inventory.mutation.ts +5 -0
  42. package/core/src/schemas/mutations/price.mutation.ts +5 -0
  43. package/core/src/schemas/mutations/product.mutation.ts +6 -0
  44. package/core/src/schemas/mutations/search.mutation.ts +5 -0
  45. package/core/src/schemas/queries/analytics.query.ts +5 -0
  46. package/core/src/schemas/queries/base.query.ts +7 -0
  47. package/core/src/schemas/queries/cart.query.ts +12 -0
  48. package/core/src/schemas/queries/identity.query.ts +10 -0
  49. package/core/src/schemas/queries/inventory.query.ts +9 -0
  50. package/core/src/schemas/queries/price.query.ts +12 -0
  51. package/core/src/schemas/queries/product.query.ts +18 -0
  52. package/core/src/schemas/queries/search.query.ts +12 -0
  53. package/core/src/schemas/session.schema.ts +9 -0
  54. package/core/tsconfig.json +23 -0
  55. package/core/tsconfig.lib.json +23 -0
  56. package/core/tsconfig.spec.json +28 -0
  57. package/eslint.config.mjs +46 -0
  58. package/examples/angular/e2e/example.spec.ts +9 -0
  59. package/examples/angular/eslint.config.mjs +41 -0
  60. package/examples/angular/playwright.config.ts +38 -0
  61. package/examples/angular/project.json +86 -0
  62. package/examples/angular/public/favicon.ico +0 -0
  63. package/examples/angular/src/app/app.component.html +6 -0
  64. package/examples/angular/src/app/app.component.scss +22 -0
  65. package/examples/angular/src/app/app.component.ts +14 -0
  66. package/examples/angular/src/app/app.config.ts +16 -0
  67. package/examples/angular/src/app/app.routes.ts +25 -0
  68. package/examples/angular/src/app/cart/cart.component.html +4 -0
  69. package/examples/angular/src/app/cart/cart.component.scss +14 -0
  70. package/examples/angular/src/app/cart/cart.component.ts +73 -0
  71. package/examples/angular/src/app/identity/identity.component.html +6 -0
  72. package/examples/angular/src/app/identity/identity.component.scss +18 -0
  73. package/examples/angular/src/app/identity/identity.component.ts +49 -0
  74. package/examples/angular/src/app/product/product.component.html +14 -0
  75. package/examples/angular/src/app/product/product.component.scss +11 -0
  76. package/examples/angular/src/app/product/product.component.ts +42 -0
  77. package/examples/angular/src/app/search/search.component.html +35 -0
  78. package/examples/angular/src/app/search/search.component.scss +129 -0
  79. package/examples/angular/src/app/search/search.component.ts +50 -0
  80. package/examples/angular/src/app/services/product.service.ts +35 -0
  81. package/examples/angular/src/app/services/search.service.ts +48 -0
  82. package/examples/angular/src/app/services/trpc.client.ts +27 -0
  83. package/examples/angular/src/index.html +13 -0
  84. package/examples/angular/src/main.ts +7 -0
  85. package/examples/angular/src/styles.scss +17 -0
  86. package/examples/angular/src/test-setup.ts +6 -0
  87. package/examples/angular/tsconfig.app.json +10 -0
  88. package/examples/angular/tsconfig.editor.json +6 -0
  89. package/examples/angular/tsconfig.json +32 -0
  90. package/examples/node/README.md +11 -0
  91. package/examples/node/eslint.config.mjs +22 -0
  92. package/examples/node/jest.config.ts +10 -0
  93. package/examples/node/package.json +10 -0
  94. package/examples/node/project.json +20 -0
  95. package/examples/node/src/index.ts +2 -0
  96. package/examples/node/src/initialize-algolia.spec.ts +29 -0
  97. package/examples/node/src/initialize-commercetools.spec.ts +31 -0
  98. package/examples/node/src/initialize-extended-providers.spec.ts +38 -0
  99. package/examples/node/src/initialize-mixed-providers.spec.ts +36 -0
  100. package/examples/node/src/providers/custom-algolia-product.provider.ts +18 -0
  101. package/examples/node/src/schemas/custom-product.schema.ts +8 -0
  102. package/examples/node/tsconfig.json +23 -0
  103. package/examples/node/tsconfig.lib.json +10 -0
  104. package/examples/node/tsconfig.spec.json +15 -0
  105. package/examples/trpc-node/eslint.config.mjs +3 -0
  106. package/examples/trpc-node/project.json +61 -0
  107. package/examples/trpc-node/src/assets/.gitkeep +0 -0
  108. package/examples/trpc-node/src/main.ts +55 -0
  109. package/examples/trpc-node/src/router-instance.ts +52 -0
  110. package/examples/trpc-node/tsconfig.app.json +9 -0
  111. package/examples/trpc-node/tsconfig.json +13 -0
  112. package/examples/vue/eslint.config.mjs +24 -0
  113. package/examples/vue/index.html +13 -0
  114. package/examples/vue/project.json +8 -0
  115. package/examples/vue/src/app/App.vue +275 -0
  116. package/examples/vue/src/main.ts +6 -0
  117. package/examples/vue/src/styles.scss +9 -0
  118. package/examples/vue/src/vue-shims.d.ts +5 -0
  119. package/examples/vue/tsconfig.app.json +14 -0
  120. package/examples/vue/tsconfig.json +20 -0
  121. package/examples/vue/vite.config.ts +31 -0
  122. package/jest.config.ts +6 -0
  123. package/jest.preset.js +3 -0
  124. package/migrations.json +11 -0
  125. package/nx.json +130 -0
  126. package/package.json +118 -0
  127. package/project.json +14 -0
  128. package/providers/algolia/README.md +11 -0
  129. package/providers/algolia/eslint.config.mjs +22 -0
  130. package/providers/algolia/jest.config.ts +10 -0
  131. package/providers/algolia/package.json +9 -0
  132. package/providers/algolia/project.json +33 -0
  133. package/providers/algolia/src/core/initialize.ts +20 -0
  134. package/providers/algolia/src/index.ts +7 -0
  135. package/providers/algolia/src/providers/product.provider.ts +25 -0
  136. package/providers/algolia/src/providers/search.provider.ts +125 -0
  137. package/providers/algolia/src/schema/capabilities.schema.ts +10 -0
  138. package/providers/algolia/src/schema/configuration.schema.ts +9 -0
  139. package/providers/algolia/src/schema/search.schema.ts +14 -0
  140. package/providers/algolia/src/test/product.provider.spec.ts +18 -0
  141. package/providers/algolia/src/test/search.provider.spec.ts +82 -0
  142. package/providers/algolia/tsconfig.json +23 -0
  143. package/providers/algolia/tsconfig.lib.json +10 -0
  144. package/providers/algolia/tsconfig.spec.json +15 -0
  145. package/providers/commercetools/README.md +11 -0
  146. package/providers/commercetools/eslint.config.mjs +22 -0
  147. package/providers/commercetools/jest.config.ts +10 -0
  148. package/providers/commercetools/package.json +10 -0
  149. package/providers/commercetools/project.json +33 -0
  150. package/providers/commercetools/src/core/client.ts +152 -0
  151. package/providers/commercetools/src/core/initialize.ts +40 -0
  152. package/providers/commercetools/src/index.ts +12 -0
  153. package/providers/commercetools/src/providers/cart.provider.ts +223 -0
  154. package/providers/commercetools/src/providers/identity.provider.ts +130 -0
  155. package/providers/commercetools/src/providers/inventory.provider.ts +82 -0
  156. package/providers/commercetools/src/providers/price.provider.ts +66 -0
  157. package/providers/commercetools/src/providers/product.provider.ts +90 -0
  158. package/providers/commercetools/src/providers/search.provider.ts +86 -0
  159. package/providers/commercetools/src/schema/capabilities.schema.ts +13 -0
  160. package/providers/commercetools/src/schema/configuration.schema.ts +11 -0
  161. package/providers/commercetools/src/test/product.provider.spec.ts +20 -0
  162. package/providers/commercetools/src/test/search.provider.spec.ts +18 -0
  163. package/providers/commercetools/tsconfig.json +23 -0
  164. package/providers/commercetools/tsconfig.lib.json +10 -0
  165. package/providers/commercetools/tsconfig.spec.json +15 -0
  166. package/providers/fake/README.md +7 -0
  167. package/providers/fake/eslint.config.mjs +22 -0
  168. package/providers/fake/package.json +9 -0
  169. package/providers/fake/project.json +33 -0
  170. package/providers/fake/src/core/initialize.ts +24 -0
  171. package/providers/fake/src/index.ts +8 -0
  172. package/providers/fake/src/providers/identity.provider.ts +91 -0
  173. package/providers/fake/src/providers/product.provider.ts +73 -0
  174. package/providers/fake/src/providers/search.provider.ts +142 -0
  175. package/providers/fake/src/schema/capabilities.schema.ts +10 -0
  176. package/providers/fake/src/schema/configuration.schema.ts +15 -0
  177. package/providers/fake/src/utilities/jitter.ts +14 -0
  178. package/providers/fake/tsconfig.json +20 -0
  179. package/providers/fake/tsconfig.lib.json +9 -0
  180. package/providers/posthog/README.md +7 -0
  181. package/providers/posthog/eslint.config.mjs +22 -0
  182. package/providers/posthog/package.json +11 -0
  183. package/providers/posthog/project.json +33 -0
  184. package/providers/posthog/src/core/initialize.ts +9 -0
  185. package/providers/posthog/src/index.ts +4 -0
  186. package/providers/posthog/src/schema/capabilities.schema.ts +8 -0
  187. package/providers/posthog/src/schema/configuration.schema.ts +8 -0
  188. package/providers/posthog/tsconfig.json +20 -0
  189. package/providers/posthog/tsconfig.lib.json +9 -0
  190. package/trpc/README.md +7 -0
  191. package/trpc/eslint.config.mjs +19 -0
  192. package/trpc/package.json +13 -0
  193. package/trpc/project.json +31 -0
  194. package/trpc/src/index.ts +64 -0
  195. package/trpc/tsconfig.json +13 -0
  196. package/trpc/tsconfig.lib.json +9 -0
  197. package/tsconfig.base.json +30 -0
package/.editorconfig ADDED
@@ -0,0 +1,13 @@
1
+ # Editor configuration, see http://editorconfig.org
2
+ root = true
3
+
4
+ [*]
5
+ charset = utf-8
6
+ indent_style = space
7
+ indent_size = 2
8
+ insert_final_newline = true
9
+ trim_trailing_whitespace = true
10
+
11
+ [*.md]
12
+ max_line_length = off
13
+ trim_trailing_whitespace = false
@@ -0,0 +1,37 @@
1
+ name: pull request
2
+
3
+ on:
4
+ pull_request:
5
+ merge_group:
6
+
7
+ permissions:
8
+ actions: read
9
+ contents: read
10
+ pull-requests: read
11
+
12
+ env:
13
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
14
+
15
+ jobs:
16
+ ci:
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ with:
21
+ filter: tree:0
22
+ fetch-depth: 0
23
+
24
+ - uses: pnpm/action-setup@v4
25
+
26
+ - run: pnpm dlx nx-cloud start-ci-run --distribute-on="3 linux-medium-js" --stop-agents-after="build"
27
+
28
+ - uses: actions/setup-node@v4
29
+ with:
30
+ node-version: 23
31
+ cache: 'pnpm'
32
+
33
+ - run: pnpm install
34
+
35
+ - uses: nrwl/nx-set-shas@v4
36
+
37
+ - run: pnpm exec nx affected -t lint build
@@ -0,0 +1,49 @@
1
+ name: release
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ main
7
+
8
+ permissions:
9
+ actions: read
10
+ contents: write
11
+ pull-requests: read
12
+ id-token: write
13
+
14
+ env:
15
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
16
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
17
+
18
+ jobs:
19
+ release:
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+ with:
24
+ filter: tree:0
25
+ fetch-depth: 0
26
+
27
+ - uses: pnpm/action-setup@v4
28
+
29
+ - uses: actions/setup-node@v4
30
+ with:
31
+ node-version: 23
32
+ cache: 'pnpm'
33
+ registry-url: https://registry.npmjs.org/
34
+
35
+ - run: pnpm install
36
+
37
+ - run: |
38
+ git config --global user.email "martin.rogne@solteq.com"
39
+ git config --global user.name "Martin Rogne"
40
+ pnpm exec nx release patch --yes -- --no-agents
41
+ env:
42
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }}
43
+ NPM_CONFIG_PROVENANCE: false
44
+ NPM_CONFIG_ACCESS: public
45
+
46
+
47
+
48
+
49
+
package/.nvmrc ADDED
@@ -0,0 +1 @@
1
+ v23.7.0
@@ -0,0 +1,6 @@
1
+ # Add files here to ignore them from prettier formatting
2
+ /dist
3
+ /coverage
4
+ /.nx/cache
5
+ /.nx/workspace-data
6
+ .angular
package/.prettierrc ADDED
@@ -0,0 +1,3 @@
1
+ {
2
+ "singleQuote": true
3
+ }
@@ -0,0 +1,28 @@
1
+ # path to a directory with all packages
2
+ storage: ../tmp/local-registry/storage
3
+
4
+ # a list of other known repositories we can talk to
5
+ uplinks:
6
+ npmjs:
7
+ url: https://registry.npmjs.org/
8
+ maxage: 60m
9
+
10
+ packages:
11
+ '**':
12
+ # give all users (including non-authenticated users) full access
13
+ # because it is a local registry
14
+ access: $all
15
+ publish: $all
16
+ unpublish: $all
17
+
18
+ # if package is not available locally, proxy requests to npm registry
19
+ proxy: npmjs
20
+
21
+ # log settings
22
+ log:
23
+ type: stdout
24
+ format: pretty
25
+ level: warn
26
+
27
+ publish:
28
+ allow_offline: true # set offline to true to allow publish offline
@@ -0,0 +1,9 @@
1
+ {
2
+ "recommendations": [
3
+ "nrwl.angular-console",
4
+ "esbenp.prettier-vscode",
5
+ "dbaeumer.vscode-eslint",
6
+ "firsttris.vscode-jest-runner",
7
+ "ms-playwright.playwright"
8
+ ]
9
+ }
package/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # Reactionary
2
+
3
+ Reactionary is a framework-agnostic client library for standardized data access, building upon the previous learnings from Perpendicular. It is decidedly opinionated. Compared to Perpendicular it:
4
+
5
+ - it favors keeping the providers on the server in order to:
6
+ - keep the client bundle minimal for performance.
7
+ - allow for cross transactional caching.
8
+ - standardize observability.
9
+ - control access, allowing for session features like rate limiting.
10
+ - it favors serializable, parseable domain models because it:
11
+ - allows for caching and state transfer.
12
+ - allows for extensible, typesafe data at runtime.
13
+
14
+ ## Contributing
15
+
16
+ ### Running locally
17
+
18
+ The includes examples generally require `.env` to be configured with the relevant API keys. We can likely create a setup for this in Vault, for easy bootstrapping.
19
+
20
+ ### Pull requests
21
+
22
+ For new features, branch from `main` and create a pull request towards `main` upon feature completion. Please observe the following guidelines:
23
+
24
+ - Preserve a linear history. This means rebasing on `main`, rather than merging. PR's containing merge commits should be considered unmergeable.
25
+ - Observe [https://www.conventionalcommits.org/en/v1.0.0/#summary][conventional commit message guidelines] for the rebased pull request commits.
26
+ - Ensure that the PR is linked to an issue.
27
+
28
+ ## Glossary
29
+
30
+ The following is a short list of commonly used terms and phrases, to keep guessing to a minimum.
31
+
32
+ - *Provider:* a backend service providing an API that can be consumed. HCL or Commercetools would be examples of ecom providers, while Algolia would be an example of a search provider.
33
+ - *Capability:* a capability (or more fully, a business capability) is a discrete area of functionality that may be provided to the consuming party. An example would be *Cart*, providing a domain model and a set of discrete operations that can be performed on it, like adding a product or removing a product.
34
+ - *Gateway:* a serverside process encapsulating a set of capabilities. This could be a trpc router, or it could be react server. The purpose here is to ensure that all of the dependencies and environmental configuration stays on the server, rather than the client.
35
+ - *Client:* the client facade to the gateway. In trpc this is would be the trpc client. It can be bundled into client-side rendering code.
36
+ - *Observability:* means of providing insights into the workings of the system, in a production context. This is distinct from analytics in that it provides information on the workings of the *system* rather than the workings of the *user*. OTEL (opentelemetry) provides a standardized specification for this in the form of *traces* and *metrics*.
37
+ - *Fake:* an implementation that provides a functional response, but with in a limited capacity. A fake provider may, for example, provide *Cart* functionality, but only store it in-memory and throw it away on a whim. As such it can be used for prototyping, but never for a production scenario.
38
+ - *Product Analytics:* structured analytics that relate to how the the product is being used.
39
+ - *Microsite:* an application of a limited scope. It may focus solely on this limited functionality, making it ideal for demonstration purposes.
package/core/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # core
2
+
3
+ This library was generated with [Nx](https://nx.dev).
4
+
5
+ ## Building
6
+
7
+ Run `nx build core` to build the library.
8
+
9
+ ## Running unit tests
10
+
11
+ Run `nx test core` to execute the unit tests via [Vitest](https://vitest.dev/).
@@ -0,0 +1,23 @@
1
+ import baseConfig from '../eslint.config.mjs';
2
+
3
+ export default [
4
+ ...baseConfig,
5
+ {
6
+ files: ['**/*.json'],
7
+ rules: {
8
+ '@nx/dependency-checks': [
9
+ 'error',
10
+ {
11
+ ignoredFiles: [
12
+ '{projectRoot}/eslint.config.{js,cjs,mjs}',
13
+ '{projectRoot}/esbuild.config.{js,ts,mjs,mts}',
14
+ '{projectRoot}/vite.config.{js,ts,mjs,mts}',
15
+ ],
16
+ },
17
+ ],
18
+ },
19
+ languageOptions: {
20
+ parser: await import('jsonc-eslint-parser'),
21
+ },
22
+ },
23
+ ];
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "@reactionary/core",
3
+ "version": "0.0.1",
4
+ "dependencies": {
5
+ "zod": "4.0.0-beta.20250430T185432",
6
+ "@upstash/redis": "^1.34.9"
7
+ }
8
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "core",
3
+ "$schema": "../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "core/src",
5
+ "projectType": "library",
6
+ "release": {
7
+ "version": {
8
+ "currentVersionResolver": "git-tag",
9
+ "fallbackCurrentVersionResolver": "disk",
10
+ "preserveLocalDependencyProtocols": false,
11
+ "manifestRootsToUpdate": ["dist/{projectRoot}"]
12
+ }
13
+ },
14
+ "tags": [],
15
+ "targets": {
16
+ "build": {
17
+ "executor": "@nx/esbuild:esbuild",
18
+ "outputs": ["{options.outputPath}"],
19
+ "options": {
20
+ "outputPath": "dist/core",
21
+ "main": "core/src/index.ts",
22
+ "tsConfig": "core/tsconfig.lib.json",
23
+ "assets": ["core/*.md"],
24
+ "format": ["esm"]
25
+ }
26
+ },
27
+ "nx-release-publish": {
28
+ "options": {
29
+ "packageRoot": "dist/{projectRoot}"
30
+ }
31
+ }
32
+ }
33
+ }
@@ -0,0 +1,25 @@
1
+ import { BaseQuery } from "../schemas/queries/base.query";
2
+ import { InventoryQuery } from "../schemas/queries/inventory.query";
3
+ import { Session } from "../schemas/session.schema";
4
+
5
+ export interface CachingStrategyEvaluation {
6
+ key: string;
7
+ cacheDurationInSeconds: number;
8
+ canCache: boolean;
9
+ }
10
+
11
+ export interface CachingStrategy {
12
+ get(query: BaseQuery, session: Session): CachingStrategyEvaluation;
13
+ }
14
+
15
+ export class BaseCachingStrategy implements CachingStrategy {
16
+ public get(query: BaseQuery, session: Session): CachingStrategyEvaluation {
17
+ const q = query as InventoryQuery;
18
+
19
+ return {
20
+ key: q.sku,
21
+ cacheDurationInSeconds: 300,
22
+ canCache: true
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,41 @@
1
+ import { Redis } from '@upstash/redis';
2
+ import { CachingStrategy } from './caching-strategy';
3
+ import { BaseQuery } from '../schemas/queries/base.query';
4
+ import { Session } from '../schemas/session.schema';
5
+ import { BaseModel } from '../schemas/models/base.model';
6
+ import z from 'zod';
7
+
8
+ export class RedisCache {
9
+ protected strategy: CachingStrategy;
10
+ protected redis: Redis;
11
+
12
+ constructor(strategy: CachingStrategy) {
13
+ this.strategy = strategy;
14
+ this.redis = Redis.fromEnv();
15
+ }
16
+
17
+ public async get<T extends BaseModel>(query: BaseQuery, session: Session, schema: z.ZodType<T>): Promise<T | null> {
18
+ let result = null;
19
+
20
+ const cacheInformation = this.strategy.get(query, session);
21
+
22
+ if (cacheInformation.canCache && cacheInformation.key) {
23
+ const unvalidated = await this.redis.get(cacheInformation.key);
24
+ const parsed = schema.safeParse(unvalidated);
25
+
26
+ if (parsed.success) {
27
+ result = parsed.data;
28
+ }
29
+ }
30
+
31
+ return result;
32
+ }
33
+
34
+ public put(query: BaseQuery, session: Session, value: unknown): void {
35
+ const cacheInformation = this.strategy.get(query, session);
36
+
37
+ if (cacheInformation.canCache && cacheInformation.key) {
38
+ this.redis.set(cacheInformation.key, value, { ex: cacheInformation.cacheDurationInSeconds });
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,39 @@
1
+ import { AnalyticsProvider } from "../providers/analytics.provider";
2
+ import { ProductProvider } from "../providers/product.provider";
3
+ import { SearchProvider } from "../providers/search.provider";
4
+ import { IdentityProvider } from '../providers/identity.provider';
5
+ import { CartProvider } from "../providers/cart.provider";
6
+ import { PriceProvider } from "../providers/price.provider";
7
+ import { InventoryProvider } from "../providers/inventory.provider";
8
+
9
+ export interface Client {
10
+ product: ProductProvider,
11
+ search: SearchProvider,
12
+ identity: IdentityProvider,
13
+ cache: Cache,
14
+ cart: CartProvider,
15
+ analytics: Array<AnalyticsProvider>,
16
+ price: PriceProvider,
17
+ inventory: InventoryProvider
18
+ }
19
+
20
+ export function buildClient<T extends Partial<Client>>(providers: Array<T>): Required<T> {
21
+ let client = { } as Required<T>;
22
+
23
+ const mergedAnalytics = [];
24
+
25
+ for (const provider of providers) {
26
+ client = {
27
+ ...client,
28
+ ...provider
29
+ }
30
+
31
+ if (provider.analytics) {
32
+ mergedAnalytics.push(...provider.analytics);
33
+ }
34
+ }
35
+
36
+ client.analytics = mergedAnalytics;
37
+
38
+ return client satisfies T;
39
+ }
@@ -0,0 +1,42 @@
1
+ export * from './cache/caching-strategy';
2
+ export * from './cache/redis-cache';
3
+
4
+ export * from './client/client';
5
+
6
+ export * from './providers/analytics.provider';
7
+ export * from './providers/base.provider';
8
+ export * from './providers/cart.provider';
9
+ export * from './providers/identity.provider';
10
+ export * from './providers/inventory.provider';
11
+ export * from './providers/price.provider';
12
+ export * from './providers/product.provider';
13
+ export * from './providers/search.provider';
14
+
15
+ export * from './schemas/capabilities.schema';
16
+ export * from './schemas/session.schema';
17
+
18
+ export * from './schemas/models/base.model';
19
+ export * from './schemas/models/cart.model';
20
+ export * from './schemas/models/currency.model';
21
+ export * from './schemas/models/identifiers.model';
22
+ export * from './schemas/models/identity.model';
23
+ export * from './schemas/models/inventory.model';
24
+ export * from './schemas/models/price.model';
25
+ export * from './schemas/models/product.model';
26
+ export * from './schemas/models/search.model';
27
+
28
+ export * from './schemas/mutations/base.mutation';
29
+ export * from './schemas/mutations/cart.mutation';
30
+ export * from './schemas/mutations/identity.mutation';
31
+ export * from './schemas/mutations/inventory.mutation';
32
+ export * from './schemas/mutations/price.mutation';
33
+ export * from './schemas/mutations/product.mutation';
34
+ export * from './schemas/mutations/search.mutation';
35
+
36
+ export * from './schemas/queries/base.query';
37
+ export * from './schemas/queries/cart.query';
38
+ export * from './schemas/queries/identity.query';
39
+ export * from './schemas/queries/inventory.query';
40
+ export * from './schemas/queries/price.query';
41
+ export * from './schemas/queries/product.query';
42
+ export * from './schemas/queries/search.query';
@@ -0,0 +1,10 @@
1
+ import { AnalyticsEvent } from '../schemas/models/analytics.model';
2
+ import { AnalyticsMutation } from '../schemas/mutations/analytics.mutation';
3
+ import { AnalyticsQuery } from '../schemas/queries/analytics.query';
4
+ import { BaseProvider } from './base.provider';
5
+
6
+ export abstract class AnalyticsProvider<
7
+ T extends AnalyticsEvent = AnalyticsEvent,
8
+ Q extends AnalyticsQuery = AnalyticsQuery,
9
+ M extends AnalyticsMutation = AnalyticsMutation
10
+ > extends BaseProvider<T, Q, M> {}
@@ -0,0 +1,75 @@
1
+ import { z } from 'zod';
2
+ import { Session } from '../schemas/session.schema';
3
+ import { BaseQuery } from '../schemas/queries/base.query';
4
+ import { BaseMutation } from '../schemas/mutations/base.mutation';
5
+ import { BaseModel } from '../schemas/models/base.model';
6
+
7
+ /**
8
+ * Base capability provider, responsible for mutations (changes) and queries (fetches)
9
+ * for a given business object domain.
10
+ */
11
+ export abstract class BaseProvider<
12
+ T extends BaseModel = BaseModel,
13
+ Q extends BaseQuery = BaseQuery,
14
+ M extends BaseMutation = BaseMutation
15
+ > {
16
+ constructor(public readonly schema: z.ZodType<T>, public readonly querySchema: z.ZodType<Q, Q>, public readonly mutationSchema: z.ZodType<M, M>) {}
17
+
18
+ /**
19
+ * Validates that the final domain model constructed by the provider
20
+ * fulfills the schema as defined. This will throw an exception.
21
+ */
22
+ protected assert(value: T) {
23
+ return this.schema.parse(value);
24
+ }
25
+
26
+ /**
27
+ * Creates a new model entity based on the schema defaults.
28
+ */
29
+ protected newModel(): T {
30
+ return this.schema.parse({});
31
+ }
32
+
33
+ /**
34
+ * Retrieves a set of entities matching the list of queries. The size of
35
+ * the resulting list WILL always match the size of the query list. The
36
+ * result list will never contain nulls or undefined. The order
37
+ * of the results will match the order of the queries.
38
+ */
39
+ public async query(queries: Q[], session: Session): Promise<T[]> {
40
+ const results = await this.fetch(queries, session);
41
+
42
+ for (const result of results) {
43
+ this.assert(result);
44
+ }
45
+
46
+ return results;
47
+ }
48
+
49
+ /**
50
+ * Executes the listed mutations in order and returns the final state
51
+ * resulting from that set of operations.
52
+ */
53
+ public async mutate(mutations: M[], session: Session): Promise<T> {
54
+ const result = await this.process(mutations, session);
55
+
56
+ this.assert(result);
57
+
58
+ return result;
59
+ }
60
+
61
+ /**
62
+ * The internal extension point for providers implementating query
63
+ * capabilities.
64
+ */
65
+ protected abstract fetch(queries: Q[], session: Session): Promise<T[]>;
66
+
67
+ /**
68
+ * The internal extension point for providers implementing mutation
69
+ * capabilities.
70
+ */
71
+ protected abstract process(
72
+ mutations: M[],
73
+ session: Session
74
+ ): Promise<T>;
75
+ }
@@ -0,0 +1,10 @@
1
+ import { CartQuery } from "../schemas/queries/cart.query";
2
+ import { CartMutation } from "../schemas/mutations/cart.mutation";
3
+ import { BaseProvider } from "./base.provider";
4
+ import { Cart } from "../schemas/models/cart.model";
5
+
6
+ export abstract class CartProvider<
7
+ T extends Cart = Cart,
8
+ Q extends CartQuery = CartQuery,
9
+ M extends CartMutation = CartMutation
10
+ > extends BaseProvider<T, Q, M> {}
@@ -0,0 +1,10 @@
1
+ import { Identity } from "../schemas/models/identity.model";
2
+ import { IdentityQuery } from "../schemas/queries/identity.query";
3
+ import { IdentityMutation } from "../schemas/mutations/identity.mutation";
4
+ import { BaseProvider } from "./base.provider";
5
+
6
+ export abstract class IdentityProvider<
7
+ T extends Identity = Identity,
8
+ Q extends IdentityQuery = IdentityQuery,
9
+ M extends IdentityMutation = IdentityMutation
10
+ > extends BaseProvider<T, Q, M> {}
@@ -0,0 +1,11 @@
1
+ import z from 'zod';
2
+ import { Inventory } from '../schemas/models/inventory.model';
3
+ import { InventoryQuery } from '../schemas/queries/inventory.query';
4
+ import { InventoryMutation } from '../schemas/mutations/inventory.mutation';
5
+ import { BaseProvider } from './base.provider';
6
+
7
+ export abstract class InventoryProvider<
8
+ T extends Inventory = Inventory,
9
+ Q extends InventoryQuery = InventoryQuery,
10
+ M extends InventoryMutation = InventoryMutation
11
+ > extends BaseProvider<T, Q, M> {}
@@ -0,0 +1,10 @@
1
+ import { Price } from '../schemas/models/price.model';
2
+ import { PriceMutation } from '../schemas/mutations/price.mutation';
3
+ import { PriceQuery } from '../schemas/queries/price.query';
4
+ import { BaseProvider } from './base.provider';
5
+
6
+ export abstract class PriceProvider<
7
+ T extends Price = Price,
8
+ Q extends PriceQuery = PriceQuery,
9
+ M extends PriceMutation = PriceMutation
10
+ > extends BaseProvider<T, Q, M> {}
@@ -0,0 +1,11 @@
1
+ import { Product } from '../schemas/models/product.model';
2
+ import { ProductMutation } from '../schemas/mutations/product.mutation';
3
+ import { ProductQuery } from '../schemas/queries/product.query';
4
+ import { BaseProvider } from './base.provider';
5
+
6
+ export abstract class ProductProvider<
7
+ T extends Product = Product,
8
+ Q extends ProductQuery = ProductQuery,
9
+ M extends ProductMutation = ProductMutation
10
+ > extends BaseProvider<T, Q, M> {
11
+ }
@@ -0,0 +1,12 @@
1
+ import { SearchResult } from '../schemas/models/search.model';
2
+ import { SearchQuery } from '../schemas/queries/search.query';
3
+ import { SearchMutation } from '../schemas/mutations/search.mutation';
4
+ import { BaseProvider } from './base.provider';
5
+
6
+ export abstract class SearchProvider<
7
+ T extends SearchResult = SearchResult,
8
+ Q extends SearchQuery = SearchQuery,
9
+ M extends SearchMutation = SearchMutation
10
+ > extends BaseProvider<T, Q, M> {}
11
+
12
+
@@ -0,0 +1,13 @@
1
+ import { z } from 'zod';
2
+
3
+ export const CapabilitiesSchema = z.looseInterface({
4
+ product: z.boolean(),
5
+ search: z.boolean(),
6
+ analytics: z.boolean(),
7
+ identity: z.boolean(),
8
+ cart: z.boolean(),
9
+ inventory: z.boolean(),
10
+ price: z.boolean()
11
+ });
12
+
13
+ export type Capabilities = z.infer<typeof CapabilitiesSchema>;
@@ -0,0 +1,7 @@
1
+ import { z } from 'zod';
2
+ import { BaseModelSchema } from './base.model';
3
+
4
+ export const AnalyticsEventSchema = BaseModelSchema.extend({
5
+ });
6
+
7
+ export type AnalyticsEvent = z.infer<typeof AnalyticsEventSchema>;
@@ -0,0 +1,19 @@
1
+ import { z } from 'zod';
2
+
3
+ export const CacheInformationSchema = z.looseInterface({
4
+ hit: z.boolean().default(false),
5
+ key: z.string().default('')
6
+ })
7
+
8
+ export const MetaSchema = z.looseInterface({
9
+ cache: CacheInformationSchema.default(() => CacheInformationSchema.parse({})),
10
+ placeholder: z.boolean().default(false).describe('Whether or not the entity exists in a remote system, or is a default placeholder.')
11
+ });
12
+
13
+ export const BaseModelSchema = z.looseInterface({
14
+ meta: MetaSchema.default(() => MetaSchema.parse({}))
15
+ });
16
+
17
+ export type CacheInformation = z.infer<typeof CacheInformationSchema>;
18
+ export type Meta = z.infer<typeof MetaSchema>;
19
+ export type BaseModel = z.infer<typeof BaseModelSchema>;
@@ -0,0 +1,17 @@
1
+ import { z } from 'zod';
2
+ import { CartIdentifierSchema, CartItemIdentifierSchema, ProductIdentifierSchema } from '../models/identifiers.model';
3
+ import { BaseModelSchema } from './base.model';
4
+
5
+ export const CartItemSchema = z.looseInterface({
6
+ identifier: CartItemIdentifierSchema.default(() => CartItemIdentifierSchema.parse({})),
7
+ product: ProductIdentifierSchema.default(() => ProductIdentifierSchema.parse({})),
8
+ quantity: z.number().default(0)
9
+ });
10
+
11
+ export const CartSchema = BaseModelSchema.extend({
12
+ identifier: CartIdentifierSchema.default(() => CartIdentifierSchema.parse({})),
13
+ items: z.array(CartItemSchema).default(() => [])
14
+ });
15
+
16
+ export type CartItem = z.infer<typeof CartItemSchema>;
17
+ export type Cart = z.infer<typeof CartSchema>;