@reactionary/source 0.0.41 → 0.0.48
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/.claude/settings.local.json +28 -0
- package/.env-template +8 -5
- package/.vscode/settings.json +5 -0
- package/README.md +41 -0
- package/core/package.json +3 -1
- package/core/src/cache/cache.interface.ts +14 -18
- package/core/src/cache/memory-cache.ts +56 -0
- package/core/src/cache/noop-cache.ts +5 -23
- package/core/src/cache/redis-cache.ts +28 -38
- package/core/src/client/client-builder.ts +3 -3
- package/core/src/client/client.ts +11 -9
- package/core/src/decorators/reactionary.decorator.ts +80 -8
- package/core/src/index.ts +5 -29
- package/core/src/initialization.ts +43 -0
- package/core/src/providers/analytics.provider.ts +1 -1
- package/core/src/providers/base.provider.ts +61 -25
- package/core/src/providers/cart-payment.provider.ts +57 -0
- package/core/src/providers/cart.provider.ts +131 -8
- package/core/src/providers/category.provider.ts +9 -9
- package/core/src/providers/identity.provider.ts +8 -7
- package/core/src/providers/index.ts +12 -0
- package/core/src/providers/inventory.provider.ts +4 -4
- package/core/src/providers/price.provider.ts +7 -7
- package/core/src/providers/product.provider.ts +17 -5
- package/core/src/providers/profile.provider.ts +22 -0
- package/core/src/providers/search.provider.ts +4 -4
- package/core/src/providers/store.provider.ts +14 -0
- package/core/src/schemas/capabilities.schema.ts +3 -1
- package/core/src/schemas/models/analytics.model.ts +1 -1
- package/core/src/schemas/models/cart.model.ts +16 -3
- package/core/src/schemas/models/identifiers.model.ts +90 -22
- package/core/src/schemas/models/identity.model.ts +23 -7
- package/core/src/schemas/models/index.ts +15 -0
- package/core/src/schemas/models/payment.model.ts +41 -0
- package/core/src/schemas/models/profile.model.ts +35 -0
- package/core/src/schemas/models/shipping-method.model.ts +14 -0
- package/core/src/schemas/models/store.model.ts +11 -0
- package/core/src/schemas/mutations/cart-payment.mutation.ts +21 -0
- package/core/src/schemas/mutations/cart.mutation.ts +62 -3
- package/core/src/schemas/mutations/identity.mutation.ts +8 -1
- package/core/src/schemas/mutations/index.ts +10 -0
- package/core/src/schemas/mutations/profile.mutation.ts +9 -0
- package/core/src/schemas/queries/cart-payment.query.ts +12 -0
- package/core/src/schemas/queries/cart.query.ts +1 -1
- package/core/src/schemas/queries/identity.query.ts +1 -1
- package/core/src/schemas/queries/index.ts +3 -0
- package/core/src/schemas/queries/inventory.query.ts +4 -12
- package/core/src/schemas/queries/price.query.ts +1 -1
- package/core/src/schemas/queries/profile.query.ts +7 -0
- package/core/src/schemas/queries/search.query.ts +1 -1
- package/core/src/schemas/queries/store.query.ts +11 -0
- package/core/src/schemas/session.schema.ts +31 -6
- package/eslint.config.mjs +7 -0
- package/examples/next/src/app/page.tsx +4 -12
- package/examples/node/package.json +1 -3
- package/examples/node/src/basic/basic-node-provider-model-extension.spec.ts +9 -8
- package/examples/node/src/basic/basic-node-provider-query-extension.spec.ts +4 -3
- package/examples/node/src/basic/basic-node-setup.spec.ts +4 -5
- package/nx.json +1 -0
- package/otel/src/metrics.ts +2 -1
- package/otel/src/provider-instrumentation.ts +2 -1
- package/otel/src/tracer.ts +7 -6
- package/otel/src/trpc-middleware.ts +3 -2
- package/package.json +2 -1
- package/providers/algolia/src/core/initialize.ts +4 -3
- package/providers/algolia/src/providers/product.provider.ts +15 -13
- package/providers/algolia/src/providers/search.provider.ts +9 -9
- package/providers/algolia/src/schema/capabilities.schema.ts +1 -1
- package/providers/algolia/src/test/search.provider.spec.ts +10 -10
- package/providers/algolia/src/test/test-utils.ts +9 -4
- package/providers/commercetools/README.md +27 -0
- package/providers/commercetools/src/core/client.ts +164 -117
- package/providers/commercetools/src/core/initialize.ts +24 -14
- package/providers/commercetools/src/providers/cart-payment.provider.ts +193 -0
- package/providers/commercetools/src/providers/cart.provider.ts +402 -125
- package/providers/commercetools/src/providers/category.provider.ts +35 -35
- package/providers/commercetools/src/providers/identity.provider.ts +23 -75
- package/providers/commercetools/src/providers/index.ts +2 -0
- package/providers/commercetools/src/providers/inventory.provider.ts +69 -40
- package/providers/commercetools/src/providers/price.provider.ts +79 -47
- package/providers/commercetools/src/providers/product.provider.ts +36 -30
- package/providers/commercetools/src/providers/profile.provider.ts +61 -0
- package/providers/commercetools/src/providers/search.provider.ts +16 -12
- package/providers/commercetools/src/providers/store.provider.ts +78 -0
- package/providers/commercetools/src/schema/capabilities.schema.ts +3 -1
- package/providers/commercetools/src/schema/commercetools.schema.ts +18 -0
- package/providers/commercetools/src/schema/configuration.schema.ts +2 -1
- package/providers/commercetools/src/test/cart-payment.provider.spec.ts +145 -0
- package/providers/commercetools/src/test/cart.provider.spec.ts +82 -22
- package/providers/commercetools/src/test/category.provider.spec.ts +18 -17
- package/providers/commercetools/src/test/identity.provider.spec.ts +88 -0
- package/providers/commercetools/src/test/inventory.provider.spec.ts +41 -0
- package/providers/commercetools/src/test/price.provider.spec.ts +9 -8
- package/providers/commercetools/src/test/product.provider.spec.ts +33 -5
- package/providers/commercetools/src/test/profile.provider.spec.ts +49 -0
- package/providers/commercetools/src/test/search.provider.spec.ts +8 -7
- package/providers/commercetools/src/test/store.provider.spec.ts +37 -0
- package/providers/commercetools/src/test/test-utils.ts +7 -31
- package/providers/fake/src/core/initialize.ts +96 -38
- package/providers/fake/src/providers/analytics.provider.ts +6 -5
- package/providers/fake/src/providers/cart.provider.ts +66 -19
- package/providers/fake/src/providers/category.provider.ts +12 -12
- package/providers/fake/src/providers/identity.provider.ts +22 -14
- package/providers/fake/src/providers/index.ts +1 -0
- package/providers/fake/src/providers/inventory.provider.ts +13 -13
- package/providers/fake/src/providers/price.provider.ts +13 -13
- package/providers/fake/src/providers/product.provider.ts +13 -10
- package/providers/fake/src/providers/search.provider.ts +7 -5
- package/providers/fake/src/providers/store.provider.ts +47 -0
- package/providers/fake/src/schema/capabilities.schema.ts +4 -1
- package/providers/fake/src/test/cart.provider.spec.ts +18 -18
- package/providers/fake/src/test/category.provider.spec.ts +55 -37
- package/providers/fake/src/test/price.provider.spec.ts +9 -14
- package/providers/fake/src/test/product.provider.spec.ts +27 -0
- package/providers/fake/src/test/test-utils.ts +2 -28
- package/providers/posthog/src/core/initialize.ts +3 -3
- package/providers/posthog/src/schema/capabilities.schema.ts +1 -1
- package/trpc/src/client.ts +42 -41
- package/trpc/src/index.ts +4 -3
- package/trpc/src/integration.spec.ts +11 -11
- package/trpc/src/server.ts +26 -24
- package/trpc/src/test-utils.ts +9 -4
- package/trpc/src/types.ts +24 -22
- package/core/src/cache/cache-evaluation.interface.ts +0 -19
- package/examples/node/src/test-utils.ts +0 -26
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(npx nx build:*)",
|
|
5
|
+
"Bash(npx tsc:*)",
|
|
6
|
+
"Bash(npx nx lint:*)",
|
|
7
|
+
"Bash(npx nx test:*)",
|
|
8
|
+
"Bash(npx nx run:*)",
|
|
9
|
+
"Bash(npx ts-node:*)",
|
|
10
|
+
"Bash(npx tsx:*)",
|
|
11
|
+
"Bash(grep:*)",
|
|
12
|
+
"Bash(pnpm list:*)",
|
|
13
|
+
"Bash(npx nx affected:build:*)",
|
|
14
|
+
"Bash(npx nx affected:lint:*)",
|
|
15
|
+
"Bash(cat:*)",
|
|
16
|
+
"Bash(npx nx show projects:*)",
|
|
17
|
+
"Bash(npx nx g:*)",
|
|
18
|
+
"Bash(pnpm add:*)",
|
|
19
|
+
"Bash(npx nx show project:*)",
|
|
20
|
+
"Bash(npx nx reset:*)",
|
|
21
|
+
"WebSearch",
|
|
22
|
+
"Bash(node:*)",
|
|
23
|
+
"Bash(npm view:*)"
|
|
24
|
+
],
|
|
25
|
+
"deny": [],
|
|
26
|
+
"ask": []
|
|
27
|
+
}
|
|
28
|
+
}
|
package/.env-template
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
CTP_PROJECT_KEY=
|
|
2
|
+
CTP_CLIENT_SECRET=
|
|
3
|
+
CTP_CLIENT_ID=
|
|
4
|
+
CTP_AUTH_URL=https://auth.europe-west1.gcp.commercetools.com
|
|
5
|
+
CTP_API_URL=https://api.europe-west1.gcp.commercetools.com
|
|
6
|
+
CTP_SCOPES=
|
|
7
|
+
|
|
8
|
+
|
|
6
9
|
|
|
7
10
|
OTEL_SERVICE_NAME=reactionary
|
|
8
11
|
OTEL_LOG_LEVEL=info
|
package/README.md
CHANGED
|
@@ -37,3 +37,44 @@ The following is a short list of commonly used terms and phrases, to keep guessi
|
|
|
37
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
38
|
- *Product Analytics:* structured analytics that relate to how the the product is being used.
|
|
39
39
|
- *Microsite:* an application of a limited scope. It may focus solely on this limited functionality, making it ideal for demonstration purposes.
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
## TODO:
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
### Core
|
|
48
|
+
- [ ] Figure a way to get a correlation id from the caller
|
|
49
|
+
- [ ] Maybe rework session so its not languageContext but requestContext, and this could have the correlation id in it?
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
### Search
|
|
53
|
+
- [ ] Add support for filters, not just facets
|
|
54
|
+
|
|
55
|
+
### Marketing
|
|
56
|
+
- [ ] Add marketing provider (input: (name, url, user, segments, productid, categoryid) => output { Array<Product | Category | Image | Text>}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
### Inventory
|
|
60
|
+
- [ ] Invent and document some notational standard for cannel-key to purpose.
|
|
61
|
+
- [ ] Be traced and cached
|
|
62
|
+
|
|
63
|
+
### Profile
|
|
64
|
+
- [ ] Be able to register new account
|
|
65
|
+
- [ ] Create profile provider
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
### Organization
|
|
69
|
+
- [ ] Create organization provider
|
|
70
|
+
- [ ] Create business user provider
|
|
71
|
+
- [ ] Create role provider
|
|
72
|
+
|
|
73
|
+
### Cart
|
|
74
|
+
- [ ] Add B2B/organization identifier
|
|
75
|
+
|
|
76
|
+
### Price
|
|
77
|
+
- [ ] Add list price
|
|
78
|
+
|
|
79
|
+
### Order
|
|
80
|
+
- [ ] Add Order
|
package/core/package.json
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
1
|
+
import type { z } from 'zod';
|
|
2
|
+
import type { BaseModel } from '../schemas/models';
|
|
3
|
+
|
|
4
|
+
export interface CacheEntryOptions {
|
|
5
|
+
ttlSeconds: number;
|
|
6
|
+
dependencyIds: Array<string>;
|
|
7
|
+
}
|
|
2
8
|
|
|
3
9
|
/**
|
|
4
10
|
* Generic cache interface that can be implemented by different cache backends
|
|
@@ -8,31 +14,21 @@ export interface Cache {
|
|
|
8
14
|
/**
|
|
9
15
|
* Retrieves a value from cache and validates it against the provided schema
|
|
10
16
|
*/
|
|
11
|
-
get<T>(key: string, schema: z.ZodType<T>): Promise<T | null>;
|
|
17
|
+
get<T extends BaseModel>(key: string, schema: z.ZodType<T>): Promise<T | null>;
|
|
12
18
|
|
|
13
19
|
/**
|
|
14
20
|
* Stores a value in cache with optional expiration time
|
|
15
21
|
*/
|
|
16
|
-
put(key: string, value: unknown,
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Removes one or more keys from cache
|
|
20
|
-
* Supports wildcard patterns (implementation dependent)
|
|
21
|
-
*/
|
|
22
|
-
del(keys: string | string[]): Promise<void>;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Finds all keys matching a pattern (implementation dependent)
|
|
26
|
-
*/
|
|
27
|
-
keys(pattern: string): Promise<string[]>;
|
|
22
|
+
put(key: string, value: unknown, options: CacheEntryOptions): Promise<void>;
|
|
28
23
|
|
|
29
24
|
/**
|
|
30
|
-
*
|
|
25
|
+
* Removes entries from the cache based on a set of dependency
|
|
26
|
+
* ids
|
|
31
27
|
*/
|
|
32
|
-
|
|
28
|
+
invalidate(dependencyIds: Array<string>): Promise<void>;
|
|
33
29
|
|
|
34
30
|
/**
|
|
35
|
-
*
|
|
31
|
+
* Removes all entries from cache, wiping it completely
|
|
36
32
|
*/
|
|
37
|
-
|
|
33
|
+
clear(): Promise<void>;
|
|
38
34
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { BaseModel } from '../schemas/models';
|
|
2
|
+
import type { Cache, CacheEntryOptions } from './cache.interface';
|
|
3
|
+
import type z from 'zod';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Memory version of the cache. Primarily useful for local development.
|
|
7
|
+
* This is NOT suited for production use.
|
|
8
|
+
*/
|
|
9
|
+
export class MemoryCache implements Cache {
|
|
10
|
+
protected entries = new Array<{ key: string; value: unknown, options: CacheEntryOptions }>();
|
|
11
|
+
|
|
12
|
+
public async get<T extends BaseModel>(key: string, schema: z.ZodType<T>): Promise<T | null> {
|
|
13
|
+
const c = this.entries.find((x) => x.key === key);
|
|
14
|
+
|
|
15
|
+
if (!c) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const parsed = schema.parse(c.value);
|
|
20
|
+
|
|
21
|
+
parsed.meta.cache.hit = true;
|
|
22
|
+
|
|
23
|
+
return parsed;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public async put(
|
|
27
|
+
key: string,
|
|
28
|
+
value: unknown,
|
|
29
|
+
options: CacheEntryOptions
|
|
30
|
+
): Promise<void> {
|
|
31
|
+
this.entries.push({
|
|
32
|
+
key,
|
|
33
|
+
value,
|
|
34
|
+
options
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public async invalidate(dependencyIds: Array<string>): Promise<void> {
|
|
41
|
+
let index = 0;
|
|
42
|
+
for (const entry of this.entries) {
|
|
43
|
+
for (const entryDependency of entry.options.dependencyIds) {
|
|
44
|
+
if (dependencyIds.indexOf(entryDependency) > -1) {
|
|
45
|
+
this.entries.splice(index, 1);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
index++;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public async clear(): Promise<void> {
|
|
54
|
+
this.entries = [];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Cache } from './cache.interface';
|
|
2
|
-
import z from 'zod';
|
|
1
|
+
import type { Cache, CacheEntryOptions } from './cache.interface';
|
|
2
|
+
import type z from 'zod';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* No-op cache implementation that never stores or returns data.
|
|
@@ -7,36 +7,18 @@ import z from 'zod';
|
|
|
7
7
|
*/
|
|
8
8
|
export class NoOpCache implements Cache {
|
|
9
9
|
public async get<T>(_key: string, _schema: z.ZodType<T>): Promise<T | null> {
|
|
10
|
-
// Always return null - never a cache hit
|
|
11
10
|
return null;
|
|
12
11
|
}
|
|
13
12
|
|
|
14
|
-
public async put(_key: string, _value: unknown,
|
|
15
|
-
// No-op - silently ignore cache write requests
|
|
13
|
+
public async put(_key: string, _value: unknown, options: CacheEntryOptions): Promise<void> {
|
|
16
14
|
return;
|
|
17
15
|
}
|
|
18
16
|
|
|
19
|
-
public async
|
|
20
|
-
// No-op - silently ignore delete requests
|
|
17
|
+
public async invalidate(dependencyIds: Array<string>): Promise<void> {
|
|
21
18
|
return;
|
|
22
19
|
}
|
|
23
20
|
|
|
24
|
-
public async
|
|
25
|
-
// Always return empty array
|
|
26
|
-
return [];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
public async clear(_pattern?: string): Promise<void> {
|
|
30
|
-
// No-op - silently ignore clear requests
|
|
21
|
+
public async clear(): Promise<void> {
|
|
31
22
|
return;
|
|
32
23
|
}
|
|
33
|
-
|
|
34
|
-
public async getStats(): Promise<{ hits: number; misses: number; size: number }> {
|
|
35
|
-
// Always return zeros
|
|
36
|
-
return {
|
|
37
|
-
hits: 0,
|
|
38
|
-
misses: 0,
|
|
39
|
-
size: 0
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
24
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Redis } from '@upstash/redis';
|
|
2
|
-
import { Cache } from './cache.interface';
|
|
3
|
-
import z from 'zod';
|
|
2
|
+
import type { Cache, CacheEntryOptions } from './cache.interface';
|
|
3
|
+
import type z from 'zod';
|
|
4
4
|
|
|
5
5
|
export class RedisCache implements Cache {
|
|
6
6
|
protected redis: Redis;
|
|
@@ -13,62 +13,52 @@ export class RedisCache implements Cache {
|
|
|
13
13
|
if (!key) {
|
|
14
14
|
return null;
|
|
15
15
|
}
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
const unvalidated = await this.redis.get(key);
|
|
18
18
|
const parsed = schema.safeParse(unvalidated);
|
|
19
19
|
|
|
20
20
|
if (parsed.success) {
|
|
21
21
|
return parsed.data;
|
|
22
22
|
}
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
return null;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
public async put(
|
|
27
|
+
public async put(
|
|
28
|
+
key: string,
|
|
29
|
+
value: unknown,
|
|
30
|
+
options: CacheEntryOptions
|
|
31
|
+
): Promise<void> {
|
|
28
32
|
if (!key) {
|
|
29
33
|
return;
|
|
30
34
|
}
|
|
31
35
|
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
}
|
|
36
|
+
const serialized = JSON.stringify(value);
|
|
37
|
+
const multi = this.redis.multi();
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (key.includes('*')) {
|
|
41
|
-
// Handle wildcard patterns
|
|
42
|
-
const matchingKeys = await this.redis.keys(key);
|
|
43
|
-
if (matchingKeys.length > 0) {
|
|
44
|
-
await this.redis.del(...matchingKeys);
|
|
45
|
-
}
|
|
46
|
-
} else {
|
|
47
|
-
// Delete specific key
|
|
48
|
-
await this.redis.del(key);
|
|
49
|
-
}
|
|
39
|
+
multi.set(key, serialized, { ex: options.ttlSeconds });
|
|
40
|
+
|
|
41
|
+
for (const depId of options.dependencyIds) {
|
|
42
|
+
multi.sadd(`dep:${depId}`, key);
|
|
50
43
|
}
|
|
51
|
-
}
|
|
52
44
|
|
|
53
|
-
|
|
54
|
-
return await this.redis.keys(pattern);
|
|
45
|
+
await multi.exec();
|
|
55
46
|
}
|
|
56
47
|
|
|
57
|
-
public async
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
48
|
+
public async invalidate(dependencyIds: Array<string>): Promise<void> {
|
|
49
|
+
for (const id of dependencyIds) {
|
|
50
|
+
const depKey = `dep:${id}`;
|
|
51
|
+
const keys = await this.redis.smembers(depKey);
|
|
52
|
+
|
|
53
|
+
if (keys.length > 0) {
|
|
54
|
+
await this.redis.del(...keys);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
await this.redis.del(depKey);
|
|
62
58
|
}
|
|
63
59
|
}
|
|
64
60
|
|
|
65
|
-
public async
|
|
66
|
-
//
|
|
67
|
-
const keys = await this.redis.keys('*');
|
|
68
|
-
return {
|
|
69
|
-
hits: 0, // Would need to track this separately
|
|
70
|
-
misses: 0, // Would need to track this separately
|
|
71
|
-
size: keys.length
|
|
72
|
-
};
|
|
61
|
+
public async clear(): Promise<void> {
|
|
62
|
+
// Not sure about supporting this on Redis.
|
|
73
63
|
}
|
|
74
64
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Cache } from "../cache/cache.interface";
|
|
1
|
+
import type { Cache } from "../cache/cache.interface";
|
|
2
2
|
import { NoOpCache } from "../cache/noop-cache";
|
|
3
|
-
import { Client } from "./client";
|
|
4
|
-
import { AnalyticsProvider } from "../providers/analytics.provider";
|
|
3
|
+
import type { Client } from "./client";
|
|
4
|
+
import type { AnalyticsProvider } from "../providers/analytics.provider";
|
|
5
5
|
|
|
6
6
|
type CapabilityFactory<T> = (cache: Cache) => T;
|
|
7
7
|
|
|
@@ -1,13 +1,14 @@
|
|
|
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
|
-
import { Cache } from "../cache/cache.interface";
|
|
1
|
+
import type { AnalyticsProvider } from "../providers/analytics.provider";
|
|
2
|
+
import type { ProductProvider } from "../providers/product.provider";
|
|
3
|
+
import type { SearchProvider } from "../providers/search.provider";
|
|
4
|
+
import type { IdentityProvider } from '../providers/identity.provider';
|
|
5
|
+
import type { CartProvider } from "../providers/cart.provider";
|
|
6
|
+
import type { PriceProvider } from "../providers/price.provider";
|
|
7
|
+
import type { InventoryProvider } from "../providers/inventory.provider";
|
|
8
|
+
import type { Cache } from "../cache/cache.interface";
|
|
9
9
|
import { RedisCache } from "../cache/redis-cache";
|
|
10
|
-
import { CategoryProvider } from "../providers/category.provider";
|
|
10
|
+
import type { CategoryProvider } from "../providers/category.provider";
|
|
11
|
+
import type { CartPaymentProvider } from "../providers";
|
|
11
12
|
|
|
12
13
|
export interface Client {
|
|
13
14
|
product: ProductProvider,
|
|
@@ -15,6 +16,7 @@ export interface Client {
|
|
|
15
16
|
identity: IdentityProvider,
|
|
16
17
|
cache: Cache,
|
|
17
18
|
cart: CartProvider,
|
|
19
|
+
cartPayment: CartPaymentProvider,
|
|
18
20
|
analytics: Array<AnalyticsProvider>,
|
|
19
21
|
price: PriceProvider,
|
|
20
22
|
inventory: InventoryProvider,
|
|
@@ -1,16 +1,88 @@
|
|
|
1
|
-
|
|
1
|
+
import type { BaseProvider } from '../providers';
|
|
2
|
+
import { getTracer, SpanKind } from '@reactionary/otel';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The options associated with annotating a provider function and marking
|
|
6
|
+
* it as a reactionary entrypoint to be called
|
|
7
|
+
*/
|
|
8
|
+
export class ReactionaryDecoratorOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Whether or not the query is eligible for caching. Queries that depend
|
|
11
|
+
* heavily on personalization, for example, are likely to be a poor fit
|
|
12
|
+
* for caching.
|
|
13
|
+
*/
|
|
14
|
+
public cache = false;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Whether or not the cache entry should be variable based on the locale
|
|
18
|
+
* of the context in which it is querried.
|
|
19
|
+
*/
|
|
20
|
+
public localeDependentCaching = false;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Whether or not the cache entry should be variable based on the currency
|
|
24
|
+
* of the context in which it is querried.
|
|
25
|
+
*/
|
|
26
|
+
public currencyDependentCaching = false;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The number of seconds which a cache entry should be considered valid for the
|
|
30
|
+
* given query.
|
|
31
|
+
*/
|
|
32
|
+
public cacheTimeToLiveInSeconds = 60;
|
|
33
|
+
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Decorator for provider functions to provide functionality such as caching, tracing and type-checked
|
|
38
|
+
* assertion through Zod. It should only be used with publically accessible queries or mutations on
|
|
39
|
+
* providers.
|
|
40
|
+
*/
|
|
41
|
+
export function Reactionary(options: Partial<ReactionaryDecoratorOptions>) {
|
|
2
42
|
return function (
|
|
3
|
-
target:
|
|
43
|
+
target: BaseProvider,
|
|
4
44
|
propertyKey: string | symbol,
|
|
5
45
|
descriptor: PropertyDescriptor
|
|
6
46
|
): PropertyDescriptor {
|
|
7
47
|
const original = descriptor.value;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
48
|
+
const scope = `${target.constructor.name}.${propertyKey.toString()}`;
|
|
49
|
+
const configuration = { ...new ReactionaryDecoratorOptions(), ...options };
|
|
50
|
+
|
|
51
|
+
if (!original) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
'@Reactionary decorator may only be applied to methods on classes extending BaseProvider.'
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
descriptor.value = async function (this: BaseProvider, ...args: any[]) {
|
|
58
|
+
const tracer = getTracer();
|
|
59
|
+
|
|
60
|
+
return tracer.startActiveSpan(
|
|
61
|
+
propertyKey.toString(),
|
|
62
|
+
{ kind: SpanKind.SERVER },
|
|
63
|
+
async (span) => {
|
|
64
|
+
const cacheKey = this.generateCacheKeyForQuery(scope, args[0]);
|
|
65
|
+
|
|
66
|
+
const fromCache = await this.cache.get(cacheKey, this.schema);
|
|
67
|
+
let result = fromCache;
|
|
68
|
+
|
|
69
|
+
if (!result) {
|
|
70
|
+
result = await original.apply(this, args);
|
|
71
|
+
|
|
72
|
+
const dependencyIds = this.generateDependencyIdsForModel(result);
|
|
73
|
+
|
|
74
|
+
this.cache.put(cacheKey, result, {
|
|
75
|
+
ttlSeconds: configuration.cacheTimeToLiveInSeconds,
|
|
76
|
+
dependencyIds: dependencyIds
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
span.end();
|
|
81
|
+
return this.assert(result as any);
|
|
82
|
+
}
|
|
83
|
+
);
|
|
12
84
|
};
|
|
13
|
-
|
|
85
|
+
|
|
14
86
|
return descriptor;
|
|
15
87
|
};
|
|
16
|
-
}
|
|
88
|
+
}
|
package/core/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export * from './cache/cache.interface';
|
|
2
|
-
export * from './cache/cache-evaluation.interface';
|
|
3
2
|
export * from './cache/redis-cache';
|
|
3
|
+
export * from './cache/memory-cache';
|
|
4
4
|
export * from './cache/noop-cache';
|
|
5
5
|
|
|
6
6
|
export * from './client/client';
|
|
@@ -8,36 +8,12 @@ export * from './client/client-builder';
|
|
|
8
8
|
|
|
9
9
|
export * from './decorators/reactionary.decorator';
|
|
10
10
|
|
|
11
|
-
export * from './providers/
|
|
12
|
-
export * from './providers/base.provider';
|
|
13
|
-
export * from './providers/cart.provider';
|
|
14
|
-
export * from './providers/identity.provider';
|
|
15
|
-
export * from './providers/inventory.provider';
|
|
16
|
-
export * from './providers/price.provider';
|
|
17
|
-
export * from './providers/product.provider';
|
|
18
|
-
export * from './providers/search.provider';
|
|
19
|
-
export * from './providers/category.provider';
|
|
11
|
+
export * from './providers/'
|
|
20
12
|
|
|
21
13
|
export * from './schemas/capabilities.schema';
|
|
22
14
|
export * from './schemas/session.schema';
|
|
23
15
|
|
|
24
|
-
export * from './schemas/models/
|
|
25
|
-
export * from './schemas/
|
|
26
|
-
export * from './schemas/models/currency.model';
|
|
27
|
-
export * from './schemas/models/identifiers.model';
|
|
28
|
-
export * from './schemas/models/identity.model';
|
|
29
|
-
export * from './schemas/models/inventory.model';
|
|
30
|
-
export * from './schemas/models/price.model';
|
|
31
|
-
export * from './schemas/models/product.model';
|
|
32
|
-
export * from './schemas/models/search.model';
|
|
33
|
-
export * from './schemas/models/category.model';
|
|
34
|
-
|
|
35
|
-
export * from './schemas/mutations/base.mutation';
|
|
36
|
-
export * from './schemas/mutations/cart.mutation';
|
|
37
|
-
export * from './schemas/mutations/identity.mutation';
|
|
38
|
-
export * from './schemas/mutations/inventory.mutation';
|
|
39
|
-
export * from './schemas/mutations/price.mutation';
|
|
40
|
-
export * from './schemas/mutations/product.mutation';
|
|
41
|
-
export * from './schemas/mutations/search.mutation';
|
|
42
|
-
|
|
16
|
+
export * from './schemas/models/';
|
|
17
|
+
export * from './schemas/mutations/';
|
|
43
18
|
export * from './schemas/queries';
|
|
19
|
+
export * from './initialization';
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { RequestContext } from "./schemas/session.schema";
|
|
2
|
+
|
|
3
|
+
export function createInitialRequestContext(): RequestContext {
|
|
4
|
+
return {
|
|
5
|
+
id: '',
|
|
6
|
+
identity: {
|
|
7
|
+
type: 'Anonymous',
|
|
8
|
+
meta: {
|
|
9
|
+
cache: { hit: false, key: '' },
|
|
10
|
+
placeholder: false,
|
|
11
|
+
},
|
|
12
|
+
id: { userId: 'anonymous' },
|
|
13
|
+
token: undefined,
|
|
14
|
+
issued: new Date(),
|
|
15
|
+
expiry: new Date(new Date().getTime() + 3600 * 1000),
|
|
16
|
+
logonId: '',
|
|
17
|
+
createdAt: '',
|
|
18
|
+
updatedAt: '',
|
|
19
|
+
keyring: [],
|
|
20
|
+
currentService: undefined,
|
|
21
|
+
},
|
|
22
|
+
languageContext: {
|
|
23
|
+
locale: 'en-US',
|
|
24
|
+
currencyCode: 'USD',
|
|
25
|
+
},
|
|
26
|
+
storeIdentifier: {
|
|
27
|
+
key: 'the-good-store',
|
|
28
|
+
},
|
|
29
|
+
taxJurisdiction: {
|
|
30
|
+
countryCode: 'US',
|
|
31
|
+
stateCode: '',
|
|
32
|
+
countyCode: '',
|
|
33
|
+
cityCode: '',
|
|
34
|
+
},
|
|
35
|
+
session: {},
|
|
36
|
+
|
|
37
|
+
correlationId: '',
|
|
38
|
+
isBot: false,
|
|
39
|
+
clientIp: '',
|
|
40
|
+
userAgent: '',
|
|
41
|
+
referrer: '',
|
|
42
|
+
};
|
|
43
|
+
}
|