@vendure/cloud-config 0.0.0
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/README.md +298 -0
- package/dist/cloud-config.d.ts +55 -0
- package/dist/cloud-config.d.ts.map +1 -0
- package/dist/cloud-config.js +2 -0
- package/dist/cloud-config.js.map +1 -0
- package/dist/cloud-dashboard-plugin.d.ts +28 -0
- package/dist/cloud-dashboard-plugin.d.ts.map +1 -0
- package/dist/cloud-dashboard-plugin.js +91 -0
- package/dist/cloud-dashboard-plugin.js.map +1 -0
- package/dist/cloud-plugin-declaration.d.ts +12 -0
- package/dist/cloud-plugin-declaration.d.ts.map +1 -0
- package/dist/cloud-plugin-declaration.js +53 -0
- package/dist/cloud-plugin-declaration.js.map +1 -0
- package/dist/composition.d.ts +23 -0
- package/dist/composition.d.ts.map +1 -0
- package/dist/composition.js +147 -0
- package/dist/composition.js.map +1 -0
- package/dist/define-cloud-config.d.ts +19 -0
- package/dist/define-cloud-config.d.ts.map +1 -0
- package/dist/define-cloud-config.js +47 -0
- package/dist/define-cloud-config.js.map +1 -0
- package/dist/errors.d.ts +4 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +13 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations.d.ts +32 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +250 -0
- package/dist/migrations.js.map +1 -0
- package/dist/outbound-hosts.d.ts +16 -0
- package/dist/outbound-hosts.d.ts.map +1 -0
- package/dist/outbound-hosts.js +211 -0
- package/dist/outbound-hosts.js.map +1 -0
- package/dist/path.d.ts +2 -0
- package/dist/path.d.ts.map +1 -0
- package/dist/path.js +18 -0
- package/dist/path.js.map +1 -0
- package/dist/platform-fields.d.ts +71 -0
- package/dist/platform-fields.d.ts.map +1 -0
- package/dist/platform-fields.js +94 -0
- package/dist/platform-fields.js.map +1 -0
- package/dist/plugin-attribution.d.ts +27 -0
- package/dist/plugin-attribution.d.ts.map +1 -0
- package/dist/plugin-attribution.js +97 -0
- package/dist/plugin-attribution.js.map +1 -0
- package/dist/runtime-backstop.d.ts +8 -0
- package/dist/runtime-backstop.d.ts.map +1 -0
- package/dist/runtime-backstop.js +20 -0
- package/dist/runtime-backstop.js.map +1 -0
- package/dist/secrets.d.ts +14 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +109 -0
- package/dist/secrets.js.map +1 -0
- package/dist/stack.d.ts +3 -0
- package/dist/stack.d.ts.map +1 -0
- package/dist/stack.js +58 -0
- package/dist/stack.js.map +1 -0
- package/dist/superseded-plugins.d.ts +19 -0
- package/dist/superseded-plugins.d.ts.map +1 -0
- package/dist/superseded-plugins.js +41 -0
- package/dist/superseded-plugins.js.map +1 -0
- package/dist/vendure-extension.d.ts +38 -0
- package/dist/vendure-extension.d.ts.map +1 -0
- package/dist/vendure-extension.js +111 -0
- package/dist/vendure-extension.js.map +1 -0
- package/package.json +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
# @vendure/cloud-config
|
|
2
|
+
|
|
3
|
+
The interface package a Vendure Cloud application depends on. It holds the Cloud Config
|
|
4
|
+
type and the checks that keep the ownership boundary honest, per
|
|
5
|
+
[ADR-0017](../../internal/adr/0017-cloud-config.md),
|
|
6
|
+
[ADR-0043](../../internal/adr/0043-cloud-config-composition-and-the-platform-entrypoint.md),
|
|
7
|
+
[ADR-0044](../../internal/adr/0044-which-vendure-plugins-the-platform-supplies.md) and
|
|
8
|
+
[ADR-0047](../../internal/adr/0047-vendure-extensions-are-declared-in-the-cloud-config.md).
|
|
9
|
+
|
|
10
|
+
A customer writes a configuration file and no entrypoint, importing from `@vendure/cloud`
|
|
11
|
+
rather than from here:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { defineCloudConfig, defineVendureExtension } from "@vendure/cloud";
|
|
15
|
+
import { DefaultSearchPlugin } from "@vendure/core";
|
|
16
|
+
import { ErpPlugin } from "./plugins/erp/erp.plugin.js";
|
|
17
|
+
|
|
18
|
+
export default defineCloudConfig({
|
|
19
|
+
plugins: [
|
|
20
|
+
DefaultSearchPlugin.init({ bufferUpdates: false }),
|
|
21
|
+
defineVendureExtension({
|
|
22
|
+
id: "acme.erp",
|
|
23
|
+
plugins: [ErpPlugin],
|
|
24
|
+
migrations: ["./migrations/*.js"],
|
|
25
|
+
outboundHosts: ["erp.acme.com"],
|
|
26
|
+
}),
|
|
27
|
+
],
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Vendure Extensions
|
|
32
|
+
|
|
33
|
+
A **Vendure Extension** is a named group of the customer's own plugins carrying the three
|
|
34
|
+
pieces of metadata the platform reads
|
|
35
|
+
([ADR-0047](../../internal/adr/0047-vendure-extensions-are-declared-in-the-cloud-config.md)).
|
|
36
|
+
It is written inline as above, or exported by a package and referenced by name; the wrapper
|
|
37
|
+
does not distinguish the two, and a third-party extension's author declares its metadata so a
|
|
38
|
+
consumer does not restate it. That ADR's table records why every other field the original
|
|
39
|
+
proposal carried was removed, and the input is a function argument so an object literal
|
|
40
|
+
reintroducing one fails the build.
|
|
41
|
+
|
|
42
|
+
`defineCloudConfig` flattens the extensions out of the plugins array before anything else
|
|
43
|
+
sees it, keeping declaration order, and records which extension declared each plugin.
|
|
44
|
+
`PlatformFieldSetByPluginError` reads that map, so a plugin inside an extension that writes a
|
|
45
|
+
platform-owned field is named together with the extension it came from. Everything
|
|
46
|
+
downstream — `hoistCloudPluginsLast`, `recordPlatformFieldWrites`, the two assertions —
|
|
47
|
+
operates on the flattened array and needed no change.
|
|
48
|
+
|
|
49
|
+
**A `migrations` glob resolves against the file that declared the extension**, not against
|
|
50
|
+
the configuration file that references it. `defineVendureExtension` reads its own call site
|
|
51
|
+
off the stack for this, which is the one piece of machinery here that is not obvious; the
|
|
52
|
+
reasoning, including why `import.meta.url` is not asked for and why the stack has to be read
|
|
53
|
+
through our own `Error.prepareStackTrace`, is at the function in `src/vendure-extension.ts`.
|
|
54
|
+
`test/managed-build.test.ts` proves it against compiled output laid out and run the way the
|
|
55
|
+
managed build produces it, because nothing about the trap survives being tested against this
|
|
56
|
+
package's own TypeScript sources.
|
|
57
|
+
|
|
58
|
+
### Migrations are resolved into an ordered class list
|
|
59
|
+
|
|
60
|
+
A glob never reaches TypeORM. The config handle carries `loadMigrations()`, which expands each
|
|
61
|
+
extension's globs, imports what they match, and returns the migration classes with the
|
|
62
|
+
extension that declared each one:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
const { loadMigrations } = readCloudConfig(handle);
|
|
66
|
+
const loaded = await loadMigrations();
|
|
67
|
+
// loaded[n] is { migration, name, timestamp, file, extension }
|
|
68
|
+
// dbConnectionOptions.migrations = loaded.map((entry) => entry.migration)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
It is a function on the handle rather than a value on it, and it is bound to that handle's own
|
|
72
|
+
extensions rather than callable with any array. ADR-0048's "Ordering is a property of the array
|
|
73
|
+
we build" argues both, and is the place to change if either stops being true.
|
|
74
|
+
|
|
75
|
+
The order is **ascending timestamp, then extension declaration order, then class name.**
|
|
76
|
+
TypeORM sorts the set by timestamp and `Array.prototype.sort` is stable, so equal timestamps
|
|
77
|
+
run in whatever order the array holds them; today that order comes from `glob.sync`, which
|
|
78
|
+
does not sort, so a tie resolves by filesystem walk order and can differ between the
|
|
79
|
+
customer's machine and the managed build. The last two keys make the tie a value the platform
|
|
80
|
+
computed. **Ordering across extensions is not a contract**
|
|
81
|
+
([ADR-0048](../../internal/adr/0048-extension-migrations-keep-typeorms-ledger-identity.md)):
|
|
82
|
+
an extension cannot declare that it must migrate after another, and nothing here adds a way
|
|
83
|
+
to say so.
|
|
84
|
+
|
|
85
|
+
The ledger identity is TypeORM's own — the migration class name, read off an instance so that
|
|
86
|
+
a migration declaring `name` as an instance field is called here what it is called in the
|
|
87
|
+
ledger. A glob that contributes no migrations raises `MigrationGlobMatchedNoMigrationsError`
|
|
88
|
+
naming the extension and the glob as it was written. It distinguishes a glob that matched
|
|
89
|
+
nothing at all, which is far more often a wrong path than an extension with no migrations,
|
|
90
|
+
from one whose every match was filtered out as not being a migration file.
|
|
91
|
+
|
|
92
|
+
## Secrets
|
|
93
|
+
|
|
94
|
+
`secret('NAME')` reads a customer-supplied value, registers the name, and returns a plain
|
|
95
|
+
string. Values arrive as ordinary environment variables (ADR-0030), so this is a read helper
|
|
96
|
+
over `process.env` rather than a second delivery mechanism.
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
export default defineCloudConfig({
|
|
100
|
+
plugins: [
|
|
101
|
+
PaymentPlugin.init({ apiKey: secret("STRIPE_SECRET_KEY") }),
|
|
102
|
+
],
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
It exists because `process.env` gives the platform no hook. Three things follow from the call
|
|
107
|
+
that a direct read cannot provide:
|
|
108
|
+
|
|
109
|
+
- **Every missing name is reported at once.** A name that is unset — or set to an empty or
|
|
110
|
+
whitespace-only value, which counts as unset because an empty credential fails inside a
|
|
111
|
+
payment call rather than at boot — is recorded rather than thrown on. `defineCloudConfig`
|
|
112
|
+
then raises `MissingSecretsError` naming all of them, so a configuration with four unset
|
|
113
|
+
secrets takes one deploy to fix rather than four.
|
|
114
|
+
- **The value is trimmed**, so a credential that picked up a trailing newline from a
|
|
115
|
+
file-based secret store or a `.env` loader does not reach the caller intact and fail in the
|
|
116
|
+
same place an empty one would. Only the ends: a multi-line value such as a PEM key keeps its
|
|
117
|
+
interior exactly, per ADR-0030's note that customers put private keys in these variables.
|
|
118
|
+
- **The declared set is exactly what the code asked for.** Registration is a side effect of
|
|
119
|
+
the call, so it cannot drift from the code the way a hand-written `requirements.secrets`
|
|
120
|
+
list would. `listDeclaredSecrets()` is the entrypoint's accessor after composition.
|
|
121
|
+
- **The value can be redacted.** `redactSecrets(text)` replaces every resolved value wherever
|
|
122
|
+
it appears. A value read straight out of `process.env` is an ordinary string that nothing
|
|
123
|
+
downstream can recognise.
|
|
124
|
+
|
|
125
|
+
**The argument has to be a string literal**, enforced by the type rather than by a lint rule.
|
|
126
|
+
Passing a variable makes the parameter type `never` and fails the build. A literal is what
|
|
127
|
+
makes the declared set readable by static analysis, and a type constraint needs no tooling in
|
|
128
|
+
the customer's project.
|
|
129
|
+
|
|
130
|
+
### Reading a secret at request time is not supported
|
|
131
|
+
|
|
132
|
+
`secret()` resolves once, while the configuration object is being built. A call made later —
|
|
133
|
+
inside a request handler, a job processor, or a lazily constructed client — happens after
|
|
134
|
+
`defineCloudConfig` has already checked the registry, so a missing value is not reported at
|
|
135
|
+
boot and is not part of the declared set.
|
|
136
|
+
|
|
137
|
+
The answer is to read the secret during composition and hold it, which is the natural shape
|
|
138
|
+
when secrets are plugin options:
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
// Yes: resolved once, declared, redactable.
|
|
142
|
+
PaymentPlugin.init({ apiKey: secret("STRIPE_SECRET_KEY") })
|
|
143
|
+
|
|
144
|
+
// No: resolved per request, invisible to the missing-secret report.
|
|
145
|
+
class PaymentService {
|
|
146
|
+
charge() {
|
|
147
|
+
const key = secret("STRIPE_SECRET_KEY");
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
A plugin that genuinely needs the value later should take it as an option and keep it, rather
|
|
153
|
+
than reaching for it at the point of use.
|
|
154
|
+
|
|
155
|
+
## Declared outbound hosts
|
|
156
|
+
|
|
157
|
+
A Vendure Extension declares the hosts it talks to, and `installOutboundHostCheck` reports a
|
|
158
|
+
call to a host it did not declare, naming the extension
|
|
159
|
+
([ADR-0028's amendment](../../internal/adr/0028-cell-network-topology.md)).
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
Vendure Cloud: Extension acme.erp attempted outbound access to api.other-erp.example.
|
|
163
|
+
Declared outbound hosts: erp.acme.com
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The check runs inside the customer's own process rather than in the network path, and
|
|
167
|
+
attribution is the reason. A firewall sees a source address and a TLS server name and cannot
|
|
168
|
+
say which extension made the call, so it cannot produce that message. Reading the stack can.
|
|
169
|
+
|
|
170
|
+
**It is reported and never blocked** (CLO-412 records the reasoning). This is a hygiene
|
|
171
|
+
control over trusted code, not a security boundary — code written to bypass it will succeed,
|
|
172
|
+
and a compromised dependency can exfiltrate through a *declared* host anyway. So blocking
|
|
173
|
+
stops no attacker, and everything it would stop is an honest extension whose declaration is
|
|
174
|
+
incomplete. Escalating to a block is left available and would need its own stated, announced
|
|
175
|
+
decision, the same condition ADR-0045 put on its own advisory.
|
|
176
|
+
|
|
177
|
+
**An extension that declares no hosts is not checked.** The declaration is the opt-in. Reading
|
|
178
|
+
"declared nothing" as "may reach nothing" would report against every extension in existence
|
|
179
|
+
today, and a report that fires on everything is not read. Note that `defineVendureExtension`
|
|
180
|
+
normalises an absent `outboundHosts` and an explicit `[]` to the same empty array, so
|
|
181
|
+
"deliberately talks to nothing" cannot currently be expressed distinctly from "did not say".
|
|
182
|
+
|
|
183
|
+
**A call the stack attributes to no extension is not checked either.** Vendure core's own
|
|
184
|
+
traffic, the database driver's, and anything reached from platform code land there, and
|
|
185
|
+
reporting them would drown the signal.
|
|
186
|
+
|
|
187
|
+
That is also the one real limit of reading the stack, and it is about where the code is rather
|
|
188
|
+
than about async boundaries. Attribution needs a frame belonging to the extension somewhere on
|
|
189
|
+
the stack when the socket opens. An extension that supplies a host as *data* and lets other
|
|
190
|
+
code dial it has no such frame, and that call is dropped. Crossing a timer, an await, or a
|
|
191
|
+
helper the extension did not write does not cause this — the extension's own frame is still
|
|
192
|
+
further down the same stack, and the suite covers all three.
|
|
193
|
+
|
|
194
|
+
### What it costs, and where it is imprecise
|
|
195
|
+
|
|
196
|
+
An allowed call costs one `Set` lookup. The host is compared against the union of every
|
|
197
|
+
declared host first, and only a host that appears in none of them is attributed, because
|
|
198
|
+
capturing a stack is the expensive part. The cost of that ordering is that an extension
|
|
199
|
+
reaching a host *another* extension declared is not reported.
|
|
200
|
+
|
|
201
|
+
Interception is `globalThis.fetch` plus `net.Socket.prototype.connect`. The socket is where
|
|
202
|
+
`http.request`, `https.request`, `net.connect` and every client library end up, and — unlike
|
|
203
|
+
replacing `net.connect` on the module object — it behaves the same whether the caller is ESM
|
|
204
|
+
or CommonJS. An ES module's named import is a snapshot taken at link time, so rewriting the
|
|
205
|
+
module object reaches CommonJS callers only, which would be the same control behaving
|
|
206
|
+
differently in the two module systems.
|
|
207
|
+
|
|
208
|
+
Each extension and host pair is reported once per process. An extension polling an undeclared
|
|
209
|
+
host every second has one thing wrong with it, and the same interception seeing it at both
|
|
210
|
+
`fetch` and the socket underneath is not two mistakes.
|
|
211
|
+
|
|
212
|
+
## The boundary, and where each half is enforced
|
|
213
|
+
|
|
214
|
+
A **Cloud Plugin** is a plugin Vendure Cloud supplies in place of one the customer would
|
|
215
|
+
otherwise declare — `CloudAssetServerPlugin` for `AssetServerPlugin`, and the three ADR-0044
|
|
216
|
+
lists as injected. The term is deliberately not "platform plugin", which reads as a plugin
|
|
217
|
+
belonging to Vendure Platform.
|
|
218
|
+
|
|
219
|
+
`PLATFORM_OWNED_FIELDS` is the single source for all four checks below, so they cannot
|
|
220
|
+
disagree about which `VendureConfig` fields Vendure Cloud owns.
|
|
221
|
+
|
|
222
|
+
| Check | Runs at | Catches |
|
|
223
|
+
| --- | --- | --- |
|
|
224
|
+
| The branded Cloud Config type | build | a config literal setting a platform-owned field |
|
|
225
|
+
| `listPlatformOwnedFieldsSetIn` | boot | a config assembled programmatically, past the type |
|
|
226
|
+
| `assertPlatformFieldsHold` | boot, after composition | a plugin that rewrote a platform-owned value after the Cloud Plugin ran |
|
|
227
|
+
| `assertNoCustomerPluginWroteAPlatformField` | boot, after composition | ADR-0044's converse check — a customer plugin whose write a Cloud Plugin overwrote |
|
|
228
|
+
|
|
229
|
+
Platform-owned keys stay *present* in the type carrying a brand that explains itself,
|
|
230
|
+
rather than being removed with `Omit`. ADR-0043 requires the build-time error to carry a
|
|
231
|
+
message we wrote, and an omitted key yields TypeScript's own wording instead. The brand's
|
|
232
|
+
text is derived from `PLATFORM_OWNED_FIELDS`, so the compile-time and runtime messages are
|
|
233
|
+
the same string.
|
|
234
|
+
|
|
235
|
+
## Three things worth knowing before changing this package
|
|
236
|
+
|
|
237
|
+
**A customer installs `@vendure/cloud` and imports from it, not from here.**
|
|
238
|
+
[ADR-0045](../../internal/adr/0045-cloud-ships-as-ordinary-npm-packages-the-customer-pins.md)
|
|
239
|
+
makes that package a facade whose exact-pinned dependencies are this package and the
|
|
240
|
+
separately published plugin packages. What it re-exports today and what is still to join it
|
|
241
|
+
is in [`packages/cloud/README.md`](../cloud/README.md).
|
|
242
|
+
|
|
243
|
+
**This package is a peer dependency of every plugin package, and a direct dependency of
|
|
244
|
+
nothing but the facade.** That is what stops a customer's tree holding two copies of it. Two
|
|
245
|
+
copies mean `instanceof CloudConfigError` stops holding across the seam, and a boot failure a
|
|
246
|
+
caller catches becomes an uncaught one — ADR-0045 argues the case, and the peer dependency is
|
|
247
|
+
what turns it from a silent boot failure into an install-time resolution error naming both
|
|
248
|
+
versions. Importing this package directly still works and is supported; it is what a plugin
|
|
249
|
+
package does.
|
|
250
|
+
|
|
251
|
+
**Nothing published is bundled or minified**, which is a decision rather than a property of
|
|
252
|
+
how the build happens to be written. A bundler is the route back to a second copy of
|
|
253
|
+
`@vendure/core`, and a minifier rewrites the class names that `CloudConfigError` and
|
|
254
|
+
`pluginName` read at runtime to build the messages ADR-0044 requires. `tsc` can do neither,
|
|
255
|
+
and `packages/cloud/test/published-output.test.ts` builds every publishable package and
|
|
256
|
+
asserts it: one emitted file per source file, every dependency still a bare import, and every
|
|
257
|
+
exported class still carrying its own name.
|
|
258
|
+
|
|
259
|
+
Read ADR-0045's packaging decision only. Its delivery decision — that the customer pins these
|
|
260
|
+
packages in their own lockfile and the platform reports a vulnerable version rather than
|
|
261
|
+
replacing it — is superseded by
|
|
262
|
+
[ADR-0046](../../internal/adr/0046-host-packages-are-dev-dependencies-and-the-runtime-supplies-production-copies.md).
|
|
263
|
+
Customers declare these as development dependencies and the Cloud Runtime supplies the
|
|
264
|
+
production copies, resolved from the `@vendure/core` version they declared.
|
|
265
|
+
|
|
266
|
+
**It has to stay loadable from CommonJS, and that constrains the code.**
|
|
267
|
+
[ADR-0049](../../internal/adr/0049-the-cloud-config-packages-are-consumable-from-commonjs.md):
|
|
268
|
+
this package emits ESM and lists `require` alongside `import` in its `exports` map, pointing at
|
|
269
|
+
the same file, so a customer whose application emits CommonJS can load it through Node's
|
|
270
|
+
`require(esm)`. Vendure's own application template emits CommonJS, so that is most customers
|
|
271
|
+
arriving from a self-hosted store.
|
|
272
|
+
|
|
273
|
+
The standing cost is that **no top-level await may appear anywhere in this package's module
|
|
274
|
+
graph**, ever. `require(esm)` refuses a graph containing one, at runtime in the customer's
|
|
275
|
+
container, and nothing here would notice: every suite in this repository loads the package as
|
|
276
|
+
ESM, where top-level await is legal. `test/managed-build.test.ts` builds and runs its fixture
|
|
277
|
+
in both module systems, which is what catches it.
|
|
278
|
+
|
|
279
|
+
**No runtime dependency on `@vendure/core`.** `@vendure/core` is a peer dependency and is
|
|
280
|
+
imported for types only. The plugin metadata key core reads is restated as a constant and
|
|
281
|
+
pinned to core's real value by a test, so a rename there fails our build rather than
|
|
282
|
+
silently disabling attribution.
|
|
283
|
+
|
|
284
|
+
## Runtime implementation
|
|
285
|
+
|
|
286
|
+
CLO-355 supplies the platform-owned server, worker and migrate entrypoints, managed Postgres
|
|
287
|
+
connection with rotating IAM credentials, structured logger, default cache/job queue/scheduler,
|
|
288
|
+
shallow health check, one-copy core guard and local substitutes. The managed build typechecks the
|
|
289
|
+
Cloud Config and installs the private implementation beside the single supplied copy of
|
|
290
|
+
`@vendure/core`.
|
|
291
|
+
|
|
292
|
+
The four wrapper plugins remain separate delivery work: CLO-399, CLO-400, CLO-401 and CLO-402.
|
|
293
|
+
Extension migration globs are resolved into the managed database configuration at boot. The
|
|
294
|
+
ownership snapshot on the Deployment row and the migration transaction, timeout and
|
|
295
|
+
orphaned-schema controls remain CLO-423 and CLO-424.
|
|
296
|
+
|
|
297
|
+
`apps/demo-store` is the reference Cloud Config: it contains only `vendure-config.ts`, with no
|
|
298
|
+
customer-owned run-mode entrypoints.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { VendureConfig } from "@vendure/core";
|
|
2
|
+
import type { PlatformOwnedFieldPath, PlatformOwnedFieldSpec } from "./platform-fields.js";
|
|
3
|
+
import type { CloudPluginEntry, VendurePluginEntry } from "./vendure-extension.js";
|
|
4
|
+
declare const platformOwnedBrand: unique symbol;
|
|
5
|
+
export type PlatformOwned<Explanation extends string> = {
|
|
6
|
+
readonly [platformOwnedBrand]: Explanation;
|
|
7
|
+
};
|
|
8
|
+
type ReasonFor<Path extends PlatformOwnedFieldPath> = Extract<PlatformOwnedFieldSpec, {
|
|
9
|
+
path: Path;
|
|
10
|
+
}>["reason"];
|
|
11
|
+
type ApiOptions = VendureConfig["apiOptions"];
|
|
12
|
+
type AuthOptions = VendureConfig["authOptions"];
|
|
13
|
+
type AssetOptions = NonNullable<VendureConfig["assetOptions"]>;
|
|
14
|
+
type SystemOptions = NonNullable<VendureConfig["systemOptions"]>;
|
|
15
|
+
type JobQueueOptions = NonNullable<VendureConfig["jobQueueOptions"]>;
|
|
16
|
+
type SchedulerOptions = NonNullable<VendureConfig["schedulerOptions"]>;
|
|
17
|
+
export type CloudApiOptions = Omit<ApiOptions, "port" | "hostname"> & {
|
|
18
|
+
port?: PlatformOwned<ReasonFor<"apiOptions.port">>;
|
|
19
|
+
hostname?: PlatformOwned<ReasonFor<"apiOptions.hostname">>;
|
|
20
|
+
};
|
|
21
|
+
export type CloudAuthOptions = Omit<AuthOptions, "superadminCredentials" | "sessionCacheStrategy" | "sessionCacheTTL"> & {
|
|
22
|
+
superadminCredentials?: PlatformOwned<ReasonFor<"authOptions.superadminCredentials">>;
|
|
23
|
+
sessionCacheStrategy?: PlatformOwned<ReasonFor<"authOptions.sessionCacheStrategy">>;
|
|
24
|
+
sessionCacheTTL?: PlatformOwned<ReasonFor<"authOptions.sessionCacheTTL">>;
|
|
25
|
+
};
|
|
26
|
+
export type CloudAssetOptions = Omit<AssetOptions, "assetStorageStrategy"> & {
|
|
27
|
+
assetStorageStrategy?: PlatformOwned<ReasonFor<"assetOptions.assetStorageStrategy">>;
|
|
28
|
+
};
|
|
29
|
+
export type CloudSystemOptions = Omit<SystemOptions, "cacheStrategy" | "healthChecks"> & {
|
|
30
|
+
cacheStrategy?: PlatformOwned<ReasonFor<"systemOptions.cacheStrategy">>;
|
|
31
|
+
healthChecks?: PlatformOwned<ReasonFor<"systemOptions.healthChecks">>;
|
|
32
|
+
};
|
|
33
|
+
export type CloudJobQueueOptions = Omit<JobQueueOptions, "jobQueueStrategy"> & {
|
|
34
|
+
jobQueueStrategy?: PlatformOwned<ReasonFor<"jobQueueOptions.jobQueueStrategy">>;
|
|
35
|
+
};
|
|
36
|
+
export type CloudSchedulerOptions = Omit<SchedulerOptions, "schedulerStrategy" | "runTasksInWorkerOnly"> & {
|
|
37
|
+
schedulerStrategy?: PlatformOwned<ReasonFor<"schedulerOptions.schedulerStrategy">>;
|
|
38
|
+
runTasksInWorkerOnly?: PlatformOwned<ReasonFor<"schedulerOptions.runTasksInWorkerOnly">>;
|
|
39
|
+
};
|
|
40
|
+
export type CloudConfig = Partial<Omit<VendureConfig, "plugins" | "dbConnectionOptions" | "logger" | "apiOptions" | "authOptions" | "assetOptions" | "systemOptions" | "jobQueueOptions" | "schedulerOptions">> & {
|
|
41
|
+
plugins?: CloudPluginEntry[];
|
|
42
|
+
dbConnectionOptions?: PlatformOwned<ReasonFor<"dbConnectionOptions">>;
|
|
43
|
+
logger?: PlatformOwned<ReasonFor<"logger">>;
|
|
44
|
+
apiOptions?: CloudApiOptions;
|
|
45
|
+
authOptions?: CloudAuthOptions;
|
|
46
|
+
assetOptions?: CloudAssetOptions;
|
|
47
|
+
systemOptions?: CloudSystemOptions;
|
|
48
|
+
jobQueueOptions?: CloudJobQueueOptions;
|
|
49
|
+
schedulerOptions?: CloudSchedulerOptions;
|
|
50
|
+
};
|
|
51
|
+
export type FlattenedCloudConfig = Omit<CloudConfig, "plugins"> & {
|
|
52
|
+
plugins: VendurePluginEntry[];
|
|
53
|
+
};
|
|
54
|
+
export {};
|
|
55
|
+
//# sourceMappingURL=cloud-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-config.d.ts","sourceRoot":"","sources":["../src/cloud-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-config.js","sourceRoot":"","sources":["../src/cloud-config.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { FlattenedCloudConfig, PlatformOwned } from "./cloud-config.js";
|
|
2
|
+
import { CloudConfigError } from "./errors.js";
|
|
3
|
+
import type { VendurePluginEntry } from "./vendure-extension.js";
|
|
4
|
+
export declare const CLOUD_DASHBOARD_PLUGIN_KIND = "dashboard";
|
|
5
|
+
export declare const DEFAULT_DASHBOARD_ROUTE = "dashboard";
|
|
6
|
+
export declare const PLATFORM_SUPPLIED_DASHBOARD_OPTIONS: {
|
|
7
|
+
readonly appDir: "The Dashboard's files are produced by the managed build, and Vendure Cloud tells the plugin where they are. A path set here would not exist in the runtime image. Remove appDir.";
|
|
8
|
+
readonly viteDevServerPort: "The Vite dev server does not run in a Vendure Cloud process, so there is nothing to probe for and no request is proxied to it. Remove viteDevServerPort. It still applies when you run the same configuration locally.";
|
|
9
|
+
};
|
|
10
|
+
export interface CloudDashboardPluginOptions {
|
|
11
|
+
route?: string;
|
|
12
|
+
appDir?: PlatformOwned<(typeof PLATFORM_SUPPLIED_DASHBOARD_OPTIONS)["appDir"]>;
|
|
13
|
+
viteDevServerPort?: PlatformOwned<(typeof PLATFORM_SUPPLIED_DASHBOARD_OPTIONS)["viteDevServerPort"]>;
|
|
14
|
+
}
|
|
15
|
+
export interface CloudDashboardSettings {
|
|
16
|
+
route: string;
|
|
17
|
+
}
|
|
18
|
+
export declare class InvalidDashboardRouteError extends CloudConfigError {
|
|
19
|
+
readonly route: string;
|
|
20
|
+
constructor(route: string, reason: string);
|
|
21
|
+
}
|
|
22
|
+
export declare const CloudDashboardPlugin: {
|
|
23
|
+
new (): {};
|
|
24
|
+
} & {
|
|
25
|
+
init(options?: CloudDashboardPluginOptions): VendurePluginEntry;
|
|
26
|
+
};
|
|
27
|
+
export declare function resolveDashboardSettings(config: FlattenedCloudConfig): CloudDashboardSettings;
|
|
28
|
+
//# sourceMappingURL=cloud-dashboard-plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-dashboard-plugin.d.ts","sourceRoot":"","sources":["../src/cloud-dashboard-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAE7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAOjE,eAAO,MAAM,2BAA2B,cAAc,CAAC;AAIvD,eAAO,MAAM,uBAAuB,cAAc,CAAC;AAMnD,eAAO,MAAM,mCAAmC;;;CAKtC,CAAC;AAEX,MAAM,WAAW,2BAA2B;IAG1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,aAAa,CAAC,CAAC,OAAO,mCAAmC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC/E,iBAAiB,CAAC,EAAE,aAAa,CAC/B,CAAC,OAAO,mCAAmC,CAAC,CAAC,mBAAmB,CAAC,CAClE,CAAC;CACH;AAID,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAC;CACf;AAeD,qBAAa,0BAA2B,SAAQ,gBAAgB;IAC9D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBAEX,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAM1C;AAsBD,eAAO,MAAM,oBAAoB;;;mBAOf,2BAA2B,GAAQ,kBAAkB;CAUtE,CAAC;AAaF,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,oBAAoB,GAAG,sBAAsB,CAS7F"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { declareCloudPlugin, listCloudPluginDeclarations } from "./cloud-plugin-declaration.js";
|
|
2
|
+
import { CloudConfigError } from "./errors.js";
|
|
3
|
+
// ADR-0044 rules `DashboardPlugin` platform-supplied and wrapped, with `route` the only option the
|
|
4
|
+
// customer keeps. This is the wrapper's declaration side: what a customer writes in their
|
|
5
|
+
// configuration file. The plugin that serves the Dashboard is built by the platform runtime, which
|
|
6
|
+
// is the only place that knows where the managed build put the Dashboard's files.
|
|
7
|
+
export const CLOUD_DASHBOARD_PLUGIN_KIND = "dashboard";
|
|
8
|
+
// The same default the upstream plugin documents, so a customer moving an existing store onto
|
|
9
|
+
// Vendure Cloud finds the Dashboard where they left it.
|
|
10
|
+
export const DEFAULT_DASHBOARD_ROUTE = "dashboard";
|
|
11
|
+
// The two options that leave the type, with the explanation each one's build-time error carries.
|
|
12
|
+
// Written as literals rather than assembled, for the reason `platform-fields.ts` records: the
|
|
13
|
+
// explanation is what TypeScript prints when the field is set, so it is the message and not a key
|
|
14
|
+
// into one.
|
|
15
|
+
export const PLATFORM_SUPPLIED_DASHBOARD_OPTIONS = {
|
|
16
|
+
appDir: "The Dashboard's files are produced by the managed build, and Vendure Cloud tells the plugin where they are. A path set here would not exist in the runtime image. Remove appDir.",
|
|
17
|
+
viteDevServerPort: "The Vite dev server does not run in a Vendure Cloud process, so there is nothing to probe for and no request is proxied to it. Remove viteDevServerPort. It still applies when you run the same configuration locally.",
|
|
18
|
+
};
|
|
19
|
+
const DEFAULT_DASHBOARD_SETTINGS = { route: DEFAULT_DASHBOARD_ROUTE };
|
|
20
|
+
// The route is one path segment. Nest mounts the Dashboard's middleware at it, and the Dashboard's
|
|
21
|
+
// own `index.html` carries it as a `<base>` tag, so a value with a slash in it produces a mount
|
|
22
|
+
// path and a base tag that disagree and a Dashboard that loads nothing. Starting at a letter or a
|
|
23
|
+
// digit rules out `.` and `..`, which resolve to somewhere else again.
|
|
24
|
+
const ROUTE_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._~-]*$/;
|
|
25
|
+
// Vendure's own defaults for the two API paths, restated because a Cloud Config that sets neither
|
|
26
|
+
// still serves them there and this check runs before core has filled them in.
|
|
27
|
+
const DEFAULT_ADMIN_API_PATH = "admin-api";
|
|
28
|
+
const DEFAULT_SHOP_API_PATH = "shop-api";
|
|
29
|
+
export class InvalidDashboardRouteError extends CloudConfigError {
|
|
30
|
+
route;
|
|
31
|
+
constructor(route, reason) {
|
|
32
|
+
super(`Vendure Cloud: the Dashboard cannot be served at "${route}".\n ${reason}\n Give CloudDashboardPlugin one path segment, such as { route: "${DEFAULT_DASHBOARD_ROUTE}" }.\n`);
|
|
33
|
+
this.route = route;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Runs where the customer wrote the route, so the message arrives before anything is deployed.
|
|
37
|
+
// Nothing in the type system can reach this: `route` is an ordinary string and the values it
|
|
38
|
+
// rejects are ordinary strings too.
|
|
39
|
+
function assertRouteShape(route) {
|
|
40
|
+
if (route.length === 0) {
|
|
41
|
+
throw new InvalidDashboardRouteError(route, "The route is empty.");
|
|
42
|
+
}
|
|
43
|
+
if (!ROUTE_PATTERN.test(route)) {
|
|
44
|
+
throw new InvalidDashboardRouteError(route, "A route is one path segment: letters, digits, and then any of . _ ~ - after the first character. Slashes, spaces and a leading dot are not part of one.");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// A class carrying the default declaration, with `init` on it. A customer who moves the Dashboard
|
|
48
|
+
// writes `CloudDashboardPlugin.init({ route })`; one who wants the default route but wants the
|
|
49
|
+
// Dashboard visible in their file can write a bare `CloudDashboardPlugin`. The second is
|
|
50
|
+
// redundant, because the platform injects the default declaration when the customer writes
|
|
51
|
+
// neither, and it is supported so that writing it is not read as a plugin nobody recognises.
|
|
52
|
+
export const CloudDashboardPlugin = Object.assign(declareCloudPlugin("CloudDashboardPlugin", CLOUD_DASHBOARD_PLUGIN_KIND, DEFAULT_DASHBOARD_SETTINGS), {
|
|
53
|
+
init(options = {}) {
|
|
54
|
+
const route = options.route ?? DEFAULT_DASHBOARD_ROUTE;
|
|
55
|
+
assertRouteShape(route);
|
|
56
|
+
return declareCloudPlugin("CloudDashboardPlugin", CLOUD_DASHBOARD_PLUGIN_KIND, { route });
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
// ADR-0044's rule that a wrapped plugin the customer declared replaces the platform's injected
|
|
60
|
+
// default rather than joining it: the customer's declaration when there is one, the default when
|
|
61
|
+
// there is not, and never both.
|
|
62
|
+
//
|
|
63
|
+
// A configuration declaring it more than once takes the last, which is what the upstream plugin's
|
|
64
|
+
// static options do and therefore what a customer who has met that plugin expects. ADR-0044 calls a
|
|
65
|
+
// double declaration a documentation problem rather than something to defend against, so this
|
|
66
|
+
// resolves it quietly rather than refusing the boot over it.
|
|
67
|
+
//
|
|
68
|
+
// It takes the whole configuration rather than the plugins array, because the route has to be
|
|
69
|
+
// checked against the paths the customer's own API options claim.
|
|
70
|
+
export function resolveDashboardSettings(config) {
|
|
71
|
+
const declarations = listCloudPluginDeclarations(config.plugins, CLOUD_DASHBOARD_PLUGIN_KIND);
|
|
72
|
+
const settings = declarations[declarations.length - 1] ?? { options: DEFAULT_DASHBOARD_SETTINGS };
|
|
73
|
+
assertRouteShape(settings.options.route);
|
|
74
|
+
assertRouteIsFree(settings.options.route, config);
|
|
75
|
+
return settings.options;
|
|
76
|
+
}
|
|
77
|
+
// A route matching an API path is served by whichever of the two Nest matches first, so the
|
|
78
|
+
// customer loses one of them with nothing said. Checked here rather than in `init`, because it is
|
|
79
|
+
// the only point at which the route and the API paths are both known.
|
|
80
|
+
function assertRouteIsFree(route, config) {
|
|
81
|
+
const claimed = [
|
|
82
|
+
["the Admin API", config.apiOptions?.adminApiPath ?? DEFAULT_ADMIN_API_PATH],
|
|
83
|
+
["the Shop API", config.apiOptions?.shopApiPath ?? DEFAULT_SHOP_API_PATH],
|
|
84
|
+
];
|
|
85
|
+
for (const [what, path] of claimed) {
|
|
86
|
+
if (path === route) {
|
|
87
|
+
throw new InvalidDashboardRouteError(route, `${what} is served at the same path. Move one of the two.`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=cloud-dashboard-plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-dashboard-plugin.js","sourceRoot":"","sources":["../src/cloud-dashboard-plugin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PluginLike } from "./plugin-attribution.js";
|
|
2
|
+
export interface CloudPluginDeclaration<Options = unknown> {
|
|
3
|
+
readonly kind: string;
|
|
4
|
+
readonly options: Options;
|
|
5
|
+
}
|
|
6
|
+
export declare function declareCloudPlugin<Options>(name: string, kind: string, options: Options): {
|
|
7
|
+
new (): {};
|
|
8
|
+
};
|
|
9
|
+
export declare function readCloudPluginDeclaration(entry: unknown): CloudPluginDeclaration | undefined;
|
|
10
|
+
export declare function isCloudPluginDeclaration(entry: unknown): boolean;
|
|
11
|
+
export declare function listCloudPluginDeclarations<Options>(plugins: readonly PluginLike[], kind: string): CloudPluginDeclaration<Options>[];
|
|
12
|
+
//# sourceMappingURL=cloud-plugin-declaration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-plugin-declaration.d.ts","sourceRoot":"","sources":["../src/cloud-plugin-declaration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAkB1D,MAAM,WAAW,sBAAsB,CAAC,OAAO,GAAG,OAAO;IAGvD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAMD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;;EAWvF;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,sBAAsB,GAAG,SAAS,CAK7F;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEhE;AAKD,wBAAgB,2BAA2B,CAAC,OAAO,EACjD,OAAO,EAAE,SAAS,UAAU,EAAE,EAC9B,IAAI,EAAE,MAAM,GACX,sBAAsB,CAAC,OAAO,CAAC,EAAE,CASnC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// How a customer declares a wrapped platform plugin (ADR-0044, shape 2). What they write is not
|
|
2
|
+
// the plugin that runs. The plugin that runs needs `@vendure/core` and the platform's own values,
|
|
3
|
+
// and build tooling imports the configuration file where neither is available. So
|
|
4
|
+
// `CloudDashboardPlugin.init({ route })` returns a marker carrying the options and nothing else,
|
|
5
|
+
// and the platform runtime builds the plugin that implements it during composition.
|
|
6
|
+
//
|
|
7
|
+
// The marker is a class rather than a plain object so that it is an ordinary member of the plugins
|
|
8
|
+
// array. It satisfies `VendurePluginEntry`, it reads as a plugin in the file, and the extension
|
|
9
|
+
// flattening and the attribution wrapping walk past it without a special case. Composition removes
|
|
10
|
+
// it before core sees it.
|
|
11
|
+
//
|
|
12
|
+
// Registered rather than a module-local symbol, for the reason the config handle's symbol is
|
|
13
|
+
// (ADR-0043): a second copy of this package in the tree would otherwise produce markers the
|
|
14
|
+
// composing copy does not recognise, and the customer's declared route would be silently ignored.
|
|
15
|
+
const DECLARATION = Symbol.for("vendure-cloud.cloud-plugin-declaration");
|
|
16
|
+
// The return type is inferred rather than written as `VendurePluginEntry`, so a caller can add a
|
|
17
|
+
// static `init` to the result and still have an ordinary member of the plugins array.
|
|
18
|
+
export function declareCloudPlugin(name, kind, options) {
|
|
19
|
+
// Named through a computed key because a class expression takes its name from the binding it is
|
|
20
|
+
// assigned to, and this one is returned rather than assigned. The name reaches boot messages and
|
|
21
|
+
// build logs, and `pluginName` reports a class without one as "an anonymous plugin".
|
|
22
|
+
const marker = { [name]: class {
|
|
23
|
+
} }[name];
|
|
24
|
+
const declaration = { kind, options };
|
|
25
|
+
// Non-enumerable, so the marker spreads and serialises as an ordinary empty class. A visible
|
|
26
|
+
// property would appear in anything that inspects the plugins array — a build log, a diagnostic
|
|
27
|
+
// dump — as configuration the customer did not write.
|
|
28
|
+
Object.defineProperty(marker, DECLARATION, { value: declaration });
|
|
29
|
+
return marker;
|
|
30
|
+
}
|
|
31
|
+
export function readCloudPluginDeclaration(entry) {
|
|
32
|
+
if (typeof entry !== "function" && (typeof entry !== "object" || entry === null)) {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
return entry[DECLARATION];
|
|
36
|
+
}
|
|
37
|
+
export function isCloudPluginDeclaration(entry) {
|
|
38
|
+
return readCloudPluginDeclaration(entry) !== undefined;
|
|
39
|
+
}
|
|
40
|
+
// The declarations of one kind, in the order the customer wrote them. The platform runtime reads
|
|
41
|
+
// these to build the plugins that implement them, and composition drops the markers from the array
|
|
42
|
+
// it hands core.
|
|
43
|
+
export function listCloudPluginDeclarations(plugins, kind) {
|
|
44
|
+
const declarations = [];
|
|
45
|
+
for (const plugin of plugins) {
|
|
46
|
+
const declaration = readCloudPluginDeclaration(plugin);
|
|
47
|
+
if (declaration?.kind === kind) {
|
|
48
|
+
declarations.push(declaration);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return declarations;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=cloud-plugin-declaration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-plugin-declaration.js","sourceRoot":"","sources":["../src/cloud-plugin-declaration.ts"],"names":[],"mappings":"AAEA,gGAAgG;AAChG,kGAAkG;AAClG,kFAAkF;AAClF,iGAAiG;AACjG,oFAAoF;AACpF,EAAE;AACF,mGAAmG;AACnG,gGAAgG;AAChG,mGAAmG;AACnG,0BAA0B;AAC1B,EAAE;AACF,6FAA6F;AAC7F,4FAA4F;AAC5F,kGAAkG;AAClG,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC;AAWzE,iGAAiG;AACjG,sFAAsF;AACtF,MAAM,UAAU,kBAAkB,CAAU,IAAY,EAAE,IAAY,EAAE,OAAgB;IACtF,gGAAgG;IAChG,iGAAiG;IACjG,qFAAqF;IACrF,MAAM,MAAM,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE;SAAQ,EAAE,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,WAAW,GAAoC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IACvE,6FAA6F;IAC7F,gGAAgG;IAChG,sDAAsD;IACtD,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;IACnE,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,KAAc;IACvD,IAAI,OAAO,KAAK,KAAK,UAAU,IAAI,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC,EAAE,CAAC;QACjF,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAQ,KAA2B,CAAC,WAAW,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,KAAc;IACrD,OAAO,0BAA0B,CAAC,KAAK,CAAC,KAAK,SAAS,CAAC;AACzD,CAAC;AAED,iGAAiG;AACjG,mGAAmG;AACnG,iBAAiB;AACjB,MAAM,UAAU,2BAA2B,CACzC,OAA8B,EAC9B,IAAY;IAEZ,MAAM,YAAY,GAAsC,EAAE,CAAC;IAC3D,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,WAAW,GAAG,0BAA0B,CAAC,MAAM,CAAC,CAAC;QACvD,IAAI,WAAW,EAAE,IAAI,KAAK,IAAI,EAAE,CAAC;YAC/B,YAAY,CAAC,IAAI,CAAC,WAA8C,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IACD,OAAO,YAAY,CAAC;AACtB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { VendureConfig, VendurePluginMetadata } from "@vendure/core";
|
|
2
|
+
import { type CloudConfigHandle } from "./define-cloud-config.js";
|
|
3
|
+
import { CloudConfigError } from "./errors.js";
|
|
4
|
+
import { type PlatformOwnedField, type PlatformOwnedFieldPath } from "./platform-fields.js";
|
|
5
|
+
import { type PluginLike } from "./plugin-attribution.js";
|
|
6
|
+
export declare const CLOUD_PLUGIN_COMPATIBILITY = "^3.7.0";
|
|
7
|
+
export type PlatformFieldValues = Partial<Record<PlatformOwnedFieldPath, unknown>>;
|
|
8
|
+
export declare function platformDefaultFieldValues(sessionCacheStrategy: unknown): PlatformFieldValues;
|
|
9
|
+
export interface ComposeCloudConfigOptions {
|
|
10
|
+
values: PlatformFieldValues;
|
|
11
|
+
cloudPlugins: readonly PluginLike[];
|
|
12
|
+
guardPluginName: string;
|
|
13
|
+
vendurePlugin: (metadata: VendurePluginMetadata) => ClassDecorator;
|
|
14
|
+
}
|
|
15
|
+
export declare function composeCloudConfig(handle: CloudConfigHandle, options: ComposeCloudConfigOptions): VendureConfig;
|
|
16
|
+
export declare function hoistCloudPluginsLast(customerPlugins: readonly PluginLike[], cloudPlugins: readonly PluginLike[]): PluginLike[];
|
|
17
|
+
export declare function applyPlatformFields(config: object, values: PlatformFieldValues): void;
|
|
18
|
+
export declare class PlatformFieldOverwrittenError extends CloudConfigError {
|
|
19
|
+
readonly fields: readonly PlatformOwnedField[];
|
|
20
|
+
constructor(fields: readonly PlatformOwnedField[]);
|
|
21
|
+
}
|
|
22
|
+
export declare function assertPlatformFieldsHold(config: unknown, values: PlatformFieldValues): void;
|
|
23
|
+
//# sourceMappingURL=composition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composition.d.ts","sourceRoot":"","sources":["../src/composition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAE1E,OAAO,EAAE,KAAK,iBAAiB,EAAmB,MAAM,0BAA0B,CAAC;AACnF,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAE5B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAEL,KAAK,UAAU,EAIhB,MAAM,yBAAyB,CAAC;AAMjC,eAAO,MAAM,0BAA0B,WAAW,CAAC;AAKnD,MAAM,MAAM,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,sBAAsB,EAAE,OAAO,CAAC,CAAC,CAAC;AAEnF,wBAAgB,0BAA0B,CAAC,oBAAoB,EAAE,OAAO,GAAG,mBAAmB,CAS7F;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,mBAAmB,CAAC;IAC5B,YAAY,EAAE,SAAS,UAAU,EAAE,CAAC;IACpC,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,CAAC,QAAQ,EAAE,qBAAqB,KAAK,cAAc,CAAC;CACpE;AAKD,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,yBAAyB,GACjC,aAAa,CA8Cf;AAuBD,wBAAgB,qBAAqB,CACnC,eAAe,EAAE,SAAS,UAAU,EAAE,EACtC,YAAY,EAAE,SAAS,UAAU,EAAE,GAClC,UAAU,EAAE,CAGd;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAMrF;AAQD,qBAAa,6BAA8B,SAAQ,gBAAgB;IACjE,QAAQ,CAAC,MAAM,EAAE,SAAS,kBAAkB,EAAE,CAAC;gBAEnC,MAAM,EAAE,SAAS,kBAAkB,EAAE;CASlD;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAU3F"}
|