@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.
- package/.editorconfig +13 -0
- package/.github/workflows/pull-request.yml +37 -0
- package/.github/workflows/release.yml +49 -0
- package/.nvmrc +1 -0
- package/.prettierignore +6 -0
- package/.prettierrc +3 -0
- package/.verdaccio/config.yml +28 -0
- package/.vscode/extensions.json +9 -0
- package/README.md +39 -0
- package/core/README.md +11 -0
- package/core/eslint.config.mjs +23 -0
- package/core/package.json +8 -0
- package/core/project.json +33 -0
- package/core/src/cache/caching-strategy.ts +25 -0
- package/core/src/cache/redis-cache.ts +41 -0
- package/core/src/client/client.ts +39 -0
- package/core/src/index.ts +42 -0
- package/core/src/providers/analytics.provider.ts +10 -0
- package/core/src/providers/base.provider.ts +75 -0
- package/core/src/providers/cart.provider.ts +10 -0
- package/core/src/providers/identity.provider.ts +10 -0
- package/core/src/providers/inventory.provider.ts +11 -0
- package/core/src/providers/price.provider.ts +10 -0
- package/core/src/providers/product.provider.ts +11 -0
- package/core/src/providers/search.provider.ts +12 -0
- package/core/src/schemas/capabilities.schema.ts +13 -0
- package/core/src/schemas/models/analytics.model.ts +7 -0
- package/core/src/schemas/models/base.model.ts +19 -0
- package/core/src/schemas/models/cart.model.ts +17 -0
- package/core/src/schemas/models/currency.model.ts +187 -0
- package/core/src/schemas/models/identifiers.model.ts +45 -0
- package/core/src/schemas/models/identity.model.ts +15 -0
- package/core/src/schemas/models/inventory.model.ts +8 -0
- package/core/src/schemas/models/price.model.ts +17 -0
- package/core/src/schemas/models/product.model.ts +28 -0
- package/core/src/schemas/models/search.model.ts +35 -0
- package/core/src/schemas/mutations/analytics.mutation.ts +22 -0
- package/core/src/schemas/mutations/base.mutation.ts +7 -0
- package/core/src/schemas/mutations/cart.mutation.ts +30 -0
- package/core/src/schemas/mutations/identity.mutation.ts +18 -0
- package/core/src/schemas/mutations/inventory.mutation.ts +5 -0
- package/core/src/schemas/mutations/price.mutation.ts +5 -0
- package/core/src/schemas/mutations/product.mutation.ts +6 -0
- package/core/src/schemas/mutations/search.mutation.ts +5 -0
- package/core/src/schemas/queries/analytics.query.ts +5 -0
- package/core/src/schemas/queries/base.query.ts +7 -0
- package/core/src/schemas/queries/cart.query.ts +12 -0
- package/core/src/schemas/queries/identity.query.ts +10 -0
- package/core/src/schemas/queries/inventory.query.ts +9 -0
- package/core/src/schemas/queries/price.query.ts +12 -0
- package/core/src/schemas/queries/product.query.ts +18 -0
- package/core/src/schemas/queries/search.query.ts +12 -0
- package/core/src/schemas/session.schema.ts +9 -0
- package/core/tsconfig.json +23 -0
- package/core/tsconfig.lib.json +23 -0
- package/core/tsconfig.spec.json +28 -0
- package/eslint.config.mjs +46 -0
- package/examples/angular/e2e/example.spec.ts +9 -0
- package/examples/angular/eslint.config.mjs +41 -0
- package/examples/angular/playwright.config.ts +38 -0
- package/examples/angular/project.json +86 -0
- package/examples/angular/public/favicon.ico +0 -0
- package/examples/angular/src/app/app.component.html +6 -0
- package/examples/angular/src/app/app.component.scss +22 -0
- package/examples/angular/src/app/app.component.ts +14 -0
- package/examples/angular/src/app/app.config.ts +16 -0
- package/examples/angular/src/app/app.routes.ts +25 -0
- package/examples/angular/src/app/cart/cart.component.html +4 -0
- package/examples/angular/src/app/cart/cart.component.scss +14 -0
- package/examples/angular/src/app/cart/cart.component.ts +73 -0
- package/examples/angular/src/app/identity/identity.component.html +6 -0
- package/examples/angular/src/app/identity/identity.component.scss +18 -0
- package/examples/angular/src/app/identity/identity.component.ts +49 -0
- package/examples/angular/src/app/product/product.component.html +14 -0
- package/examples/angular/src/app/product/product.component.scss +11 -0
- package/examples/angular/src/app/product/product.component.ts +42 -0
- package/examples/angular/src/app/search/search.component.html +35 -0
- package/examples/angular/src/app/search/search.component.scss +129 -0
- package/examples/angular/src/app/search/search.component.ts +50 -0
- package/examples/angular/src/app/services/product.service.ts +35 -0
- package/examples/angular/src/app/services/search.service.ts +48 -0
- package/examples/angular/src/app/services/trpc.client.ts +27 -0
- package/examples/angular/src/index.html +13 -0
- package/examples/angular/src/main.ts +7 -0
- package/examples/angular/src/styles.scss +17 -0
- package/examples/angular/src/test-setup.ts +6 -0
- package/examples/angular/tsconfig.app.json +10 -0
- package/examples/angular/tsconfig.editor.json +6 -0
- package/examples/angular/tsconfig.json +32 -0
- package/examples/node/README.md +11 -0
- package/examples/node/eslint.config.mjs +22 -0
- package/examples/node/jest.config.ts +10 -0
- package/examples/node/package.json +10 -0
- package/examples/node/project.json +20 -0
- package/examples/node/src/index.ts +2 -0
- package/examples/node/src/initialize-algolia.spec.ts +29 -0
- package/examples/node/src/initialize-commercetools.spec.ts +31 -0
- package/examples/node/src/initialize-extended-providers.spec.ts +38 -0
- package/examples/node/src/initialize-mixed-providers.spec.ts +36 -0
- package/examples/node/src/providers/custom-algolia-product.provider.ts +18 -0
- package/examples/node/src/schemas/custom-product.schema.ts +8 -0
- package/examples/node/tsconfig.json +23 -0
- package/examples/node/tsconfig.lib.json +10 -0
- package/examples/node/tsconfig.spec.json +15 -0
- package/examples/trpc-node/eslint.config.mjs +3 -0
- package/examples/trpc-node/project.json +61 -0
- package/examples/trpc-node/src/assets/.gitkeep +0 -0
- package/examples/trpc-node/src/main.ts +55 -0
- package/examples/trpc-node/src/router-instance.ts +52 -0
- package/examples/trpc-node/tsconfig.app.json +9 -0
- package/examples/trpc-node/tsconfig.json +13 -0
- package/examples/vue/eslint.config.mjs +24 -0
- package/examples/vue/index.html +13 -0
- package/examples/vue/project.json +8 -0
- package/examples/vue/src/app/App.vue +275 -0
- package/examples/vue/src/main.ts +6 -0
- package/examples/vue/src/styles.scss +9 -0
- package/examples/vue/src/vue-shims.d.ts +5 -0
- package/examples/vue/tsconfig.app.json +14 -0
- package/examples/vue/tsconfig.json +20 -0
- package/examples/vue/vite.config.ts +31 -0
- package/jest.config.ts +6 -0
- package/jest.preset.js +3 -0
- package/migrations.json +11 -0
- package/nx.json +130 -0
- package/package.json +118 -0
- package/project.json +14 -0
- package/providers/algolia/README.md +11 -0
- package/providers/algolia/eslint.config.mjs +22 -0
- package/providers/algolia/jest.config.ts +10 -0
- package/providers/algolia/package.json +9 -0
- package/providers/algolia/project.json +33 -0
- package/providers/algolia/src/core/initialize.ts +20 -0
- package/providers/algolia/src/index.ts +7 -0
- package/providers/algolia/src/providers/product.provider.ts +25 -0
- package/providers/algolia/src/providers/search.provider.ts +125 -0
- package/providers/algolia/src/schema/capabilities.schema.ts +10 -0
- package/providers/algolia/src/schema/configuration.schema.ts +9 -0
- package/providers/algolia/src/schema/search.schema.ts +14 -0
- package/providers/algolia/src/test/product.provider.spec.ts +18 -0
- package/providers/algolia/src/test/search.provider.spec.ts +82 -0
- package/providers/algolia/tsconfig.json +23 -0
- package/providers/algolia/tsconfig.lib.json +10 -0
- package/providers/algolia/tsconfig.spec.json +15 -0
- package/providers/commercetools/README.md +11 -0
- package/providers/commercetools/eslint.config.mjs +22 -0
- package/providers/commercetools/jest.config.ts +10 -0
- package/providers/commercetools/package.json +10 -0
- package/providers/commercetools/project.json +33 -0
- package/providers/commercetools/src/core/client.ts +152 -0
- package/providers/commercetools/src/core/initialize.ts +40 -0
- package/providers/commercetools/src/index.ts +12 -0
- package/providers/commercetools/src/providers/cart.provider.ts +223 -0
- package/providers/commercetools/src/providers/identity.provider.ts +130 -0
- package/providers/commercetools/src/providers/inventory.provider.ts +82 -0
- package/providers/commercetools/src/providers/price.provider.ts +66 -0
- package/providers/commercetools/src/providers/product.provider.ts +90 -0
- package/providers/commercetools/src/providers/search.provider.ts +86 -0
- package/providers/commercetools/src/schema/capabilities.schema.ts +13 -0
- package/providers/commercetools/src/schema/configuration.schema.ts +11 -0
- package/providers/commercetools/src/test/product.provider.spec.ts +20 -0
- package/providers/commercetools/src/test/search.provider.spec.ts +18 -0
- package/providers/commercetools/tsconfig.json +23 -0
- package/providers/commercetools/tsconfig.lib.json +10 -0
- package/providers/commercetools/tsconfig.spec.json +15 -0
- package/providers/fake/README.md +7 -0
- package/providers/fake/eslint.config.mjs +22 -0
- package/providers/fake/package.json +9 -0
- package/providers/fake/project.json +33 -0
- package/providers/fake/src/core/initialize.ts +24 -0
- package/providers/fake/src/index.ts +8 -0
- package/providers/fake/src/providers/identity.provider.ts +91 -0
- package/providers/fake/src/providers/product.provider.ts +73 -0
- package/providers/fake/src/providers/search.provider.ts +142 -0
- package/providers/fake/src/schema/capabilities.schema.ts +10 -0
- package/providers/fake/src/schema/configuration.schema.ts +15 -0
- package/providers/fake/src/utilities/jitter.ts +14 -0
- package/providers/fake/tsconfig.json +20 -0
- package/providers/fake/tsconfig.lib.json +9 -0
- package/providers/posthog/README.md +7 -0
- package/providers/posthog/eslint.config.mjs +22 -0
- package/providers/posthog/package.json +11 -0
- package/providers/posthog/project.json +33 -0
- package/providers/posthog/src/core/initialize.ts +9 -0
- package/providers/posthog/src/index.ts +4 -0
- package/providers/posthog/src/schema/capabilities.schema.ts +8 -0
- package/providers/posthog/src/schema/configuration.schema.ts +8 -0
- package/providers/posthog/tsconfig.json +20 -0
- package/providers/posthog/tsconfig.lib.json +9 -0
- package/trpc/README.md +7 -0
- package/trpc/eslint.config.mjs +19 -0
- package/trpc/package.json +13 -0
- package/trpc/project.json +31 -0
- package/trpc/src/index.ts +64 -0
- package/trpc/tsconfig.json +13 -0
- package/trpc/tsconfig.lib.json +9 -0
- 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
|
package/.prettierignore
ADDED
package/.prettierrc
ADDED
|
@@ -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
|
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,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,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,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>;
|