zuplo 6.71.10 → 6.71.11
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.
|
@@ -88,12 +88,26 @@ zuplo ca-certificate list --account your-account
|
|
|
88
88
|
See the [`ca-certificate` CLI reference](../cli/ca-certificate-create.mdx) for
|
|
89
89
|
all available subcommands (`create`, `list`, `describe`, `update`, `delete`).
|
|
90
90
|
|
|
91
|
-
:::
|
|
91
|
+
:::caution{title="Upload the self-signed root CA, not an intermediate"}
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
Zuplo must build a complete chain from the presented client certificate up to a
|
|
94
|
+
trust anchor. Upload the **self-signed root** CA that anchors the chain — not an
|
|
95
|
+
intermediate or subordinate CA. Uploading a subordinate CA is the most common
|
|
96
|
+
cause of the `FAILED to get issuer certificate` error (see
|
|
97
|
+
[Troubleshooting](#failed-to-get-issuer-certificate)).
|
|
98
|
+
|
|
99
|
+
To confirm a certificate is a self-signed root, check that its subject and
|
|
100
|
+
issuer are identical:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
openssl x509 -in ca.pem -noout -subject -issuer -nameopt RFC2253
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
If `subject` and `issuer` match, it's a root. If they differ, the file is an
|
|
107
|
+
intermediate CA — trace the chain to the root and upload that instead. When your
|
|
108
|
+
client certificates are issued by an intermediate CA, clients still send the
|
|
109
|
+
leaf certificate plus any intermediates when they connect (see
|
|
110
|
+
[Test with curl](#4-test-with-curl)).
|
|
97
111
|
|
|
98
112
|
:::
|
|
99
113
|
|
|
@@ -315,11 +329,73 @@ the policy in a working-copy or preview environment.
|
|
|
315
329
|
`context.incomingRequestProperties.clientMtlsVerificationReason` to see why
|
|
316
330
|
verification failed.
|
|
317
331
|
|
|
332
|
+
### `FAILED to get issuer certificate`
|
|
333
|
+
|
|
334
|
+
This error means Zuplo can't build a complete chain from the presented client
|
|
335
|
+
certificate up to a trusted root. The usual cause is uploading an **intermediate
|
|
336
|
+
or subordinate CA** instead of the **self-signed root** CA that anchors the
|
|
337
|
+
chain.
|
|
338
|
+
|
|
339
|
+
Confirm what you uploaded. A self-signed root has an identical subject and
|
|
340
|
+
issuer:
|
|
341
|
+
|
|
342
|
+
```bash
|
|
343
|
+
openssl x509 -in ca.pem -noout -subject -issuer -nameopt RFC2253
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
If `subject` and `issuer` differ, the file is an intermediate CA. Find the root
|
|
347
|
+
that anchors the chain and re-upload it:
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
# Remove the incorrect CA
|
|
351
|
+
zuplo ca-certificate delete --cert-id mtlsca_abc123 --account your-account
|
|
352
|
+
|
|
353
|
+
# Upload the self-signed root instead
|
|
354
|
+
zuplo ca-certificate create --name my_ca --cert ./root-ca.pem --account your-account
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
If your CA is an Active Directory Certificate Services (AD CS) deployment,
|
|
358
|
+
export the issuing CA's own certificate and inspect it:
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
# Export the CA certificate (often DER-encoded)
|
|
362
|
+
certutil -ca.cert ca.cer
|
|
363
|
+
|
|
364
|
+
# Convert DER to the PEM format Zuplo requires
|
|
365
|
+
openssl x509 -inform der -in ca.cer -out ca.pem
|
|
366
|
+
|
|
367
|
+
# Verify subject == issuer (a root); if not, export the root above it instead
|
|
368
|
+
openssl x509 -in ca.pem -noout -subject -issuer -nameopt RFC2253
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### `client certificate metadata not provided`
|
|
372
|
+
|
|
373
|
+
The certificate verified at the edge, but the parsed certificate wasn't
|
|
374
|
+
forwarded to your gateway workers, so `request.user.data.mtlsAuth` is empty even
|
|
375
|
+
though the request was authenticated.
|
|
376
|
+
|
|
377
|
+
The most common cause is an **oversized leaf client certificate**. Zuplo's edge
|
|
378
|
+
can only forward client certificates up to roughly 10 KB of DER-encoded data.
|
|
379
|
+
Certificates with large RSA keys, many Subject Alternative Names, or large
|
|
380
|
+
custom extensions can exceed this. Check the DER size of the leaf certificate:
|
|
381
|
+
|
|
382
|
+
```bash
|
|
383
|
+
openssl x509 -in client.pem -outform der | wc -c
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
If the result is near or above ~10,000 bytes, reissue a smaller leaf
|
|
387
|
+
certificate. Trim unnecessary extensions and SANs, or switch to ECDSA keys
|
|
388
|
+
instead of large (4096-bit) RSA keys. This limit applies to the **leaf**
|
|
389
|
+
certificate the edge forwards, not the full chain.
|
|
390
|
+
|
|
318
391
|
### `request.user.data.mtlsAuth` is missing
|
|
319
392
|
|
|
320
393
|
- The policy only attaches metadata when a parseable client certificate is
|
|
321
394
|
present on the request. Confirm the client is sending one.
|
|
322
395
|
- Verify the route includes the `mtls-auth-inbound` policy.
|
|
396
|
+
- If the certificate verifies but metadata is still missing, check the leaf
|
|
397
|
+
certificate size (see
|
|
398
|
+
[`client certificate metadata not provided`](#client-certificate-metadata-not-provided)).
|
|
323
399
|
|
|
324
400
|
### Custom domains
|
|
325
401
|
|
|
@@ -331,6 +407,27 @@ that custom domain.
|
|
|
331
407
|
If you add a custom domain later and your clients aren't being verified against
|
|
332
408
|
it, contact [support@zuplo.com](mailto:support@zuplo.com).
|
|
333
409
|
|
|
410
|
+
### Custom domains behind your own CDN
|
|
411
|
+
|
|
412
|
+
Inbound mTLS requires the TLS handshake to terminate at Zuplo's edge, because
|
|
413
|
+
that's where the client certificate is verified and parsed. If you front your
|
|
414
|
+
Zuplo gateway with **your own CDN** (for example, your own Cloudflare zone) that
|
|
415
|
+
terminates TLS before traffic reaches Zuplo, the handshake — and the client
|
|
416
|
+
certificate — ends at your CDN. Zuplo never sees the certificate, so the
|
|
417
|
+
`mtls-auth-inbound` policy has nothing to verify.
|
|
418
|
+
|
|
419
|
+
You have two supported options:
|
|
420
|
+
|
|
421
|
+
- **Let Zuplo terminate TLS.** Point clients at a Zuplo-managed gateway domain,
|
|
422
|
+
or configure your custom domain directly on Zuplo so Zuplo terminates TLS. The
|
|
423
|
+
client certificate then reaches Zuplo's edge and inbound mTLS works as
|
|
424
|
+
documented above.
|
|
425
|
+
- **Verify mTLS at your CDN.** If you must keep your own CDN in front, terminate
|
|
426
|
+
and verify the client certificate at the CDN, then forward the verified
|
|
427
|
+
identity to Zuplo in a request header. Validate that header in a Zuplo policy
|
|
428
|
+
instead of relying on `mtls-auth-inbound`. Make sure the header can't be
|
|
429
|
+
spoofed by clients that bypass your CDN.
|
|
430
|
+
|
|
334
431
|
## Additional resources
|
|
335
432
|
|
|
336
433
|
- [`mtls-auth-inbound` policy reference](../policies/mtls-auth-inbound.mdx)
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Transform Route Parameters for URL Rewrite
|
|
3
|
+
sidebar_label: Transform Route Parameters
|
|
4
|
+
description:
|
|
5
|
+
Learn how to use an inbound policy to transform route parameter values before
|
|
6
|
+
the URL Rewrite handler forwards the request to your backend.
|
|
7
|
+
tags:
|
|
8
|
+
- custom-code
|
|
9
|
+
- backends
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
This guide explains how to transform incoming route parameter values in an
|
|
13
|
+
inbound policy before the [URL Rewrite handler](../handlers/url-rewrite.mdx)
|
|
14
|
+
uses them to build the upstream URL. This pattern is useful when your public API
|
|
15
|
+
paths use different naming conventions than your internal backend.
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
|
|
19
|
+
When you use the URL Rewrite handler, it builds the upstream URL by
|
|
20
|
+
interpolating values like `${params.resourceType}` directly from the incoming
|
|
21
|
+
route parameters. Sometimes, however, you need to **change** those values before
|
|
22
|
+
the rewrite happens. Common scenarios include:
|
|
23
|
+
|
|
24
|
+
- **Value mapping** — translating a public-facing parameter like `order` to an
|
|
25
|
+
internal value like `customerorder`
|
|
26
|
+
- **Case normalization** — converting `Products` to `products` before forwarding
|
|
27
|
+
- **Path translation** — mapping user-friendly slugs to internal identifiers
|
|
28
|
+
|
|
29
|
+
The recommended approach is to read the route parameters in an inbound policy,
|
|
30
|
+
transform them, store the results on
|
|
31
|
+
[`context.custom`](../programmable-api/zuplo-context.mdx), and reference the
|
|
32
|
+
transformed values in the URL Rewrite pattern.
|
|
33
|
+
|
|
34
|
+
## Step-by-Step Example
|
|
35
|
+
|
|
36
|
+
The solution has three parts: an **inbound policy** that reads `request.params`
|
|
37
|
+
and stores transformed values on `context.custom`, a **URL Rewrite handler**
|
|
38
|
+
that references those values using `${context.custom.*}` in the
|
|
39
|
+
`rewritePattern`, and **route configuration** that wires the two together.
|
|
40
|
+
|
|
41
|
+
Imagine your public API exposes a route like `/api/:resourceType/:resourceId`,
|
|
42
|
+
but your backend expects the resource type to be prefixed with `customer`. A
|
|
43
|
+
request to `/api/order/123` should be forwarded to
|
|
44
|
+
`https://backend.example.com/api/customerorder/123`.
|
|
45
|
+
|
|
46
|
+
### 1. Write the Inbound Policy
|
|
47
|
+
|
|
48
|
+
Create a custom inbound policy that reads the route parameters, transforms the
|
|
49
|
+
values, and stores them on `context.custom`:
|
|
50
|
+
|
|
51
|
+
```ts title="modules/transform-params.ts"
|
|
52
|
+
import { ZuploContext, ZuploRequest } from "@zuplo/runtime";
|
|
53
|
+
|
|
54
|
+
export default async function (
|
|
55
|
+
request: ZuploRequest,
|
|
56
|
+
context: ZuploContext,
|
|
57
|
+
options: any,
|
|
58
|
+
policyName: string,
|
|
59
|
+
): Promise<ZuploRequest | Response> {
|
|
60
|
+
// Read the original route parameter
|
|
61
|
+
const resourceType = request.params.resourceType;
|
|
62
|
+
|
|
63
|
+
// Transform the value — prefix with "customer"
|
|
64
|
+
const transformedResourceType = `customer${resourceType}`;
|
|
65
|
+
|
|
66
|
+
// Store the transformed value on context.custom
|
|
67
|
+
context.custom.transformedResourceType = transformedResourceType;
|
|
68
|
+
|
|
69
|
+
context.log.info({
|
|
70
|
+
message: "Transformed route parameter",
|
|
71
|
+
original: resourceType,
|
|
72
|
+
transformed: transformedResourceType,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return request;
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 2. Register the Policy
|
|
80
|
+
|
|
81
|
+
Add the policy to `config/policies.json`:
|
|
82
|
+
|
|
83
|
+
```json title="config/policies.json"
|
|
84
|
+
{
|
|
85
|
+
"policies": [
|
|
86
|
+
{
|
|
87
|
+
"name": "transform-params",
|
|
88
|
+
"policyType": "custom-code-inbound",
|
|
89
|
+
"handler": {
|
|
90
|
+
"export": "default",
|
|
91
|
+
"module": "$import(./modules/transform-params)"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 3. Configure the Route
|
|
99
|
+
|
|
100
|
+
Define the route in `config/routes.oas.json` with the inbound policy and a URL
|
|
101
|
+
Rewrite handler that references `context.custom`:
|
|
102
|
+
|
|
103
|
+
```json title="config/routes.oas.json"
|
|
104
|
+
{
|
|
105
|
+
"paths": {
|
|
106
|
+
"/api/{resourceType}/{resourceId}": {
|
|
107
|
+
"x-zuplo-path": {
|
|
108
|
+
"pathMode": "open-api"
|
|
109
|
+
},
|
|
110
|
+
"get": {
|
|
111
|
+
"summary": "Get resource by type and ID",
|
|
112
|
+
"x-zuplo-route": {
|
|
113
|
+
"corsPolicy": "none",
|
|
114
|
+
"handler": {
|
|
115
|
+
"export": "urlRewriteHandler",
|
|
116
|
+
"module": "$import(@zuplo/runtime)",
|
|
117
|
+
"options": {
|
|
118
|
+
"rewritePattern": "https://backend.example.com/api/${context.custom.transformedResourceType}/${params.resourceId}"
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
"policies": {
|
|
122
|
+
"inbound": ["transform-params"]
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
With this configuration, a request to `/api/order/123` flows through the
|
|
132
|
+
pipeline as follows:
|
|
133
|
+
|
|
134
|
+
1. The route matches with `params.resourceType = "order"` and
|
|
135
|
+
`params.resourceId = "123"`
|
|
136
|
+
2. The `transform-params` inbound policy runs and sets
|
|
137
|
+
`context.custom.transformedResourceType = "customerorder"`
|
|
138
|
+
3. The URL Rewrite handler builds the upstream URL:
|
|
139
|
+
`https://backend.example.com/api/customerorder/123`
|
|
140
|
+
|
|
141
|
+
## Common Pitfall: Modifying `request.params` Directly
|
|
142
|
+
|
|
143
|
+
:::caution
|
|
144
|
+
|
|
145
|
+
Do not try to transform route parameters by constructing a new `ZuploRequest`
|
|
146
|
+
with modified `params` and expecting the URL Rewrite handler to pick them up.
|
|
147
|
+
|
|
148
|
+
:::
|
|
149
|
+
|
|
150
|
+
A common first attempt is to create a new
|
|
151
|
+
[`ZuploRequest`](../programmable-api/zuplo-request.mdx) with different `params`:
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
// ⚠️ This approach does NOT work as expected with URL Rewrite
|
|
155
|
+
const newRequest = new ZuploRequest(request, {
|
|
156
|
+
params: {
|
|
157
|
+
...request.params,
|
|
158
|
+
resourceType: "customerorder",
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
return newRequest;
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
In practice, the URL Rewrite handler evaluates `${params.*}` against the
|
|
165
|
+
route-level parameters rather than the request object returned by a policy. This
|
|
166
|
+
means the rewritten URL may contain `undefined` segments instead of your
|
|
167
|
+
transformed values. Use `context.custom` for reliable interpolation of
|
|
168
|
+
transformed values — the URL Rewrite handler's `rewritePattern` fully supports
|
|
169
|
+
`${context.custom.*}`, and values set in an inbound policy are available when
|
|
170
|
+
the handler runs.
|
|
171
|
+
|
|
172
|
+
## Variations
|
|
173
|
+
|
|
174
|
+
### Using a Lookup Map
|
|
175
|
+
|
|
176
|
+
For more complex mappings where the transformation is not a simple string
|
|
177
|
+
operation, use a lookup object:
|
|
178
|
+
|
|
179
|
+
```ts title="modules/transform-params-map.ts"
|
|
180
|
+
import { ZuploContext, ZuploRequest, HttpProblems } from "@zuplo/runtime";
|
|
181
|
+
|
|
182
|
+
// Map public resource types to internal names
|
|
183
|
+
const RESOURCE_TYPE_MAP: Record<string, string> = {
|
|
184
|
+
order: "customerorder",
|
|
185
|
+
invoice: "billing-invoice",
|
|
186
|
+
profile: "user-profile",
|
|
187
|
+
subscription: "recurring-plan",
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
export default async function (
|
|
191
|
+
request: ZuploRequest,
|
|
192
|
+
context: ZuploContext,
|
|
193
|
+
options: any,
|
|
194
|
+
policyName: string,
|
|
195
|
+
): Promise<ZuploRequest | Response> {
|
|
196
|
+
const resourceType = request.params.resourceType;
|
|
197
|
+
const mappedType = RESOURCE_TYPE_MAP[resourceType];
|
|
198
|
+
|
|
199
|
+
if (!mappedType) {
|
|
200
|
+
return HttpProblems.notFound(request, context, {
|
|
201
|
+
detail: `Unknown resource type: ${resourceType}`,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
context.custom.transformedResourceType = mappedType;
|
|
206
|
+
|
|
207
|
+
return request;
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Transforming Multiple Parameters
|
|
212
|
+
|
|
213
|
+
You can transform any number of route parameters and store each on
|
|
214
|
+
`context.custom`. Reference them individually in the rewrite pattern:
|
|
215
|
+
|
|
216
|
+
```ts title="modules/transform-multiple-params.ts"
|
|
217
|
+
import { ZuploContext, ZuploRequest } from "@zuplo/runtime";
|
|
218
|
+
|
|
219
|
+
export default async function (
|
|
220
|
+
request: ZuploRequest,
|
|
221
|
+
context: ZuploContext,
|
|
222
|
+
options: any,
|
|
223
|
+
policyName: string,
|
|
224
|
+
): Promise<ZuploRequest | Response> {
|
|
225
|
+
// Normalize casing
|
|
226
|
+
context.custom.version = request.params.version?.toLowerCase();
|
|
227
|
+
|
|
228
|
+
// Map resource type
|
|
229
|
+
context.custom.resource =
|
|
230
|
+
request.params.resource === "users" ? "customers" : request.params.resource;
|
|
231
|
+
|
|
232
|
+
return request;
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Then use both values in the rewrite pattern:
|
|
237
|
+
|
|
238
|
+
```json
|
|
239
|
+
{
|
|
240
|
+
"rewritePattern": "https://backend.example.com/${context.custom.version}/${context.custom.resource}/${params.id}"
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Combining with Body Transformation
|
|
245
|
+
|
|
246
|
+
If your API also needs to transform values in the request body alongside route
|
|
247
|
+
parameters, you can handle both in the same inbound policy. Create a new
|
|
248
|
+
`ZuploRequest` with a modified body while storing the route parameter
|
|
249
|
+
transformations on `context.custom`:
|
|
250
|
+
|
|
251
|
+
```ts title="modules/transform-params-and-body.ts"
|
|
252
|
+
import { ZuploContext, ZuploRequest } from "@zuplo/runtime";
|
|
253
|
+
|
|
254
|
+
export default async function (
|
|
255
|
+
request: ZuploRequest,
|
|
256
|
+
context: ZuploContext,
|
|
257
|
+
options: any,
|
|
258
|
+
policyName: string,
|
|
259
|
+
): Promise<ZuploRequest | Response> {
|
|
260
|
+
// Transform route parameter
|
|
261
|
+
context.custom.transformedResourceType = `customer${request.params.resourceType}`;
|
|
262
|
+
|
|
263
|
+
// Transform the request body if present
|
|
264
|
+
if (request.headers.get("content-type")?.includes("application/json")) {
|
|
265
|
+
const body = await request.json();
|
|
266
|
+
|
|
267
|
+
// Map fields in the body to match the backend schema
|
|
268
|
+
const transformedBody = {
|
|
269
|
+
...body,
|
|
270
|
+
type: context.custom.transformedResourceType,
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
// Return a new request with the modified body
|
|
274
|
+
return new ZuploRequest(request, {
|
|
275
|
+
body: JSON.stringify(transformedBody),
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return request;
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Best Practices
|
|
284
|
+
|
|
285
|
+
- **Use descriptive keys on `context.custom`** — names like
|
|
286
|
+
`context.custom.transformedResourceType` are easier to debug than generic keys
|
|
287
|
+
like `context.custom.value`
|
|
288
|
+
- **Log transformations** — use `context.log` to record original and transformed
|
|
289
|
+
values so you can trace issues in production
|
|
290
|
+
- **Validate before transforming** — return an appropriate error response (using
|
|
291
|
+
[`HttpProblems`](../programmable-api/http-problems.mdx)) if a parameter value
|
|
292
|
+
is unexpected, rather than forwarding bad data to your backend
|
|
293
|
+
- **Keep the policy focused** — if your transformation logic is complex,
|
|
294
|
+
consider splitting it into a separate utility module imported by the policy
|
|
295
|
+
|
|
296
|
+
## Next Steps
|
|
297
|
+
|
|
298
|
+
- [URL Rewrite Handler](../handlers/url-rewrite.mdx) — full reference for
|
|
299
|
+
rewrite patterns and available interpolation variables
|
|
300
|
+
- [Custom Code Patterns](../articles/custom-code-patterns.md) — common patterns
|
|
301
|
+
for writing inbound policies, outbound policies, and handlers
|
|
302
|
+
- [ZuploContext](../programmable-api/zuplo-context.mdx) — reference for
|
|
303
|
+
`context.custom` and other context properties
|
|
304
|
+
- [ZuploRequest](../programmable-api/zuplo-request.mdx) — reference for
|
|
305
|
+
`request.params` and constructing new requests
|
|
306
|
+
- [User-Based Backend Routing](./user-based-backend-routing.mdx) — a related
|
|
307
|
+
pattern using `context.custom` with URL Rewrite for routing by user identity
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zuplo",
|
|
3
|
-
"version": "6.71.
|
|
3
|
+
"version": "6.71.11",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The programmable API Gateway",
|
|
6
6
|
"author": "Zuplo, Inc.",
|
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
"zuplo": "zuplo.js"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@zuplo/cli": "6.71.
|
|
23
|
-
"@zuplo/core": "6.71.
|
|
24
|
-
"@zuplo/runtime": "6.71.
|
|
22
|
+
"@zuplo/cli": "6.71.11",
|
|
23
|
+
"@zuplo/core": "6.71.11",
|
|
24
|
+
"@zuplo/runtime": "6.71.11",
|
|
25
25
|
"@zuplo/test": "1.4.0"
|
|
26
26
|
}
|
|
27
27
|
}
|