@aventara/core 1.0.0-rc.8 → 1.0.0-rc.9
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/CHANGELOG.md +56 -0
- package/ERRORS.md +2 -2
- package/README.md +75 -6
- package/dist/compiler/contract-compiler-diagnostic.guidance.js +2 -2
- package/dist/config/config.resolver.d.ts +2 -0
- package/dist/config/config.resolver.js +7 -2
- package/dist/config/config.validator.js +36 -12
- package/dist/config/exact-framework-config.d.ts +11 -1
- package/dist/config/framework-config.d.ts +9 -0
- package/dist/contracts/contract-canonicalizer.js +2 -0
- package/dist/framework/contract-view.d.ts +9 -1
- package/dist/framework/framework-construction.error.js +2 -0
- package/dist/framework/framework.d.ts +22 -1
- package/dist/framework/framework.factory.js +29 -10
- package/dist/framework/operation-facade.d.ts +53 -6
- package/dist/index.d.ts +2 -1
- package/dist/internal.d.ts +1 -0
- package/dist/internal.js +1 -0
- package/dist/policy/access-provider.interface.d.ts +36 -0
- package/dist/policy/access-provider.interface.js +1 -0
- package/dist/policy/access-provider.registry.d.ts +9 -0
- package/dist/policy/access-provider.registry.js +10 -0
- package/dist/policy/policy-startup.validator.js +10 -10
- package/dist/prebuilt/core-version.generated.d.ts +1 -1
- package/dist/prebuilt/core-version.generated.js +1 -1
- package/dist/prebuilt/prebuilt-inputs.fingerprint.js +16 -1
- package/dist/protocol/http-response.encoder.js +2 -2
- package/dist/runtime/caller-data.copier.js +3 -4
- package/dist/runtime/core-class.brand.d.ts +10 -0
- package/dist/runtime/core-class.brand.js +21 -0
- package/dist/runtime/decimal.d.ts +14 -0
- package/dist/runtime/decimal.js +21 -0
- package/dist/runtime/diagnostics-reporter.js +13 -1
- package/dist/runtime/framework-error.js +9 -0
- package/dist/runtime/readonly-value.d.ts +1 -1
- package/dist/runtime/readonly-value.js +2 -2
- package/dist/runtime/scalar-runtime.js +4 -5
- package/dist/transactions/operation-arguments-encoder.js +8 -3
- package/dist/transactions/sql-statement.validator.js +2 -2
- package/dist/transactions/transaction-integrity-error.js +2 -0
- package/dist/transactions/transaction-plan-assembler.d.ts +9 -2
- package/dist/transactions/transaction-plan-assembler.js +17 -2
- package/dist/transactions/transaction-slot-path-error.js +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,61 @@
|
|
|
1
1
|
# @aventara/core
|
|
2
2
|
|
|
3
|
+
## 1.0.0-rc.9
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 205a379: `createFramework` takes `access`, an access provider: the policy engine and the request state from one object.
|
|
8
|
+
`AvAccessProvider` is the new main-entry type it is written against, with two members:
|
|
9
|
+
|
|
10
|
+
- `policy`: the engine every client and agent request is checked against, as `policy.engine` takes it.
|
|
11
|
+
- `state({ headers, request })`: seeds each request's `ctx.$state`. Its `me` is the principal the engine is asked
|
|
12
|
+
about, `null` for an anonymous caller.
|
|
13
|
+
|
|
14
|
+
Aventara Access provides one, and a hand-written provider works the same way (a till's PIN sign-in, say: see the
|
|
15
|
+
README). The host calls `state` once per request; server code that calls the Framework directly passes the state
|
|
16
|
+
itself. A governed field of the provider's engine is left out of the default projection's type, as with `policy`.
|
|
17
|
+
`access` together with `policy` is refused at startup (`CONFIG_INVALID_VALUE` at `policy`) and is a type error at
|
|
18
|
+
`policy`. A provider whose `policy` is no engine or whose `state` is no function is refused at startup, member by
|
|
19
|
+
member, and the engine's `governed`, `paths` and `afterChecks` are checked against the Core Contract under
|
|
20
|
+
`access.policy`. A prebuilt Contracts file counts the provider by its engine's governed set alone.
|
|
21
|
+
|
|
22
|
+
- a0bb05e: A Framework's `diagnostics` sink receives every report when the process holds more than one copy of `@aventara/core`
|
|
23
|
+
(Next.js giving each layer its own copy, a duplicate install, a monorepo). A protocol failure such as a result with no
|
|
24
|
+
wire form (`500 A3003`), or an MCP endpoint failure, raised through another copy than the one that built the Framework
|
|
25
|
+
now reaches the sink instead of being silently dropped. Copies of one major version share the sinks; nothing in the
|
|
26
|
+
public API or its types changed.
|
|
27
|
+
- 86ab319: Values and errors from `@aventara/core` are recognized across duplicate copies of the package in one process. A
|
|
28
|
+
bundler that gives each layer its own copy (Next.js instrumentation, route handlers and pages), a duplicate install, a
|
|
29
|
+
monorepo, Vite SSR, per-function bundles and test runners' module isolation all load core more than once.
|
|
30
|
+
|
|
31
|
+
- A decimal made by one copy is an `AvDecimal` to every other: `value instanceof AvDecimal` is `true`, the protocol
|
|
32
|
+
encodes it (a `find.many` selecting a decimal no longer answers `500 A3003` "Result serialization failed"), and a
|
|
33
|
+
Framework accepts it in arguments, `$sql` values and Adapter results.
|
|
34
|
+
- `AvError` and its subclasses (`AvValidationError`, `AvNotFoundError`, `AvConflictError`, `AvAuthError`,
|
|
35
|
+
`AvInternalError`, `AvProtocolError`, `AvContractMismatchError`), `AvConstructionError`,
|
|
36
|
+
`AvTransactionIntegrityError` and `AvTransactionSlotPathError` pass `instanceof` across copies, with the same
|
|
37
|
+
subclass rules: every one is an `AvError`, and each is only its own subclass. An `AvError` a pipeline throws with
|
|
38
|
+
another copy's class is answered as the outcome it names. A subclass you declare yourself is checked as before.
|
|
39
|
+
- Copies recognize each other within one major version only. Nothing in the public API or its types changed.
|
|
40
|
+
|
|
41
|
+
- fdf52e6: `framework.serverClient` and `framework.serverClientTx`: the client layer for server code, called with application
|
|
42
|
+
values, such as a Next.js Server Component's.
|
|
43
|
+
|
|
44
|
+
- `framework.serverClient.<Resource>.<family>.<variant>(args, options?)` is typed from the Client Contract's view, as
|
|
45
|
+
`framework.client` is: what the client layer hides is absent. Its arguments are the application forms (`Date`,
|
|
46
|
+
`bigint`, `AvDecimal`, `Uint8Array`), the ones `framework.application` takes, and their wire strings are type errors.
|
|
47
|
+
Its results are `framework.client`'s. At runtime a value already in its wire form is accepted too, as
|
|
48
|
+
`framework.client` reads it.
|
|
49
|
+
- Each call copies its arguments, encodes the copy into the wire forms against the Client Contract, and runs the request
|
|
50
|
+
`framework.client` would run for those wire forms: the same client pipelines, validation, exposure and policy, the
|
|
51
|
+
same answers and the same `AvError` classes. `framework.client` itself is unchanged and keeps the wire forms.
|
|
52
|
+
- `framework.serverClientTx` builds client-origin transaction handles from application values. One
|
|
53
|
+
`framework.transaction([...])` may hold them beside `clientTx` handles, never beside `appTx` ones.
|
|
54
|
+
- New main-entry type names: `AvServerClientFacade`, `AvServerClientResourceFacade`, `AvServerClientFamilyFacade`,
|
|
55
|
+
`AvServerClientProjectingCall`, `AvServerClientPlainCall`, `AvServerClientTxFacade`,
|
|
56
|
+
`AvServerClientTxResourceFacade`, `AvServerClientTxFamilyFacade`, `AvServerClientTxProjectingCall`,
|
|
57
|
+
`AvServerClientTxPlainCall`.
|
|
58
|
+
|
|
3
59
|
## 1.0.0-rc.8
|
|
4
60
|
|
|
5
61
|
1.0.0-rc.5, rc.6 and rc.7 were tagged but never published; 1.0.0-rc.8 contains everything listed under them and the changes below.
|
package/ERRORS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Aventara error codes
|
|
2
2
|
|
|
3
|
-
Every code `@aventara/core` 1.0.0-rc.
|
|
3
|
+
Every code `@aventara/core` 1.0.0-rc.9 answers, reports in a validation issue, or refuses to start with. Generated from `AvErrorCatalog` (exported by `@aventara/core`); do not edit.
|
|
4
4
|
|
|
5
5
|
Branch on the code, never on the message. In code, `AvErrorCatalog[code]` gives the same entry: `{ code, kind, message, cause, fix, docs }`.
|
|
6
6
|
|
|
@@ -134,7 +134,7 @@ Each entry of `cause.issues[]` under `A2001`, `A2002`, `A2004`, `A2007` or `A200
|
|
|
134
134
|
| [`COMPILER_INVALID_LIFECYCLE`](https://aventara-framework.vercel.app/docs/error-codes#compiler_invalid_lifecycle) | Invalid field lifecycle. | An added field declares a lifecycle other than VIRTUAL, a VIRTUAL field claims a database-generated lifecycle or a provider-side capability, or a definition redefines a discovered field's lifecycle. | Give an added field lifecycle: ["VIRTUAL"] and only the capabilities it can serve; computed lifecycles come from compute stages. |
|
|
135
135
|
| [`COMPILER_MISSING_READ_COMPUTE`](https://aventara-framework.vercel.app/docs/error-codes#compiler_missing_read_compute) | A selectable virtual field has no compute.read. | A VIRTUAL field that can be selected has no pipelines.compute.read stage. | Add a compute.read stage, or make the field write-only with capabilities.select: false. |
|
|
136
136
|
| [`COMPILER_INVALID_COMPUTE_DEPENDENCY`](https://aventara-framework.vercel.app/docs/error-codes#compiler_invalid_compute_dependency) | Invalid compute.read dependency. | A dependsOn entry that is not a stored scalar field of the same Resource. | List only stored scalar fields of the Resource in dependsOn. |
|
|
137
|
-
| [`COMPILER_RESERVED_RESOURCE_NAME`](https://aventara-framework.vercel.app/docs/error-codes#compiler_reserved_resource_name) | Reserved Resource name. | A model name starts with $, which is reserved for the framework's own names (ctx.$sql, $ref). | Rename the model; the table it maps to can keep its name. |
|
|
137
|
+
| [`COMPILER_RESERVED_RESOURCE_NAME`](https://aventara-framework.vercel.app/docs/error-codes#compiler_reserved_resource_name) | Reserved Resource name. | A model name starts with $, which is reserved for the framework's own names (ctx.$sql, $ref), or, on a host that adds members beside the Resources, is one of those members' names (on Next.js: tx and transaction on avClient, appTx and transaction on avApp). | Rename the model; the table it maps to can keep its name. A host member's name is also left free when the layer it belongs to hides the Resource. |
|
|
138
138
|
| [`COMPILER_MISSING_IDENTIFIER`](https://aventara-framework.vercel.app/docs/error-codes#compiler_missing_identifier) | A unique operation has no identifier. | A layer exposes a unique variant (find.unique, update.unique, delete.unique, upsert.unique) and no complete identifier of the Resource: a Pick that did not pick one, or an Omit that hid every one. | Expose an identifier field (id, or every field of one unique), or exempt the unique variants. |
|
|
139
139
|
| [`COMPILER_HIDDEN_REQUIRED_FIELD`](https://aventara-framework.vercel.app/docs/error-codes#compiler_hidden_required_field) | A layer that creates a Resource hides a required field. | A layer still creates the Resource (a create or upsert variant, or a nested create) while a required field, or a required relation's reference field, is hidden or has its create capability exempted, and no compute.create fills it. | Expose the field, give it a compute.create stage, or exempt the layer's create operations. |
|
|
140
140
|
| [`COMPILER_INVALID_VALIDATOR`](https://aventara-framework.vercel.app/docs/error-codes#compiler_invalid_validator) | A field validator cannot apply. | A validator that is not built with AvZ, does not fit the field's type (AvZ.maxLength on an int, AvZ.maxItems on a field that is not a list), or sits on a relation or Json field. | Use an AvZ check that fits the field's type; a Json field's checks go in its json shape. |
|
package/README.md
CHANGED
|
@@ -118,6 +118,27 @@ try {
|
|
|
118
118
|
generated client throws the same classes by the same table. `framework.execute` and `framework.executeTransaction`, the
|
|
119
119
|
entries a transport calls, still answer the envelope.
|
|
120
120
|
|
|
121
|
+
## The client layer, called with your own values
|
|
122
|
+
|
|
123
|
+
`framework.client` reads its arguments in the wire forms a decoded request body carries: a `datetime` is its ISO string,
|
|
124
|
+
a `bigint` its base-10 string. That suits a test that should behave like a remote caller. Server code that acts for a
|
|
125
|
+
remote caller, such as a Server Component or a Server Action, holds `Date`s and `bigint`s instead. It calls
|
|
126
|
+
`framework.serverClient`:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
const recent = await framework.serverClient.Post.find.many({
|
|
130
|
+
where: { createdAt: { gt: new Date(Date.now() - 86_400_000) } },
|
|
131
|
+
select: ["id", "title"],
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`serverClient` has the client layer's types and rules: what the client layer hides is absent, and the client
|
|
136
|
+
pipelines, validation, exposure and policy run. Its arguments are the application forms (`Date`, `bigint`,
|
|
137
|
+
`AvDecimal`, `Uint8Array`), the same ones `framework.application` takes, and its results are `framework.client`'s.
|
|
138
|
+
Each call copies its arguments, encodes them into the wire forms and runs the request `framework.client` would run.
|
|
139
|
+
`framework.serverClientTx` builds client-layer transaction handles the same way; one `framework.transaction([...])`
|
|
140
|
+
may hold them beside `clientTx` handles, never beside `appTx` ones.
|
|
141
|
+
|
|
121
142
|
## Every code, explained
|
|
122
143
|
|
|
123
144
|
`AvErrorCatalog` holds every code this package answers, reports in a validation issue, or refuses to start with: its
|
|
@@ -199,15 +220,15 @@ findMany(): import("@aventara/core").AvApplicationProjectingCall<
|
|
|
199
220
|
"Post", { readonly family: "find"; readonly variant: "many" }>;
|
|
200
221
|
```
|
|
201
222
|
|
|
202
|
-
The names, one set per surface (`application`, `client`, `appTx`, `clientTx`):
|
|
223
|
+
The names, one set per surface (`application`, `client`, `serverClient`, `appTx`, `clientTx`, `serverClientTx`):
|
|
203
224
|
|
|
204
225
|
| Node | `framework.application` | the others |
|
|
205
226
|
| --- | --- | --- |
|
|
206
|
-
| the whole surface | `AvApplicationFacade` | `AvClientFacade`, `AvAppTxFacade`, `AvClientTxFacade` |
|
|
227
|
+
| the whole surface | `AvApplicationFacade` | `AvClientFacade`, `AvServerClientFacade`, `AvAppTxFacade`, `AvClientTxFacade`, `AvServerClientTxFacade` |
|
|
207
228
|
| one Resource | `AvApplicationResourceFacade` | `AvClientResourceFacade`, … |
|
|
208
229
|
| one family | `AvApplicationFamilyFacade` | `AvClientFamilyFacade`, … |
|
|
209
|
-
| a variant that takes a `select` or `include` | `AvApplicationProjectingCall` | `AvClientProjectingCall`, `AvAppTxProjectingCall`, `AvClientTxProjectingCall` |
|
|
210
|
-
| a variant that does not (the `count` variants: `find.count`, `update.count`, …) | `AvApplicationPlainCall` | `AvClientPlainCall`, `AvAppTxPlainCall`, `AvClientTxPlainCall` |
|
|
230
|
+
| a variant that takes a `select` or `include` | `AvApplicationProjectingCall` | `AvClientProjectingCall`, `AvServerClientProjectingCall`, `AvAppTxProjectingCall`, `AvClientTxProjectingCall`, `AvServerClientTxProjectingCall` |
|
|
231
|
+
| a variant that does not (the `count` variants: `find.count`, `update.count`, …) | `AvApplicationPlainCall` | `AvClientPlainCall`, `AvServerClientPlainCall`, `AvAppTxPlainCall`, `AvClientTxPlainCall`, `AvServerClientTxPlainCall` |
|
|
211
232
|
|
|
212
233
|
Each takes the view, `AvContractView`, as its first argument. The view's arguments are your model by its name, the root's
|
|
213
234
|
and the overlay's exposure as written, and the `fields` entries your configuration's `resources` blocks hold. A
|
|
@@ -230,8 +251,8 @@ never with the model the inline stages were typed from.
|
|
|
230
251
|
|
|
231
252
|
## Server-side calls copy their input
|
|
232
253
|
|
|
233
|
-
`framework.application`, `framework.client`, `framework.
|
|
234
|
-
`clientTx` builders read what you pass them once, when you call them, and copy it into plain data the framework owns.
|
|
254
|
+
`framework.application`, `framework.client`, `framework.serverClient`, `framework.execute`,
|
|
255
|
+
`framework.executeTransaction`, and the `appTx` / `clientTx` / `serverClientTx` builders read what you pass them once, when you call them, and copy it into plain data the framework owns.
|
|
235
256
|
Validation, your guards and pipes, computed values and the adapter all see only the copy, and your stages see it
|
|
236
257
|
frozen: only a pipe changes the arguments, by returning new ones, so what the validators checked is what the adapter
|
|
237
258
|
writes. In practice:
|
|
@@ -374,6 +395,54 @@ caller may change or delete, as `update.many` does. After the call, every row th
|
|
|
374
395
|
(the after check, the move-out re-read), and every row they created is read back as stored and asked `can`; one refusal
|
|
375
396
|
rolls the whole write back. Where `transactionsStyle` is `"none"`, a nested write under a policy is refused.
|
|
376
397
|
|
|
398
|
+
## An access provider
|
|
399
|
+
|
|
400
|
+
`access` takes the policy engine and the request state from one object, an `AvAccessProvider`. Aventara Access
|
|
401
|
+
provides one, and you can write your own. It has two members:
|
|
402
|
+
|
|
403
|
+
- `policy`: the engine, as `policy.engine` takes it.
|
|
404
|
+
- `state({ headers, request })`: seeds each request's `ctx.$state`. Its `me` is the principal the engine is asked
|
|
405
|
+
about (`null` for an anonymous caller).
|
|
406
|
+
|
|
407
|
+
The host calls `state` once per request: `@aventara/nest` with the platform request's headers and the request itself,
|
|
408
|
+
`@aventara/next` with the request's headers (and the route's `Request` on a route call). With `access`, leave out
|
|
409
|
+
`policy` and the host's own `state` option; either one beside it is refused at startup, and as a type error where the
|
|
410
|
+
configuration's type shows both. Server code that calls the Framework directly passes the state itself,
|
|
411
|
+
`{ state: await access.state({ headers }) }`.
|
|
412
|
+
|
|
413
|
+
A point-of-sale till with its own PIN sign-in, written by hand:
|
|
414
|
+
|
|
415
|
+
```ts
|
|
416
|
+
import type { AvAccessProvider } from "@aventara/core";
|
|
417
|
+
|
|
418
|
+
type Cashier = { id: string; role: "cashier" | "manager" };
|
|
419
|
+
|
|
420
|
+
declare module "@aventara/core" {
|
|
421
|
+
interface AvState {
|
|
422
|
+
me?: Cashier | null;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
export const tillAccess = {
|
|
427
|
+
// The till sends the PIN the cashier typed as `x-pos-pin`.
|
|
428
|
+
state: async ({ headers }) => ({ me: await cashierByPin(headers.get("x-pos-pin")) }),
|
|
429
|
+
policy: {
|
|
430
|
+
can: ({ principal, resource, action }) => {
|
|
431
|
+
const me = principal as Cashier | null;
|
|
432
|
+
if (me === null) return false; // no PIN, no till
|
|
433
|
+
if (resource === "Refund" && action !== "read") return me.role === "manager";
|
|
434
|
+
return true;
|
|
435
|
+
},
|
|
436
|
+
scopeOf: () => true,
|
|
437
|
+
fieldsOf: () => ({ always: "all", perRow: {} }),
|
|
438
|
+
},
|
|
439
|
+
} satisfies AvAccessProvider;
|
|
440
|
+
|
|
441
|
+
const framework = await createFramework({ api: "/api", adapter, access: tillAccess });
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
Every stage reads the cashier as `ctx.$state.me`, typed by the `AvState` declaration above.
|
|
445
|
+
|
|
377
446
|
## License
|
|
378
447
|
|
|
379
448
|
PolyForm Shield 1.0.0 with an additional permission. It is free to use, including in commercial applications, but you
|
|
@@ -66,8 +66,8 @@ export const CONTRACT_COMPILER_DIAGNOSTIC_GUIDANCE = {
|
|
|
66
66
|
},
|
|
67
67
|
COMPILER_RESERVED_RESOURCE_NAME: {
|
|
68
68
|
message: "Reserved Resource name.",
|
|
69
|
-
cause: "A model name starts with $, which is reserved for the framework's own names (ctx.$sql, $ref).",
|
|
70
|
-
fix: "Rename the model; the table it maps to can keep its name.",
|
|
69
|
+
cause: "A model name starts with $, which is reserved for the framework's own names (ctx.$sql, $ref), or, on a host that adds members beside the Resources, is one of those members' names (on Next.js: tx and transaction on avClient, appTx and transaction on avApp).",
|
|
70
|
+
fix: "Rename the model; the table it maps to can keep its name. A host member's name is also left free when the layer it belongs to hides the Resource.",
|
|
71
71
|
},
|
|
72
72
|
COMPILER_MISSING_IDENTIFIER: {
|
|
73
73
|
message: "A unique operation has no identifier.",
|
|
@@ -11,6 +11,8 @@ import type { AvConfigFieldDefinition } from "./framework-field-definition.js";
|
|
|
11
11
|
import { type ResolvedPipelineTree } from "./pipeline-tree.resolver.js";
|
|
12
12
|
/** The root's `policy`, its engine's passive lists read once. */
|
|
13
13
|
export type ResolvedPolicyConfig = {
|
|
14
|
+
/** Where the engine was configured, for the startup diagnostics' paths. */
|
|
15
|
+
readonly at: "policy.engine" | "access.policy";
|
|
14
16
|
readonly engine: AvPolicyEngine;
|
|
15
17
|
readonly principal: NonNullable<AvConfig["policy"]>["principal"];
|
|
16
18
|
/** The governed fields and relations, by Resource. */
|
|
@@ -158,11 +158,16 @@ function frozenPathLists(value) {
|
|
|
158
158
|
Object.freeze(paths.map((path) => Object.freeze([...path]))),
|
|
159
159
|
])));
|
|
160
160
|
}
|
|
161
|
-
|
|
161
|
+
const accessPrincipal = ({ $state, }) => $state.me ?? null;
|
|
162
|
+
function resolvePolicy(configured, access) {
|
|
163
|
+
const policy = access === undefined
|
|
164
|
+
? configured
|
|
165
|
+
: { engine: access.policy, principal: accessPrincipal };
|
|
162
166
|
if (policy === undefined) {
|
|
163
167
|
return undefined;
|
|
164
168
|
}
|
|
165
169
|
return Object.freeze({
|
|
170
|
+
at: access === undefined ? "policy.engine" : "access.policy",
|
|
166
171
|
engine: policy.engine,
|
|
167
172
|
principal: policy.principal,
|
|
168
173
|
governed: frozenLists(policy.engine.governed),
|
|
@@ -188,7 +193,7 @@ export function resolveFrameworkConfig(config) {
|
|
|
188
193
|
adapter: config.adapter,
|
|
189
194
|
diagnostics: config.diagnostics,
|
|
190
195
|
misuse: config.misuse,
|
|
191
|
-
policy: resolvePolicy(config.policy),
|
|
196
|
+
policy: resolvePolicy(config.policy, config.access),
|
|
192
197
|
definitions: definitionsOf(root.resources),
|
|
193
198
|
trees: {
|
|
194
199
|
root: root.resources,
|
|
@@ -7,6 +7,7 @@ const ROOT_ONLY_PROPERTIES = new Set([
|
|
|
7
7
|
"adapter",
|
|
8
8
|
"prebuilt",
|
|
9
9
|
"policy",
|
|
10
|
+
"access",
|
|
10
11
|
"diagnostics",
|
|
11
12
|
"misuse",
|
|
12
13
|
"application",
|
|
@@ -628,6 +629,12 @@ export function validateFrameworkConfig(value) {
|
|
|
628
629
|
if (Object.hasOwn(value, "policy") && value.policy !== undefined) {
|
|
629
630
|
validatePolicy(value.policy, diagnostics);
|
|
630
631
|
}
|
|
632
|
+
if (Object.hasOwn(value, "access") && value.access !== undefined) {
|
|
633
|
+
validateAccess(value.access, diagnostics);
|
|
634
|
+
if (Object.hasOwn(value, "policy") && value.policy !== undefined) {
|
|
635
|
+
diagnostics.push(diagnostic("CONFIG_INVALID_VALUE", "policy", "policy cannot be configured with access: access.policy is the policy engine, and ctx.$state.me the principal; remove policy"));
|
|
636
|
+
}
|
|
637
|
+
}
|
|
631
638
|
if (Object.hasOwn(value, "diagnostics") &&
|
|
632
639
|
value.diagnostics !== undefined &&
|
|
633
640
|
typeof value.diagnostics !== "function") {
|
|
@@ -670,26 +677,43 @@ function validatePolicy(value, diagnostics) {
|
|
|
670
677
|
diagnostics.push(diagnostic("CONFIG_REQUIRED", "policy.engine", "policy.engine is required"));
|
|
671
678
|
return;
|
|
672
679
|
}
|
|
673
|
-
|
|
680
|
+
validatePolicyEngine(value.engine, "policy.engine", diagnostics);
|
|
681
|
+
}
|
|
682
|
+
function validateAccess(value, diagnostics) {
|
|
683
|
+
if (value === null || typeof value !== "object") {
|
|
684
|
+
diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", "access", "access must be an access provider, { policy, state }"));
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
const provider = value;
|
|
688
|
+
if (typeof provider.state !== "function") {
|
|
689
|
+
diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", "access.state", "access.state must be a function"));
|
|
690
|
+
}
|
|
691
|
+
if (provider.policy === undefined) {
|
|
692
|
+
diagnostics.push(diagnostic("CONFIG_REQUIRED", "access.policy", "access.policy is required"));
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
validatePolicyEngine(provider.policy, "access.policy", diagnostics);
|
|
696
|
+
}
|
|
697
|
+
function validatePolicyEngine(engine, at, diagnostics) {
|
|
674
698
|
if (engine === null || typeof engine !== "object") {
|
|
675
|
-
diagnostics.push(diagnostic("CONFIG_INVALID_TYPE",
|
|
699
|
+
diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", at, `${at} must be an object`));
|
|
676
700
|
return;
|
|
677
701
|
}
|
|
678
702
|
const members = engine;
|
|
679
703
|
for (const question of ["can", "scopeOf", "fieldsOf"]) {
|
|
680
704
|
if (typeof members[question] !== "function") {
|
|
681
|
-
diagnostics.push(diagnostic("CONFIG_INVALID_TYPE",
|
|
705
|
+
diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", `${at}.${question}`, `${at}.${question} must be a function`));
|
|
682
706
|
}
|
|
683
707
|
}
|
|
684
|
-
validatePolicyLists(members.governed, "governed", { all: "string lists", one: "a list of strings" }, isStringList, diagnostics);
|
|
685
|
-
validatePolicyLists(members.paths, "paths", {
|
|
708
|
+
validatePolicyLists(members.governed, at, "governed", { all: "string lists", one: "a list of strings" }, isStringList, diagnostics);
|
|
709
|
+
validatePolicyLists(members.paths, at, "paths", {
|
|
686
710
|
all: "path lists",
|
|
687
711
|
one: "a list of paths, each a list of strings",
|
|
688
712
|
}, (paths) => Array.isArray(paths) && paths.every(isStringList), diagnostics);
|
|
689
|
-
validatePolicyLists(members.afterChecks, "afterChecks", { all: "action lists", one: "a list of actions" }, isStringList, diagnostics);
|
|
690
|
-
validateAfterCheckActions(members.afterChecks, diagnostics);
|
|
713
|
+
validatePolicyLists(members.afterChecks, at, "afterChecks", { all: "action lists", one: "a list of actions" }, isStringList, diagnostics);
|
|
714
|
+
validateAfterCheckActions(members.afterChecks, at, diagnostics);
|
|
691
715
|
}
|
|
692
|
-
function validateAfterCheckActions(lists, diagnostics) {
|
|
716
|
+
function validateAfterCheckActions(lists, at, diagnostics) {
|
|
693
717
|
if (!isPlainRecord(lists)) {
|
|
694
718
|
return;
|
|
695
719
|
}
|
|
@@ -699,7 +723,7 @@ function validateAfterCheckActions(lists, diagnostics) {
|
|
|
699
723
|
}
|
|
700
724
|
for (const action of actions) {
|
|
701
725
|
if (action !== "update") {
|
|
702
|
-
diagnostics.push(diagnostic("CONFIG_INVALID_VALUE",
|
|
726
|
+
diagnostics.push(diagnostic("CONFIG_INVALID_VALUE", `${at}.afterChecks.${resource}`, `${at}.afterChecks.${resource}: "${action}" is no action an after check runs on; only "update" is`));
|
|
703
727
|
}
|
|
704
728
|
}
|
|
705
729
|
}
|
|
@@ -707,17 +731,17 @@ function validateAfterCheckActions(lists, diagnostics) {
|
|
|
707
731
|
function isStringList(value) {
|
|
708
732
|
return (Array.isArray(value) && value.every((name) => typeof name === "string"));
|
|
709
733
|
}
|
|
710
|
-
function validatePolicyLists(lists, list, entry, isEntry, diagnostics) {
|
|
734
|
+
function validatePolicyLists(lists, at, list, entry, isEntry, diagnostics) {
|
|
711
735
|
if (lists === undefined) {
|
|
712
736
|
return;
|
|
713
737
|
}
|
|
714
738
|
if (!isPlainRecord(lists)) {
|
|
715
|
-
diagnostics.push(diagnostic("CONFIG_INVALID_TYPE",
|
|
739
|
+
diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", `${at}.${list}`, `${at}.${list} must be an object of ${entry.all} when provided`));
|
|
716
740
|
return;
|
|
717
741
|
}
|
|
718
742
|
for (const [key, value] of Object.entries(lists)) {
|
|
719
743
|
if (!isEntry(value)) {
|
|
720
|
-
diagnostics.push(diagnostic("CONFIG_INVALID_TYPE",
|
|
744
|
+
diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", `${at}.${list}.${key}`, `${at}.${list}.${key} must be ${entry.one}`));
|
|
721
745
|
}
|
|
722
746
|
}
|
|
723
747
|
}
|
|
@@ -243,6 +243,7 @@ type RootSchema<M extends AvAdapterModel, Cfg> = LayerKeysSchema & {
|
|
|
243
243
|
readonly engine: Leaf;
|
|
244
244
|
readonly principal: Leaf;
|
|
245
245
|
};
|
|
246
|
+
readonly access: Leaf;
|
|
246
247
|
readonly diagnostics: Leaf;
|
|
247
248
|
readonly misuse: Leaf;
|
|
248
249
|
readonly resources: RootResourcesSchema<M>;
|
|
@@ -264,7 +265,16 @@ type Exact<S, V> = [S] extends [Leaf] ? unknown : V extends readonly unknown[] ?
|
|
|
264
265
|
* rc.0 key, a `definition` in an overlay, a stage a relation field does not take,
|
|
265
266
|
* or a name the model and the root do not have.
|
|
266
267
|
*/
|
|
267
|
-
export type AvConfigExact<M extends AvAdapterModel, Cfg> = Exact<RootSchema<M, Cfg>, Cfg> & ExposedWithExemptions<Cfg, "application"> & ExposedWithExemptions<Cfg, "client"> & ExposedWithExemptions<Cfg, "agent"> & ForceNeedsTransactions<M, Cfg>;
|
|
268
|
+
export type AvConfigExact<M extends AvAdapterModel, Cfg> = Exact<RootSchema<M, Cfg>, Cfg> & ExposedWithExemptions<Cfg, "application"> & ExposedWithExemptions<Cfg, "client"> & ExposedWithExemptions<Cfg, "agent"> & ForceNeedsTransactions<M, Cfg> & AccessWithoutPolicy<Cfg>;
|
|
269
|
+
/** `policy` refused when the configuration also states `access`. */
|
|
270
|
+
type AccessWithoutPolicy<Cfg> = Cfg extends {
|
|
271
|
+
readonly access: object;
|
|
272
|
+
readonly policy: object;
|
|
273
|
+
} ? {
|
|
274
|
+
readonly policy: readonly [
|
|
275
|
+
"Error: `access` brings the policy engine; remove `policy`"
|
|
276
|
+
];
|
|
277
|
+
} : unknown;
|
|
268
278
|
/** A layer's own literal `transactionsStyle`, `undefined` when it states none. */
|
|
269
279
|
type StyleOf<Layer> = Layer extends {
|
|
270
280
|
readonly transactionsStyle: infer Style;
|
|
@@ -2,6 +2,7 @@ import type { AvAdapter } from "../adapters/adapter.interface.js";
|
|
|
2
2
|
import type { AvAdapterModel } from "../adapters/adapter-model.js";
|
|
3
3
|
import type { AvContractLimits } from "../contracts/contract-limits.js";
|
|
4
4
|
import type { AvTransactionCapability } from "../contracts/transaction-capability.js";
|
|
5
|
+
import type { AvAccessProvider } from "../policy/access-provider.interface.js";
|
|
5
6
|
import type { AvPolicyEngine } from "../policy/policy-engine.js";
|
|
6
7
|
import type { AvPrebuiltContracts } from "../prebuilt/prebuilt-contracts.js";
|
|
7
8
|
import type { AvDiagnosticsSink } from "../runtime/diagnostics-reporter.js";
|
|
@@ -100,6 +101,14 @@ export type AvConfig<M extends AvAdapterModel = AvAdapterModel, Cfg = AvConfigAn
|
|
|
100
101
|
readonly $origin: AvRequestOrigin;
|
|
101
102
|
}) => unknown;
|
|
102
103
|
};
|
|
104
|
+
/**
|
|
105
|
+
* The access provider (Aventara Access, or one written by hand): its
|
|
106
|
+
* `policy` engine checks every client and agent request, with
|
|
107
|
+
* `ctx.$state.me` as the principal, and the host seeds each request's
|
|
108
|
+
* `ctx.$state` with its `state`. Not together with `policy`, nor with a
|
|
109
|
+
* host's own `state` option.
|
|
110
|
+
*/
|
|
111
|
+
readonly access?: AvAccessProvider;
|
|
103
112
|
readonly diagnostics?: AvDiagnosticsSink;
|
|
104
113
|
/** Told about every caller value refused as unreadable or not plain data. */
|
|
105
114
|
readonly misuse?: AvMisuseSink;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { brandCoreClass } from "../runtime/core-class.brand.js";
|
|
1
2
|
import { FILTER_MODES, PROJECTION_SCOPES, WRITE_SCOPES } from "./capability.js";
|
|
2
3
|
import { LIFECYCLE_TRAITS } from "./lifecycle-trait.js";
|
|
3
4
|
import { projectionScopeOf, writeScopeOf } from "./resource-operations.js";
|
|
@@ -5,6 +6,7 @@ const PAGINATION_ORDER = ["limit", "offset", "cursor"];
|
|
|
5
6
|
const REDUCER_ORDER = ["$count"];
|
|
6
7
|
export class AvCanonicalJsonError extends TypeError {
|
|
7
8
|
}
|
|
9
|
+
brandCoreClass(AvCanonicalJsonError, "AvCanonicalJsonError");
|
|
8
10
|
function compareCodeUnits(left, right) {
|
|
9
11
|
if (left === right) {
|
|
10
12
|
return 0;
|
|
@@ -104,8 +104,16 @@ type PolicyEngineOf<Cfg> = (NonNullable<(Cfg & {
|
|
|
104
104
|
})["policy"]> & {
|
|
105
105
|
readonly engine?: unknown;
|
|
106
106
|
})["engine"];
|
|
107
|
+
/** The configuration's `access.policy`, read by indexed access. */
|
|
108
|
+
type AccessEngineOf<Cfg> = ((Cfg & {
|
|
109
|
+
readonly access?: {
|
|
110
|
+
readonly policy?: unknown;
|
|
111
|
+
};
|
|
112
|
+
})["access"] & {
|
|
113
|
+
readonly policy?: unknown;
|
|
114
|
+
})["policy"];
|
|
107
115
|
/** The configuration's policy engine's `governed` set, when its type states one. */
|
|
108
|
-
type GovernedBlock<Cfg> = LiteralRecord<(NonNullable<PolicyEngineOf<Cfg>> & {
|
|
116
|
+
type GovernedBlock<Cfg> = LiteralRecord<(NonNullable<PolicyEngineOf<Cfg>> & AccessEngineOf<Cfg> & {
|
|
109
117
|
readonly governed?: unknown;
|
|
110
118
|
})["governed"]>;
|
|
111
119
|
/** The names `Governed` lists for Resource `R`, or `never`. */
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { brandCoreClass } from "../runtime/core-class.brand.js";
|
|
1
2
|
import { errorDocsUrl } from "../runtime/error.catalog.js";
|
|
2
3
|
const FRAMEWORK_CONSTRUCTION_MESSAGES = {
|
|
3
4
|
configuration: "Aventara framework configuration validation failed",
|
|
@@ -27,3 +28,4 @@ export class AvConstructionError extends Error {
|
|
|
27
28
|
}));
|
|
28
29
|
}
|
|
29
30
|
}
|
|
31
|
+
brandCoreClass(AvConstructionError, "AvConstructionError");
|
|
@@ -7,7 +7,7 @@ import type { AvOperationResponse } from "../runtime/operation-response.js";
|
|
|
7
7
|
import type { AvTransactionRunner } from "../transactions/deferred-operation.js";
|
|
8
8
|
import type { AvTransactionRequest } from "../transactions/transaction-request.js";
|
|
9
9
|
import type { AvTransactionResponse } from "../transactions/transaction-response.js";
|
|
10
|
-
import type { ApplicationOperations, ApplicationTransactionBuilders, ClientOperations, ClientTransactionBuilders } from "./operation-facade.js";
|
|
10
|
+
import type { ApplicationOperations, ApplicationTransactionBuilders, ClientOperations, ClientTransactionBuilders, ServerClientOperations, ServerClientTransactionBuilders } from "./operation-facade.js";
|
|
11
11
|
/**
|
|
12
12
|
* One Aventara instance, as returned by {@link createFramework}: its compiled
|
|
13
13
|
* Contracts, typed in-process operations, transaction builders and the
|
|
@@ -50,6 +50,19 @@ export type AvFramework<M extends AvAdapterModel = AvAdapterModel, Cfg = AvConfi
|
|
|
50
50
|
* failure thrown.
|
|
51
51
|
*/
|
|
52
52
|
readonly client: ClientOperations<M, Cfg>;
|
|
53
|
+
/**
|
|
54
|
+
* The client layer for server code, taking application values:
|
|
55
|
+
* `framework.serverClient.<resource>.<family>.<variant>(args, options?)`.
|
|
56
|
+
*
|
|
57
|
+
* Typed and run exactly as {@link client} (the Client Contract's view, the
|
|
58
|
+
* client pipelines, validation, exposure and policy), but its arguments are
|
|
59
|
+
* the application forms: a `datetime` is a `Date`, a `bigint` a `bigint`, a
|
|
60
|
+
* `decimal` an `AvDecimal`, `bytes` a `Uint8Array`. Each call copies its
|
|
61
|
+
* arguments, encodes them into the wire forms `client` reads, and hands the
|
|
62
|
+
* same client-origin request to {@link execute}. Results are the same as
|
|
63
|
+
* `client`'s; the optional second argument is {@link AvExecutionOptions}.
|
|
64
|
+
*/
|
|
65
|
+
readonly serverClient: ServerClientOperations<M, Cfg>;
|
|
53
66
|
/**
|
|
54
67
|
* Transaction builders under application rules:
|
|
55
68
|
* `framework.appTx.<resource>.<family>.<variant>(args)` defers one operation
|
|
@@ -72,6 +85,14 @@ export type AvFramework<M extends AvAdapterModel = AvAdapterModel, Cfg = AvConfi
|
|
|
72
85
|
* share one (`A2004`/`V1020` at the first step of the other origin).
|
|
73
86
|
*/
|
|
74
87
|
readonly clientTx: ClientTransactionBuilders<M, Cfg>;
|
|
88
|
+
/**
|
|
89
|
+
* The {@link clientTx} builders taking application values, as
|
|
90
|
+
* {@link serverClient} takes them: each handle's arguments are copied and
|
|
91
|
+
* encoded into the wire forms when it is built. Its handles are client-origin,
|
|
92
|
+
* so one {@link transaction} may hold them beside `clientTx` handles, never
|
|
93
|
+
* beside `appTx` ones.
|
|
94
|
+
*/
|
|
95
|
+
readonly serverClientTx: ServerClientTransactionBuilders<M, Cfg>;
|
|
75
96
|
execute(request: AvOperationRequest): Promise<AvOperationResponse>;
|
|
76
97
|
/**
|
|
77
98
|
* Validates a whole transaction plan, then runs its operations one after
|
|
@@ -4,10 +4,11 @@ import { resolveFrameworkConfig, } from "../config/config.resolver.js";
|
|
|
4
4
|
import { validateFrameworkConfig } from "../config/config.validator.js";
|
|
5
5
|
import { validateForcedOperations } from "../config/forced-operation.validator.js";
|
|
6
6
|
import { AVENTARA_PROTOCOL_VERSION } from "../contracts/contract-protocol.js";
|
|
7
|
+
import { recordAccessProvider } from "../policy/access-provider.registry.js";
|
|
7
8
|
import { callerOf } from "../policy/policy-request.js";
|
|
8
9
|
import { policyStartupDiagnostics } from "../policy/policy-startup.validator.js";
|
|
9
10
|
import { loadPrebuiltContracts, warnStalePrebuilt, } from "../prebuilt/prebuilt-contracts.loader.js";
|
|
10
|
-
import { copyEnvelopeAtDoor, refuseCallerData, } from "../runtime/caller-data.door.js";
|
|
11
|
+
import { copyAtDoor, copyEnvelopeAtDoor, refuseCallerData, } from "../runtime/caller-data.door.js";
|
|
11
12
|
import { createDiagnosticsReporter, createTransactionDiagnosticsChannel, recordDiagnosticsSink, } from "../runtime/diagnostics-reporter.js";
|
|
12
13
|
import { createMisuseReporter, } from "../runtime/misuse-reporter.js";
|
|
13
14
|
import { internalFailure, operationFailure, } from "../runtime/operation-response.js";
|
|
@@ -16,6 +17,7 @@ import { recordOwnedExecution } from "../runtime/owned-execution.registry.js";
|
|
|
16
17
|
import { validateOperationEnvelope, validateTransactionEnvelope, } from "../runtime/request-envelope.validator.js";
|
|
17
18
|
import { requestIdOf } from "../runtime/request-id.js";
|
|
18
19
|
import { stateOptionOf } from "../runtime/request-state.resolver.js";
|
|
20
|
+
import { encodeOperationArgumentsForWire } from "../transactions/operation-arguments-encoder.js";
|
|
19
21
|
import { createTransactionPlanAssembler } from "../transactions/transaction-plan-assembler.js";
|
|
20
22
|
import { createTransactionRuntime } from "../transactions/transaction-runtime.js";
|
|
21
23
|
import { AvConstructionError } from "./framework-construction.error.js";
|
|
@@ -68,11 +70,21 @@ export async function createFramework(config) {
|
|
|
68
70
|
const executeTransaction = createTransactionRuntime(runtimeDependencies, pipelineDependencies);
|
|
69
71
|
const misuse = createMisuseReporter(resolvedConfig.misuse);
|
|
70
72
|
const executeOperation = (request, caller) => executeTotally(execute, request, resolvedConfig.diagnostics, "caller", caller);
|
|
73
|
+
const executeApplicationValues = (request, caller) => executeTotally(encodingApplicationValues(execute, compilation.contracts.client), request, resolvedConfig.diagnostics, "caller", caller);
|
|
71
74
|
const protocol = {
|
|
72
75
|
version: AVENTARA_PROTOCOL_VERSION,
|
|
73
76
|
hash: compilation.contracts.client.protocol.hash,
|
|
74
77
|
};
|
|
75
78
|
const assembler = createTransactionPlanAssembler(compilation.contracts, protocol, misuse);
|
|
79
|
+
const clientRequest = (resource, identity, args, requestId, state) => ({
|
|
80
|
+
...identity,
|
|
81
|
+
origin: "client",
|
|
82
|
+
protocol,
|
|
83
|
+
requestId,
|
|
84
|
+
...(state === undefined ? {} : { state }),
|
|
85
|
+
resource,
|
|
86
|
+
arguments: args,
|
|
87
|
+
});
|
|
76
88
|
const framework = {
|
|
77
89
|
api: resolvedConfig.api,
|
|
78
90
|
mcp: resolvedConfig.mcp,
|
|
@@ -85,25 +97,20 @@ export async function createFramework(config) {
|
|
|
85
97
|
resource,
|
|
86
98
|
arguments: args,
|
|
87
99
|
}), executeOperation),
|
|
88
|
-
client: createOperationFacade(model,
|
|
89
|
-
|
|
90
|
-
origin: "client",
|
|
91
|
-
protocol,
|
|
92
|
-
requestId,
|
|
93
|
-
...(state === undefined ? {} : { state }),
|
|
94
|
-
resource,
|
|
95
|
-
arguments: args,
|
|
96
|
-
}), executeOperation),
|
|
100
|
+
client: createOperationFacade(model, clientRequest, executeOperation),
|
|
101
|
+
serverClient: createOperationFacade(model, clientRequest, executeApplicationValues),
|
|
97
102
|
appTx: Object.freeze({
|
|
98
103
|
...createOperationTree(model, (resource, identity) => (args) => assembler.defer("application", resource, identity, args === undefined ? {} : args)),
|
|
99
104
|
[APPLICATION_SQL_BUILDER]: (strings, ...values) => assembler.deferSql(strings, values),
|
|
100
105
|
}),
|
|
101
106
|
clientTx: createOperationTree(model, (resource, identity) => (args) => assembler.defer("client", resource, identity, args === undefined ? {} : args)),
|
|
107
|
+
serverClientTx: createOperationTree(model, (resource, identity) => (args) => assembler.defer("client", resource, identity, args === undefined ? {} : args, "application")),
|
|
102
108
|
execute: (request) => executeRequestTotally(execute, request, resolvedConfig.diagnostics, misuse),
|
|
103
109
|
executeTransaction: (request) => executeTransactionRequestTotally(executeTransaction, request, resolvedConfig.diagnostics, misuse),
|
|
104
110
|
transaction: (async (operations, options) => settleOperation(await runTransactionTotally(executeTransaction, assembler, operations, requestIdOf(options), stateOptionOf(options), resolvedConfig.diagnostics, callerOf(options)))),
|
|
105
111
|
};
|
|
106
112
|
recordDiagnosticsSink(framework, resolvedConfig.diagnostics);
|
|
113
|
+
recordAccessProvider(framework, config.access);
|
|
107
114
|
recordOwnedExecution(framework, {
|
|
108
115
|
execute: (request, refused) => executeTotally(execute, request, resolvedConfig.diagnostics, refused === undefined ? "owned" : { refused }),
|
|
109
116
|
executeTransaction: (request, refused) => executeTransactionTotally(executeTransaction, request, resolvedConfig.diagnostics, undefined, refused === undefined
|
|
@@ -129,6 +136,18 @@ async function prebuiltCompilation(prebuilt, model, config, resolved) {
|
|
|
129
136
|
}
|
|
130
137
|
return { ok: true, contracts: loaded.contracts };
|
|
131
138
|
}
|
|
139
|
+
function encodingApplicationValues(execute, contract) {
|
|
140
|
+
return (request, report, _source, caller) => {
|
|
141
|
+
const copy = copyAtDoor(request.arguments);
|
|
142
|
+
if (!copy.ok) {
|
|
143
|
+
return execute(request, report, { refused: copy.error }, caller);
|
|
144
|
+
}
|
|
145
|
+
return execute({
|
|
146
|
+
...request,
|
|
147
|
+
arguments: encodeOperationArgumentsForWire(contract, request, copy.value),
|
|
148
|
+
}, report, "owned", caller);
|
|
149
|
+
};
|
|
150
|
+
}
|
|
132
151
|
async function executeTotally(execute, request, diagnostics, source, caller) {
|
|
133
152
|
const report = createDiagnosticsReporter(diagnostics, request);
|
|
134
153
|
try {
|