@aventara/core 1.0.0-rc.1 → 1.0.0-rc.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.
- package/CHANGELOG.md +1022 -16
- package/ERRORS.md +142 -0
- package/README.md +330 -16
- package/dist/adapters/adapter-discovery-diagnostic.guidance.d.ts +6 -0
- package/dist/adapters/adapter-discovery-diagnostic.guidance.js +7 -0
- package/dist/adapters/adapter-field.d.ts +22 -0
- package/dist/adapters/adapter-identifier.d.ts +9 -0
- package/dist/adapters/adapter-identifier.js +6 -1
- package/dist/adapters/adapter-model.d.ts +9 -0
- package/dist/adapters/adapter-operation.d.ts +18 -3
- package/dist/adapters/adapter-resource.d.ts +5 -0
- package/dist/adapters/adapter.interface.d.ts +10 -0
- package/dist/avz/avz.d.ts +1 -1
- package/dist/avz/json-shape.registry.d.ts +2 -1
- package/dist/compiler/contract-compiler-diagnostic.guidance.d.ts +6 -0
- package/dist/compiler/contract-compiler-diagnostic.guidance.js +97 -0
- package/dist/compiler/contract.compiler.d.ts +6 -0
- package/dist/compiler/contract.compiler.js +281 -31
- package/dist/compiler/create-requirements.projector.js +1 -3
- package/dist/compiler/field-validator.compiler.js +1 -3
- package/dist/compiler/managed-elsewhere.projector.d.ts +52 -0
- package/dist/compiler/managed-elsewhere.projector.js +179 -0
- package/dist/compiler/managed-elsewhere.validator.d.ts +9 -0
- package/dist/compiler/managed-elsewhere.validator.js +74 -0
- package/dist/config/cache-stage.factory.d.ts +24 -0
- package/dist/config/cache-stage.factory.js +3 -0
- package/dist/config/computed-fields.d.ts +62 -0
- package/dist/config/computed-fields.js +73 -0
- package/dist/config/config-diagnostic.guidance.d.ts +6 -0
- package/dist/config/config-diagnostic.guidance.js +32 -0
- package/dist/config/config.resolver.d.ts +24 -0
- package/dist/config/config.resolver.js +66 -7
- package/dist/config/config.validator.js +283 -21
- package/dist/config/define-resource.d.ts +1 -5
- package/dist/config/entrypoint.normalizer.d.ts +3 -2
- package/dist/config/exact-framework-config.d.ts +73 -22
- package/dist/config/field-compute.resolver.d.ts +2 -0
- package/dist/config/field-compute.resolver.js +9 -3
- package/dist/config/framework-config.d.ts +53 -15
- package/dist/config/framework-resources-config.d.ts +25 -14
- package/dist/config/pipeline-config.d.ts +20 -4
- package/dist/config/pipeline-tree.resolver.d.ts +2 -2
- package/dist/config/pipeline-tree.resolver.js +2 -3
- package/dist/contracts/access-route.rules.d.ts +10 -0
- package/dist/contracts/access-route.rules.js +45 -0
- package/dist/contracts/canonical-json.writer.d.ts +7 -0
- package/dist/contracts/canonical-json.writer.js +55 -0
- package/dist/contracts/capability.d.ts +26 -1
- package/dist/contracts/capability.js +20 -0
- package/dist/contracts/client-contract-structure.validator.d.ts +365 -89
- package/dist/contracts/client-contract-structure.validator.js +49 -2
- package/dist/contracts/contract-access.d.ts +29 -0
- package/dist/contracts/contract-access.js +1 -0
- package/dist/contracts/contract-canonicalizer.d.ts +9 -1
- package/dist/contracts/contract-canonicalizer.js +35 -4
- package/dist/contracts/contract-hash.d.ts +0 -1
- package/dist/contracts/contract-hash.js +1 -1
- package/dist/contracts/contract-limits.d.ts +6 -0
- package/dist/contracts/contract-limits.js +2 -0
- package/dist/contracts/contract.d.ts +42 -1
- package/dist/contracts/field.d.ts +58 -1
- package/dist/contracts/json-schema.projector.d.ts +11 -0
- package/dist/contracts/json-schema.projector.js +76 -0
- package/dist/contracts/scalar-value-type.d.ts +3 -1
- package/dist/framework/access-facade.d.ts +116 -0
- package/dist/framework/access-facade.js +118 -0
- package/dist/framework/contract-view.d.ts +120 -16
- package/dist/framework/framework-construction.error.d.ts +14 -4
- package/dist/framework/framework-construction.error.js +18 -2
- package/dist/framework/framework.d.ts +26 -5
- package/dist/framework/framework.factory.d.ts +1 -1
- package/dist/framework/framework.factory.js +145 -24
- package/dist/framework/operation-facade.d.ts +141 -13
- package/dist/framework/operation-facade.js +9 -1
- package/dist/framework/operation-set.runner.d.ts +47 -0
- package/dist/framework/operation-set.runner.js +168 -0
- package/dist/index.d.ts +19 -5
- package/dist/index.js +3 -0
- package/dist/internal.d.ts +9 -2
- package/dist/internal.js +8 -0
- package/dist/operations/database-refusal.d.ts +51 -0
- package/dist/operations/database-refusal.js +45 -0
- package/dist/operations/operation-arguments.d.ts +148 -60
- package/dist/operations/operation-call.d.ts +9 -5
- package/dist/operations/operation-result.d.ts +6 -4
- package/dist/operations/operation-validator-core.js +160 -39
- package/dist/operations/operation-validator.d.ts +1 -1
- package/dist/operations/structural-limits.walker.d.ts +1 -1
- package/dist/operations/structural-limits.walker.js +1 -3
- package/dist/operations/validation-code.guidance.d.ts +6 -0
- package/dist/operations/validation-code.guidance.js +90 -0
- package/dist/operations/write-scope.resolver.d.ts +4 -0
- package/dist/operations/write-scope.resolver.js +2 -0
- package/dist/policy/access-provider.interface.d.ts +91 -0
- package/dist/policy/access-provider.interface.js +1 -0
- package/dist/policy/access-provider.registry.d.ts +17 -0
- package/dist/policy/access-provider.registry.js +15 -0
- package/dist/policy/access-question.d.ts +20 -0
- package/dist/policy/access-question.js +1 -0
- package/dist/policy/access-question.registry.d.ts +5 -0
- package/dist/policy/access-question.registry.js +19 -0
- package/dist/policy/access-question.runtime.d.ts +36 -0
- package/dist/policy/access-question.runtime.js +295 -0
- package/dist/policy/access-verb.table.d.ts +56 -0
- package/dist/policy/access-verb.table.js +21 -0
- package/dist/policy/access-verbs.d.ts +46 -0
- package/dist/policy/access-verbs.js +1 -0
- package/dist/policy/policy-admission.d.ts +20 -0
- package/dist/policy/policy-admission.js +407 -0
- package/dist/policy/policy-after-update.runtime.d.ts +79 -0
- package/dist/policy/policy-after-update.runtime.js +206 -0
- package/dist/policy/policy-condition.d.ts +15 -0
- package/dist/policy/policy-condition.js +36 -0
- package/dist/policy/policy-engine.d.ts +205 -0
- package/dist/policy/policy-engine.js +1 -0
- package/dist/policy/policy-named-rows.d.ts +18 -0
- package/dist/policy/policy-named-rows.js +51 -0
- package/dist/policy/policy-nested-write.runtime.d.ts +44 -0
- package/dist/policy/policy-nested-write.runtime.js +536 -0
- package/dist/policy/policy-read.runtime.d.ts +81 -0
- package/dist/policy/policy-read.runtime.js +501 -0
- package/dist/policy/policy-reference-write.runtime.d.ts +51 -0
- package/dist/policy/policy-reference-write.runtime.js +336 -0
- package/dist/policy/policy-request.d.ts +72 -0
- package/dist/policy/policy-request.js +115 -0
- package/dist/policy/policy-session.d.ts +106 -0
- package/dist/policy/policy-session.js +293 -0
- package/dist/policy/policy-startup.validator.d.ts +10 -0
- package/dist/policy/policy-startup.validator.js +112 -0
- package/dist/policy/policy-write.runtime.d.ts +39 -0
- package/dist/policy/policy-write.runtime.js +530 -0
- package/dist/prebuilt/core-version.generated.d.ts +2 -0
- package/dist/prebuilt/core-version.generated.js +1 -0
- package/dist/prebuilt/prebuilt-changes.d.ts +29 -0
- package/dist/prebuilt/prebuilt-changes.js +63 -0
- package/dist/prebuilt/prebuilt-contracts.d.ts +40 -0
- package/dist/prebuilt/prebuilt-contracts.emitter.d.ts +16 -0
- package/dist/prebuilt/prebuilt-contracts.emitter.js +50 -0
- package/dist/prebuilt/prebuilt-contracts.js +1 -0
- package/dist/prebuilt/prebuilt-contracts.loader.d.ts +19 -0
- package/dist/prebuilt/prebuilt-contracts.loader.js +75 -0
- package/dist/prebuilt/prebuilt-inputs.fingerprint.d.ts +16 -0
- package/dist/prebuilt/prebuilt-inputs.fingerprint.js +227 -0
- package/dist/protocol/absent-route.responder.d.ts +49 -14
- package/dist/protocol/absent-route.responder.js +92 -15
- package/dist/protocol/contract-document.responder.d.ts +9 -1
- package/dist/protocol/contract-document.responder.js +3 -2
- package/dist/protocol/http-request.decoder.d.ts +52 -3
- package/dist/protocol/http-request.decoder.js +45 -6
- package/dist/protocol/http-request.reader.d.ts +10 -3
- package/dist/protocol/http-request.reader.js +12 -2
- package/dist/protocol/http-response.encoder.d.ts +12 -0
- package/dist/protocol/http-response.encoder.js +17 -3
- package/dist/protocol/operation-response.guard.js +1 -3
- package/dist/protocol/operation-set.decoder.d.ts +48 -0
- package/dist/protocol/operation-set.decoder.js +185 -0
- package/dist/protocol/operation-set.handler.d.ts +27 -0
- package/dist/protocol/operation-set.handler.js +179 -0
- package/dist/protocol/protocol-headers.d.ts +12 -3
- package/dist/protocol/protocol-headers.js +12 -5
- package/dist/protocol/protocol-surface.d.ts +16 -3
- package/dist/protocol/protocol-surface.js +41 -1
- package/dist/protocol/protocol.binder.d.ts +52 -1
- package/dist/protocol/protocol.binder.js +6 -1
- package/dist/protocol/protocol.handler.d.ts +45 -1
- package/dist/protocol/protocol.handler.js +164 -19
- package/dist/protocol/read-cache.responder.d.ts +26 -0
- package/dist/protocol/read-cache.responder.js +47 -0
- package/dist/protocol/read-query.codec.d.ts +34 -0
- package/dist/protocol/read-query.codec.js +125 -0
- package/dist/runtime/caller-data.copier.d.ts +18 -3
- package/dist/runtime/caller-data.copier.js +21 -8
- package/dist/runtime/caller-data.door.d.ts +13 -6
- package/dist/runtime/computed-read-runtime.d.ts +6 -1
- package/dist/runtime/computed-read-runtime.js +229 -48
- package/dist/runtime/computed-value.error.d.ts +3 -2
- package/dist/runtime/computed-value.error.js +3 -2
- package/dist/runtime/context-executor.runtime.d.ts +4 -3
- package/dist/runtime/context-executor.runtime.js +4 -2
- package/dist/runtime/core-class.brand.d.ts +10 -0
- package/dist/runtime/core-class.brand.js +21 -0
- package/dist/runtime/create-requirement.d.ts +31 -5
- package/dist/runtime/create-requirement.js +130 -24
- package/dist/runtime/decimal.d.ts +14 -0
- package/dist/runtime/decimal.js +21 -0
- package/dist/runtime/diagnostics-reporter.d.ts +49 -9
- package/dist/runtime/diagnostics-reporter.js +16 -9
- package/dist/runtime/error-boundary.d.ts +9 -2
- package/dist/runtime/error-boundary.js +12 -0
- package/dist/runtime/error-guidance.d.ts +12 -0
- package/dist/runtime/error-guidance.js +1 -0
- package/dist/runtime/error.catalog.d.ts +49 -0
- package/dist/runtime/error.catalog.js +49 -0
- package/dist/runtime/execution-options.d.ts +8 -0
- package/dist/runtime/field-check.evaluator.d.ts +0 -2
- package/dist/runtime/field-check.evaluator.js +1 -1
- package/dist/runtime/field-compute.d.ts +11 -10
- package/dist/runtime/field-stage.runtime.d.ts +3 -3
- package/dist/runtime/field-stage.runtime.js +9 -11
- package/dist/runtime/field-validator.runtime.d.ts +19 -1
- package/dist/runtime/field-validator.runtime.js +49 -9
- package/dist/runtime/framework-error.js +9 -0
- package/dist/runtime/framework-warning.emitter.d.ts +6 -0
- package/dist/runtime/framework-warning.emitter.js +8 -0
- package/dist/runtime/json-value.d.ts +10 -5
- package/dist/runtime/level-lifecycle.runtime.d.ts +4 -3
- package/dist/runtime/level-lifecycle.runtime.js +3 -5
- package/dist/runtime/mutation-planner.d.ts +1 -6
- package/dist/runtime/mutation-planner.js +4 -17
- package/dist/runtime/nested-write.walker.js +2 -1
- package/dist/runtime/operation-code.guidance.d.ts +6 -0
- package/dist/runtime/operation-code.guidance.js +167 -0
- package/dist/runtime/operation-request.d.ts +16 -9
- package/dist/runtime/operation-runtime.d.ts +35 -17
- package/dist/runtime/operation-runtime.js +220 -49
- package/dist/runtime/outcome-mapper.d.ts +2 -1
- package/dist/runtime/output-validator.d.ts +1 -1
- package/dist/runtime/output-validator.js +1 -3
- package/dist/runtime/owned-execution.registry.d.ts +34 -4
- package/dist/runtime/pipe-output.validator.d.ts +10 -0
- package/dist/runtime/pipe-output.validator.js +4 -2
- package/dist/runtime/pipeline.d.ts +52 -23
- package/dist/runtime/readonly-value.d.ts +20 -5
- package/dist/runtime/readonly-value.js +74 -26
- package/dist/runtime/record.guard.d.ts +14 -0
- package/dist/runtime/record.guard.js +10 -0
- package/dist/runtime/referential-action.d.ts +1 -1
- package/dist/runtime/request-envelope.validator.js +13 -10
- package/dist/runtime/scalar-runtime.d.ts +4 -21
- package/dist/runtime/scalar-runtime.js +91 -31
- package/dist/runtime/thenable.guard.d.ts +7 -0
- package/dist/runtime/thenable.guard.js +5 -0
- package/dist/runtime/unsent-value.normalizer.d.ts +15 -0
- package/dist/runtime/unsent-value.normalizer.js +129 -0
- package/dist/transactions/operation-arguments-encoder.js +9 -10
- package/dist/transactions/operation-fingerprint.js +2 -2
- package/dist/transactions/sql-statement.d.ts +10 -2
- package/dist/transactions/sql-statement.validator.d.ts +2 -2
- package/dist/transactions/sql-statement.validator.js +40 -6
- package/dist/transactions/transaction-envelope.d.ts +5 -4
- package/dist/transactions/transaction-integrity-error.js +2 -0
- package/dist/transactions/transaction-plan-assembler.d.ts +12 -5
- package/dist/transactions/transaction-plan-assembler.js +49 -17
- package/dist/transactions/transaction-plan-validator.d.ts +23 -7
- package/dist/transactions/transaction-plan-validator.js +31 -7
- package/dist/transactions/transaction-reference-collector.js +8 -10
- package/dist/transactions/transaction-reference-resolver.d.ts +3 -2
- package/dist/transactions/transaction-reference-resolver.js +1 -3
- package/dist/transactions/transaction-reference-validator.d.ts +43 -3
- package/dist/transactions/transaction-reference-validator.js +83 -57
- package/dist/transactions/transaction-reference.d.ts +1 -1
- package/dist/transactions/transaction-reference.js +5 -0
- package/dist/transactions/transaction-request.d.ts +8 -8
- package/dist/transactions/transaction-runtime.d.ts +5 -2
- package/dist/transactions/transaction-runtime.js +108 -27
- package/dist/transactions/transaction-settlement.runtime.d.ts +4 -3
- package/dist/transactions/transaction-slot-path-error.js +2 -0
- package/dist/transactions/transaction-stage.runtime.d.ts +5 -3
- package/dist/transactions/transaction-stage.runtime.js +8 -7
- package/package.json +6 -5
- package/src/runtime/field-check.evaluator.ts +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isAccessPath, isAccessUrl } from "./access-route.rules.js";
|
|
2
|
+
import { FILTER_MODES, PROJECTION_SCOPES, WRITE_SCOPES } from "./capability.js";
|
|
2
3
|
import { AVENTARA_PROTOCOL_VERSION } from "./contract-protocol.js";
|
|
3
4
|
import { LIFECYCLE_TRAITS } from "./lifecycle-trait.js";
|
|
4
5
|
import { REFERENCE_WRITE_LIMITS } from "./reference-write-limit.js";
|
|
@@ -226,6 +227,8 @@ const SCALAR_FIELD = object({
|
|
|
226
227
|
capabilities: object({}, {
|
|
227
228
|
select: BOOLEAN,
|
|
228
229
|
filter: list(literal(SCALAR_FILTER_OPERATORS)),
|
|
230
|
+
filterModes: list(literal(FILTER_MODES)),
|
|
231
|
+
pathFilterModes: list(literal(FILTER_MODES)),
|
|
229
232
|
order: BOOLEAN,
|
|
230
233
|
orderNulls: BOOLEAN,
|
|
231
234
|
create: list(literal(SCALAR_MUTATION_DIRECTIVES)),
|
|
@@ -235,6 +238,7 @@ const SCALAR_FIELD = object({
|
|
|
235
238
|
format: STRING,
|
|
236
239
|
json: { is: "jsonSchema" },
|
|
237
240
|
validators: list(FIELD_CHECK),
|
|
241
|
+
governed: literal([true]),
|
|
238
242
|
});
|
|
239
243
|
const RELATION_FIELD = object({
|
|
240
244
|
kind: literal(["relation"]),
|
|
@@ -255,8 +259,16 @@ const RELATION_FIELD = object({
|
|
|
255
259
|
update: RELATION_MUTATION,
|
|
256
260
|
nestedWriteFilter: literal(["scalar"]),
|
|
257
261
|
}),
|
|
262
|
+
}, {
|
|
263
|
+
inverse: STRING,
|
|
264
|
+
referenceFields: nonEmptyList(STRING),
|
|
265
|
+
governed: literal([true]),
|
|
258
266
|
});
|
|
259
267
|
const OPERATION_AVAILABILITY = union(literal([true]), object({ projection: literal(PROJECTION_SCOPES) }, { write: literal(WRITE_SCOPES) }), object({ write: literal(WRITE_SCOPES) }, { projection: literal(PROJECTION_SCOPES) }));
|
|
268
|
+
const READ_CACHE_MARK = object({
|
|
269
|
+
for: literal(["user", "anonymous", "everyone"]),
|
|
270
|
+
maxAge: NUMBER,
|
|
271
|
+
}, {});
|
|
260
272
|
const RESOURCE = object({
|
|
261
273
|
fields: registry({
|
|
262
274
|
is: "variants",
|
|
@@ -296,6 +308,32 @@ const RESOURCE = object({
|
|
|
296
308
|
oneOf: nonEmptyList(pairOrMoreList(STRING)),
|
|
297
309
|
parentSupplies: registry(registry(nonEmptyList(STRING))),
|
|
298
310
|
}),
|
|
311
|
+
cache: object({}, {
|
|
312
|
+
first: READ_CACHE_MARK,
|
|
313
|
+
unique: READ_CACHE_MARK,
|
|
314
|
+
many: READ_CACHE_MARK,
|
|
315
|
+
count: READ_CACHE_MARK,
|
|
316
|
+
}),
|
|
317
|
+
});
|
|
318
|
+
const ACCESS_VERB = object({
|
|
319
|
+
method: literal(["GET", "POST"]),
|
|
320
|
+
path: { is: "route", form: "path" },
|
|
321
|
+
result: { is: "jsonSchema" },
|
|
322
|
+
}, { args: { is: "jsonSchema" } });
|
|
323
|
+
const ACCESS = object({}, {
|
|
324
|
+
url: { is: "route", form: "url" },
|
|
325
|
+
verbs: object({}, {
|
|
326
|
+
signIn: ACCESS_VERB,
|
|
327
|
+
signUp: ACCESS_VERB,
|
|
328
|
+
signOut: ACCESS_VERB,
|
|
329
|
+
resetPassword: ACCESS_VERB,
|
|
330
|
+
me: ACCESS_VERB,
|
|
331
|
+
switchUser: ACCESS_VERB,
|
|
332
|
+
lock: ACCESS_VERB,
|
|
333
|
+
setPin: ACCESS_VERB,
|
|
334
|
+
enrollDevice: ACCESS_VERB,
|
|
335
|
+
revokeDevice: ACCESS_VERB,
|
|
336
|
+
}),
|
|
299
337
|
});
|
|
300
338
|
export const CLIENT_CONTRACT_STRUCTURE = object({
|
|
301
339
|
scalars: registry(object({ builtin: literal([true]) })),
|
|
@@ -308,13 +346,15 @@ export const CLIENT_CONTRACT_STRUCTURE = object({
|
|
|
308
346
|
maxTransactionOperations: NUMBER,
|
|
309
347
|
maxBooleanNodes: NUMBER,
|
|
310
348
|
maxJsonDepth: NUMBER,
|
|
349
|
+
maxAll: NUMBER,
|
|
350
|
+
maxAllConcurrency: NUMBER,
|
|
311
351
|
}),
|
|
312
352
|
transactions: literal(TRANSACTION_CAPABILITIES),
|
|
313
353
|
protocol: object({
|
|
314
354
|
version: literal([AVENTARA_PROTOCOL_VERSION]),
|
|
315
355
|
hash: { is: "prefixed", prefix: "sha256:" },
|
|
316
356
|
}),
|
|
317
|
-
}, { referenceWrites: literal(REFERENCE_WRITE_LIMITS) });
|
|
357
|
+
}, { referenceWrites: literal(REFERENCE_WRITE_LIMITS), access: ACCESS });
|
|
318
358
|
export function validateClientContractStructure(value) {
|
|
319
359
|
const defect = defectOf(CLIENT_CONTRACT_STRUCTURE, value, "$", 0);
|
|
320
360
|
if (defect !== undefined) {
|
|
@@ -336,6 +376,13 @@ function defectOf(node, value, path, depth) {
|
|
|
336
376
|
(value.startsWith(node.prefix)
|
|
337
377
|
? undefined
|
|
338
378
|
: at(path, depth, `is ${JSON.stringify(value)}, which does not begin with ${JSON.stringify(node.prefix)}`)));
|
|
379
|
+
case "route":
|
|
380
|
+
return (kindDefect(value, "string", path, depth) ??
|
|
381
|
+
((node.form === "url" ? isAccessUrl : isAccessPath)(value)
|
|
382
|
+
? undefined
|
|
383
|
+
: at(path, depth, node.form === "url"
|
|
384
|
+
? "is no absolute http(s) URL and no root-relative path"
|
|
385
|
+
: 'is no path under the url, starting with one "/"')));
|
|
339
386
|
case "list":
|
|
340
387
|
return listDefect(node, value, path, depth);
|
|
341
388
|
case "registry": {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { JsonSchema } from "./json-shape.js";
|
|
2
|
+
/**
|
|
3
|
+
* The ClientContract's `access` section: present exactly when the framework's
|
|
4
|
+
* `access` option is configured, and then it marks the `POST /_access/can`
|
|
5
|
+
* route. `url` and `verbs` say where Aventara Access is mounted and each of its
|
|
6
|
+
* `$access` verbs, when the provider states them.
|
|
7
|
+
*/
|
|
8
|
+
export type AvContractClientAccess = {
|
|
9
|
+
/** Where the app mounted Access: absolute, or root-relative (`"/api/auth"`). */
|
|
10
|
+
readonly url?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Each `$access` verb the provider offers: its route under `url`, and the
|
|
13
|
+
* JSON Schema of its arguments and result. A verb left out is no verb of
|
|
14
|
+
* `$access` (a `service` profile has no `signIn` or `signUp`; only a
|
|
15
|
+
* `kiosk` profile has `switchUser`, `lock`, `setPin`, `enrollDevice` and
|
|
16
|
+
* `revokeDevice`).
|
|
17
|
+
*/
|
|
18
|
+
readonly verbs?: {
|
|
19
|
+
readonly [Verb in "signIn" | "signUp" | "signOut" | "resetPassword" | "me" | "switchUser" | "lock" | "setPin" | "enrollDevice" | "revokeDevice"]?: {
|
|
20
|
+
readonly method: "GET" | "POST";
|
|
21
|
+
/** Under `url`: `"/sign-in/email"`. */
|
|
22
|
+
readonly path: string;
|
|
23
|
+
/** The arguments' JSON Schema; absent when the verb takes none. */
|
|
24
|
+
readonly args?: JsonSchema;
|
|
25
|
+
/** The result's JSON Schema (`me`: the value when someone is signed in). */
|
|
26
|
+
readonly result: JsonSchema;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -7,8 +7,15 @@ export declare class AvCanonicalJsonError extends TypeError {
|
|
|
7
7
|
}
|
|
8
8
|
/** Canonical JSON (RFC 8785) for a JSON-safe value. */
|
|
9
9
|
export declare function canonicalizeJson(value: unknown): string;
|
|
10
|
+
/**
|
|
11
|
+
* Canonical JSON (RFC 8785) for a value as JSON carries it: a key holding
|
|
12
|
+
* `undefined` is left out, and an array element holding it (or a hole) is
|
|
13
|
+
* `null`, as `JSON.stringify` writes them. Everything else as
|
|
14
|
+
* {@link canonicalizeJson}.
|
|
15
|
+
*/
|
|
16
|
+
export declare function canonicalizeWireJson(value: unknown): string;
|
|
10
17
|
/** One top-level member of a canonical Contract: its key and its value's canonical text. */
|
|
11
|
-
|
|
18
|
+
type CanonicalContractMember = {
|
|
12
19
|
readonly key: string;
|
|
13
20
|
readonly text: string;
|
|
14
21
|
};
|
|
@@ -25,3 +32,4 @@ export declare function recordCanonicalContractText(contract: AvContract, text:
|
|
|
25
32
|
* recorded one, else written now. Both are the same bytes for the same Contract.
|
|
26
33
|
*/
|
|
27
34
|
export declare function canonicalContractTextOf<C extends AvContract>(contract: C): string;
|
|
35
|
+
export {};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { brandCoreClass } from "../runtime/core-class.brand.js";
|
|
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";
|
|
4
5
|
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;
|
|
@@ -61,6 +63,12 @@ function normalizeCapabilities(capabilities) {
|
|
|
61
63
|
if (Array.isArray(filter)) {
|
|
62
64
|
normalized.filter = normalizeStringSet(filter);
|
|
63
65
|
}
|
|
66
|
+
for (const key of ["filterModes", "pathFilterModes"]) {
|
|
67
|
+
const modes = normalized[key];
|
|
68
|
+
if (Array.isArray(modes)) {
|
|
69
|
+
normalized[key] = normalizeStringSet(modes, FILTER_MODES);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
64
72
|
for (const key of ["create", "update"]) {
|
|
65
73
|
const value = normalized[key];
|
|
66
74
|
if (Array.isArray(value)) {
|
|
@@ -178,6 +186,21 @@ function normalizeResource(resource) {
|
|
|
178
186
|
: {
|
|
179
187
|
createRequirements: normalizeCreateRequirements(resource.createRequirements),
|
|
180
188
|
}),
|
|
189
|
+
...coreResourceFacts(resource),
|
|
190
|
+
...clientResourceFacts(resource),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function clientResourceFacts(resource) {
|
|
194
|
+
const { cache } = resource;
|
|
195
|
+
return cache === undefined ? {} : { cache };
|
|
196
|
+
}
|
|
197
|
+
function coreResourceFacts(resource) {
|
|
198
|
+
const facts = resource;
|
|
199
|
+
return {
|
|
200
|
+
...(facts.schema === undefined ? {} : { schema: facts.schema }),
|
|
201
|
+
...(facts.managedElsewhere === undefined
|
|
202
|
+
? {}
|
|
203
|
+
: { managedElsewhere: facts.managedElsewhere }),
|
|
181
204
|
};
|
|
182
205
|
}
|
|
183
206
|
function isJsonObject(value) {
|
|
@@ -286,7 +309,7 @@ function canonicalizePrimitive(value, link) {
|
|
|
286
309
|
return undefined;
|
|
287
310
|
}
|
|
288
311
|
}
|
|
289
|
-
function canonicalizeValue(value, link = undefined) {
|
|
312
|
+
function canonicalizeValue(value, link = undefined, undefinedAsJson = false) {
|
|
290
313
|
const output = [];
|
|
291
314
|
const stack = [{ kind: "value", value, link }];
|
|
292
315
|
while (stack.length > 0) {
|
|
@@ -310,7 +333,10 @@ function canonicalizeValue(value, link = undefined) {
|
|
|
310
333
|
output.push("[");
|
|
311
334
|
stack.push({ kind: "text", text: "]" });
|
|
312
335
|
for (let index = current.length - 1; index >= 0; index -= 1) {
|
|
313
|
-
if (index
|
|
336
|
+
if (undefinedAsJson && current[index] === undefined) {
|
|
337
|
+
stack.push({ kind: "text", text: "null" });
|
|
338
|
+
}
|
|
339
|
+
else if (index in current) {
|
|
314
340
|
stack.push({
|
|
315
341
|
kind: "value",
|
|
316
342
|
value: current[index],
|
|
@@ -328,7 +354,9 @@ function canonicalizeValue(value, link = undefined) {
|
|
|
328
354
|
throw new AvCanonicalJsonError(`non-plain object at ${pathOf(link)} is not canonical JSON`);
|
|
329
355
|
}
|
|
330
356
|
const record = current;
|
|
331
|
-
const keys = Object.keys(record)
|
|
357
|
+
const keys = Object.keys(record)
|
|
358
|
+
.filter((key) => !undefinedAsJson || record[key] !== undefined)
|
|
359
|
+
.sort(compareCodeUnits);
|
|
332
360
|
output.push("{");
|
|
333
361
|
stack.push({ kind: "text", text: "}" });
|
|
334
362
|
for (let index = keys.length - 1; index >= 0; index -= 1) {
|
|
@@ -349,6 +377,9 @@ function canonicalizeValue(value, link = undefined) {
|
|
|
349
377
|
export function canonicalizeJson(value) {
|
|
350
378
|
return canonicalizeValue(value);
|
|
351
379
|
}
|
|
380
|
+
export function canonicalizeWireJson(value) {
|
|
381
|
+
return canonicalizeValue(value, undefined, true);
|
|
382
|
+
}
|
|
352
383
|
export function canonicalizeContractMembers(contract) {
|
|
353
384
|
const record = normalizeContract(contract);
|
|
354
385
|
return Object.keys(record)
|
|
@@ -4,7 +4,6 @@ import type { AvContractHash, AvContractProtocol } from "./contract-protocol.js"
|
|
|
4
4
|
export type AvContractClientHashInput = Omit<AvContractClient, "protocol"> & {
|
|
5
5
|
readonly protocol: AvContractProtocol;
|
|
6
6
|
};
|
|
7
|
-
export declare function createClientContractHashInput(contract: AvContractClient | AvContractClientHashInput): AvContractClientHashInput;
|
|
8
7
|
/** Computes a ClientContract's `sha256:` hash, ignoring `protocol.hash` itself. */
|
|
9
8
|
export declare function computeClientContractHash(contract: AvContractClient | AvContractClientHashInput): Promise<AvContractHash>;
|
|
10
9
|
/**
|
|
@@ -12,6 +12,10 @@ export type AvContractLimits = {
|
|
|
12
12
|
readonly maxBooleanNodes: number;
|
|
13
13
|
/** How deeply a `json` field's value may nest: its arrays and objects, counted from the value. */
|
|
14
14
|
readonly maxJsonDepth: number;
|
|
15
|
+
/** The most operations one Operation Set (`POST /_all`) may hold. */
|
|
16
|
+
readonly maxAll: number;
|
|
17
|
+
/** The most entries of one Operation Set that run at once; at least 1. */
|
|
18
|
+
readonly maxAllConcurrency: number;
|
|
15
19
|
};
|
|
16
20
|
/** The limits in force when the configuration sets none. */
|
|
17
21
|
export declare const DEFAULT_CONTRACT_LIMITS: {
|
|
@@ -21,4 +25,6 @@ export declare const DEFAULT_CONTRACT_LIMITS: {
|
|
|
21
25
|
readonly maxTransactionOperations: 20;
|
|
22
26
|
readonly maxBooleanNodes: 50;
|
|
23
27
|
readonly maxJsonDepth: 64;
|
|
28
|
+
readonly maxAll: 10;
|
|
29
|
+
readonly maxAllConcurrency: 4;
|
|
24
30
|
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { AvContractClientAccess } from "./contract-access.js";
|
|
1
2
|
import type { AvContractLimits } from "./contract-limits.js";
|
|
2
3
|
import type { AvContractClientProtocol } from "./contract-protocol.js";
|
|
3
4
|
import type { AvEnumDefinition } from "./enum-definition.js";
|
|
5
|
+
import type { AvCoreField } from "./field.js";
|
|
4
6
|
import type { ReferenceWriteLimit } from "./reference-write-limit.js";
|
|
5
7
|
import type { AvResource } from "./resource.js";
|
|
6
8
|
import type { AvScalarRegistry } from "./scalar.js";
|
|
@@ -24,8 +26,27 @@ export interface AvContract {
|
|
|
24
26
|
*/
|
|
25
27
|
readonly referenceWrites?: ReferenceWriteLimit;
|
|
26
28
|
}
|
|
27
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* The Contract compiled from the root configuration. Its relation fields also
|
|
31
|
+
* state the target's fields their reference names (`references`), and its
|
|
32
|
+
* Resources the database schema the store keeps them in (`schema`) and whether
|
|
33
|
+
* another service owns them (`managedElsewhere`), which no other Contract
|
|
34
|
+
* carries.
|
|
35
|
+
*/
|
|
28
36
|
export interface AvContractCore extends AvContract {
|
|
37
|
+
/** The Resources, by name. */
|
|
38
|
+
readonly resources: Readonly<Record<string, AvCoreResource>>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A Resource as the Core Contract states it: its fields are
|
|
42
|
+
* {@link AvCoreField}s, so its relation fields carry `references`.
|
|
43
|
+
*/
|
|
44
|
+
export interface AvCoreResource extends AvResource<AvContractCore> {
|
|
45
|
+
readonly fields: Readonly<Record<string, AvCoreField<AvContractCore>>>;
|
|
46
|
+
/** The database schema the store keeps this Resource in, when the adapter states one. */
|
|
47
|
+
readonly schema?: string;
|
|
48
|
+
/** Present when another service owns this Resource: it is read, never written. */
|
|
49
|
+
readonly managedElsewhere?: true;
|
|
29
50
|
}
|
|
30
51
|
/** The Contract in-process calls run under (`framework.application`). */
|
|
31
52
|
export interface AvContractApplication extends AvContract {
|
|
@@ -35,8 +56,28 @@ export interface AvContractApplication extends AvContract {
|
|
|
35
56
|
* identified by its hash.
|
|
36
57
|
*/
|
|
37
58
|
export interface AvContractClient extends AvContract {
|
|
59
|
+
/**
|
|
60
|
+
* The Resources, by name. One with a cached read carries `cache`, by `find`
|
|
61
|
+
* variant: that read is also served as
|
|
62
|
+
* `GET /_resources/<resource>/find/<variant>?q=…&v=…`, which a browser
|
|
63
|
+
* (`for: "user"`), a CDN for anonymous callers (`"anonymous"`) or a CDN for
|
|
64
|
+
* everyone (`"everyone"`) may keep for `maxAge` seconds.
|
|
65
|
+
*/
|
|
66
|
+
readonly resources: Readonly<Record<string, AvResource<this> & {
|
|
67
|
+
readonly cache?: {
|
|
68
|
+
readonly [variant in "first" | "unique" | "many" | "count"]?: {
|
|
69
|
+
readonly for: "user" | "anonymous" | "everyone";
|
|
70
|
+
readonly maxAge: number;
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
}>>;
|
|
38
74
|
/** The protocol version and this Contract's hash. */
|
|
39
75
|
readonly protocol: AvContractClientProtocol;
|
|
76
|
+
/**
|
|
77
|
+
* Present exactly when the framework's `access` option is configured: the
|
|
78
|
+
* `POST /_access/can` route, and `$access`'s other verbs when it has `verbs`.
|
|
79
|
+
*/
|
|
80
|
+
readonly access?: AvContractClientAccess;
|
|
40
81
|
}
|
|
41
82
|
/**
|
|
42
83
|
* The Contract an AI agent's calls run under (the `agent` layer), identified by
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AvPaginationCapability, AvProjectionScope, AvRelationFilterCapability, AvRelationMutationDirective, AvRelationReducer, AvScalarFilterOperator, AvScalarMutationDirective, AvWriteScope } from "./capability.js";
|
|
1
|
+
import type { AvPaginationCapability, AvProjectionScope, AvRelationFilterCapability, AvRelationMutationDirective, AvRelationReducer, AvScalarFilterOperator, AvScalarMutationDirective, AvWriteScope, ScalarFilterMode } from "./capability.js";
|
|
2
2
|
import type { AvContract } from "./contract.js";
|
|
3
3
|
import type { AvFieldCheck } from "./field-check.js";
|
|
4
4
|
import type { JsonSchema } from "./json-shape.js";
|
|
@@ -53,6 +53,19 @@ export type AvRelationMutationCapability = readonly AvRelationMutationDirective[
|
|
|
53
53
|
export type AvScalarFieldCapabilities = {
|
|
54
54
|
readonly select?: boolean;
|
|
55
55
|
readonly filter?: readonly AvScalarFilterOperator[];
|
|
56
|
+
/**
|
|
57
|
+
* The modes the `mode` operator takes on this field: `"default"` (the
|
|
58
|
+
* database's own comparison) always, and `"sensitive"` or `"insensitive"`
|
|
59
|
+
* where the adapter honours them. Present exactly when `filter` offers `mode`.
|
|
60
|
+
*/
|
|
61
|
+
readonly filterModes?: readonly ScalarFilterMode[];
|
|
62
|
+
/**
|
|
63
|
+
* A `json` field's modes when the filter names a `path`: the text compared is
|
|
64
|
+
* the value at that path rather than the whole document, and the database may
|
|
65
|
+
* honour more modes there. Present exactly when `filter` offers `mode` and
|
|
66
|
+
* `path`; without a `path`, `filterModes` applies.
|
|
67
|
+
*/
|
|
68
|
+
readonly pathFilterModes?: readonly ScalarFilterMode[];
|
|
56
69
|
readonly order?: boolean;
|
|
57
70
|
readonly orderNulls?: boolean;
|
|
58
71
|
readonly create?: readonly AvScalarMutationDirective[];
|
|
@@ -125,6 +138,12 @@ export type AvScalarField<C extends AvContract> = {
|
|
|
125
138
|
* `list: true` length check, which applies to the list.
|
|
126
139
|
*/
|
|
127
140
|
readonly validators?: readonly AvFieldCheck[];
|
|
141
|
+
/**
|
|
142
|
+
* `true` when the policy governs the field: it is left out of every default
|
|
143
|
+
* projection, and a caller that wants it names it (`select: ["$defaults",
|
|
144
|
+
* "email"]`).
|
|
145
|
+
*/
|
|
146
|
+
readonly governed?: true;
|
|
128
147
|
};
|
|
129
148
|
/** A relation field; the `target` Resource describes the related row. */
|
|
130
149
|
export type AvRelationField<C extends AvContract> = {
|
|
@@ -133,6 +152,44 @@ export type AvRelationField<C extends AvContract> = {
|
|
|
133
152
|
readonly cardinality: "one" | "many";
|
|
134
153
|
readonly nullable: boolean;
|
|
135
154
|
readonly capabilities: AvRelationFieldCapabilities;
|
|
155
|
+
/**
|
|
156
|
+
* The target's relation field that is this relation's other end
|
|
157
|
+
* (`User.posts` ↔ `Post.author`): a record written through this relation sits
|
|
158
|
+
* at that field's `"loopback"` write position. Absent when the adapter states
|
|
159
|
+
* no pairing or the layer hides the other end.
|
|
160
|
+
*/
|
|
161
|
+
readonly inverse?: string;
|
|
162
|
+
/**
|
|
163
|
+
* The scalar fields of this Resource that hold the relation's reference, in the
|
|
164
|
+
* order the data source pairs them with the target's key (`Post.category` has
|
|
165
|
+
* `["categoryId"]`). Writing the relation and one of them in one record names
|
|
166
|
+
* the reference twice; inside a record written through the relation's other
|
|
167
|
+
* end they sit at the `"loopback"` position with it. Absent on the end that
|
|
168
|
+
* holds no reference, and where the layer shows none of them.
|
|
169
|
+
*/
|
|
170
|
+
readonly referenceFields?: readonly [string, ...string[]];
|
|
171
|
+
/**
|
|
172
|
+
* `true` when the policy engine's `governed` set names the relation: including
|
|
173
|
+
* it on rows the caller may not read it on refuses the request.
|
|
174
|
+
*/
|
|
175
|
+
readonly governed?: true;
|
|
136
176
|
};
|
|
137
177
|
/** One field of a Resource: a scalar or a relation. */
|
|
138
178
|
export type AvField<C extends AvContract> = AvScalarField<C> | AvRelationField<C>;
|
|
179
|
+
/**
|
|
180
|
+
* A relation field as the Core Contract states it: an {@link AvRelationField}
|
|
181
|
+
* and, on the end that holds the reference, the target's fields it names.
|
|
182
|
+
*/
|
|
183
|
+
export type AvCoreRelationField<C extends AvContract> = AvRelationField<C> & {
|
|
184
|
+
/**
|
|
185
|
+
* The target's scalar fields that `referenceFields` name, pair by pair in the
|
|
186
|
+
* same order (`Post.author`: `referenceFields: ["authorId"]`,
|
|
187
|
+
* `references: ["id"]`). Present only beside a `referenceFields` the
|
|
188
|
+
* Contract states whole, and only when every one of them is a scalar field
|
|
189
|
+
* the Core Contract shows on the target; absent on the end that holds no
|
|
190
|
+
* reference, where the other end (`inverse`) states the pairs.
|
|
191
|
+
*/
|
|
192
|
+
readonly references?: readonly [string, ...string[]];
|
|
193
|
+
};
|
|
194
|
+
/** One field of a Resource as the Core Contract states it. */
|
|
195
|
+
export type AvCoreField<C extends AvContract> = AvScalarField<C> | AvCoreRelationField<C>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
type Schema = {
|
|
2
|
+
readonly [key: string]: unknown;
|
|
3
|
+
};
|
|
4
|
+
/**
|
|
5
|
+
* `value` projected onto `schema` (the `json` field subset): every object down
|
|
6
|
+
* to the members the schema declares, a list member by member, frozen.
|
|
7
|
+
*
|
|
8
|
+
* @throws TypeError when the value is not one the schema describes.
|
|
9
|
+
*/
|
|
10
|
+
export declare function projectJsonValue(value: unknown, schema: Schema): unknown;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { isRecord } from "../runtime/record.guard.js";
|
|
2
|
+
function memberSchema(schema) {
|
|
3
|
+
if (!isRecord(schema)) {
|
|
4
|
+
throw new TypeError("the schema is not one of the forms a ClientContract states");
|
|
5
|
+
}
|
|
6
|
+
return schema;
|
|
7
|
+
}
|
|
8
|
+
function fits(value, schema) {
|
|
9
|
+
if (Object.hasOwn(schema, "const")) {
|
|
10
|
+
return value === schema.const;
|
|
11
|
+
}
|
|
12
|
+
if (Array.isArray(schema.enum)) {
|
|
13
|
+
return schema.enum.includes(value);
|
|
14
|
+
}
|
|
15
|
+
for (const keyword of ["anyOf", "oneOf"]) {
|
|
16
|
+
const options = schema[keyword];
|
|
17
|
+
if (Array.isArray(options)) {
|
|
18
|
+
return options.some((option) => fits(value, option));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
switch (schema.type) {
|
|
22
|
+
case "null":
|
|
23
|
+
return value === null;
|
|
24
|
+
case "boolean":
|
|
25
|
+
return typeof value === "boolean";
|
|
26
|
+
case "string":
|
|
27
|
+
return typeof value === "string";
|
|
28
|
+
case "number":
|
|
29
|
+
return typeof value === "number";
|
|
30
|
+
case "integer":
|
|
31
|
+
return Number.isInteger(value);
|
|
32
|
+
case "array":
|
|
33
|
+
return Array.isArray(value);
|
|
34
|
+
case "object":
|
|
35
|
+
return isRecord(value);
|
|
36
|
+
default:
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function projectJsonValue(value, schema) {
|
|
41
|
+
if (!fits(value, schema)) {
|
|
42
|
+
throw new TypeError("the value is not the one the ClientContract describes");
|
|
43
|
+
}
|
|
44
|
+
for (const keyword of ["anyOf", "oneOf"]) {
|
|
45
|
+
const options = schema[keyword];
|
|
46
|
+
if (Array.isArray(options)) {
|
|
47
|
+
const option = options.find((candidate) => fits(value, candidate));
|
|
48
|
+
return projectJsonValue(value, option);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (Array.isArray(value)) {
|
|
52
|
+
const prefix = schema.prefixItems;
|
|
53
|
+
const items = Array.isArray(prefix)
|
|
54
|
+
? undefined
|
|
55
|
+
: memberSchema(schema.items);
|
|
56
|
+
return Object.freeze(value.map((member, index) => projectJsonValue(member, items ?? memberSchema(prefix[index]))));
|
|
57
|
+
}
|
|
58
|
+
if (isRecord(value)) {
|
|
59
|
+
const properties = schema.properties;
|
|
60
|
+
if (isRecord(properties)) {
|
|
61
|
+
const copy = {};
|
|
62
|
+
for (const [key, member] of Object.entries(properties)) {
|
|
63
|
+
if (Object.hasOwn(value, key) && value[key] !== undefined) {
|
|
64
|
+
copy[key] = projectJsonValue(value[key], member);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return Object.freeze(copy);
|
|
68
|
+
}
|
|
69
|
+
const values = memberSchema(schema.additionalProperties);
|
|
70
|
+
return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, member]) => [
|
|
71
|
+
key,
|
|
72
|
+
projectJsonValue(member, values),
|
|
73
|
+
])));
|
|
74
|
+
}
|
|
75
|
+
return value;
|
|
76
|
+
}
|
|
@@ -78,5 +78,7 @@ type ListValue<List, Atom> = [List] extends [false] ? Atom : [List] extends [tru
|
|
|
78
78
|
* - `list: true` → `readonly T[]`, whose entries are never `null`;
|
|
79
79
|
* - `nullable: true` → `| null`, on the whole value, list or not.
|
|
80
80
|
*/
|
|
81
|
-
export type AvScalarValueType<C extends AvContract, Field extends AvScalarField<C>, Forms extends ScalarForms = AvApplicationScalarForms> =
|
|
81
|
+
export type AvScalarValueType<C extends AvContract, Field extends AvScalarField<C>, Forms extends ScalarForms = AvApplicationScalarForms> = [
|
|
82
|
+
ListValue<Field["list"], FieldAtom<C, Field, Forms>> | (true extends Field["nullable"] ? null : never)
|
|
83
|
+
] extends [infer Value] ? Value : never;
|
|
82
84
|
export {};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { AvContract } from "../contracts/contract.js";
|
|
2
|
+
import type { AvApplicationScalarForms } from "../contracts/scalar-value-type.js";
|
|
3
|
+
import type { AvResourceKey, AvUniqueWhereInput } from "../operations/operation-arguments.js";
|
|
4
|
+
import type { AvAccessProvider } from "../policy/access-provider.interface.js";
|
|
5
|
+
import type { AvAccessQuestion } from "../policy/access-question.js";
|
|
6
|
+
import type { AccessQuestionsAnswerer } from "../policy/access-question.runtime.js";
|
|
7
|
+
import type { AvAccessVerbs } from "../policy/access-verbs.js";
|
|
8
|
+
import type { AvState } from "../runtime/request-state.js";
|
|
9
|
+
/** The questions a client of `C` may ask: one per Resource, with its own unique `where` in the application forms. */
|
|
10
|
+
export type AccessQuestionOf<C extends AvContract> = C extends AvContract ? {
|
|
11
|
+
readonly [R in AvResourceKey<C>]: AvAccessQuestion<R, AvUniqueWhereInput<C, R, AvApplicationScalarForms>>;
|
|
12
|
+
}[AvResourceKey<C>] : never;
|
|
13
|
+
/** The headers a server-side `$access` call hands Access: a Web `Headers`, or a Node request's `headers`. */
|
|
14
|
+
type AccessHeadersInput = {
|
|
15
|
+
get(name: string): string | null;
|
|
16
|
+
has(name: string): boolean;
|
|
17
|
+
forEach(callback: (value: string, name: string) => void): void;
|
|
18
|
+
} | {
|
|
19
|
+
readonly [name: string]: string | readonly string[] | undefined;
|
|
20
|
+
};
|
|
21
|
+
/** A server-side `$access` call's options. */
|
|
22
|
+
type ServerAccessOptions = {
|
|
23
|
+
/** As `AvExecutionOptions.requestId`: `can`'s request id. */
|
|
24
|
+
readonly requestId?: string;
|
|
25
|
+
/**
|
|
26
|
+
* As `AvExecutionOptions.state`: the state `can` is answered under. Left out,
|
|
27
|
+
* `can` is answered under the state the provider seeds from `headers`, or
|
|
28
|
+
* anonymously without them.
|
|
29
|
+
*/
|
|
30
|
+
readonly state?: AvState & object;
|
|
31
|
+
/**
|
|
32
|
+
* The request's headers, which Access reads the session from: a Web
|
|
33
|
+
* `Headers`, or a Node request's `headers` (`req.headers`). Omitted, none.
|
|
34
|
+
*/
|
|
35
|
+
readonly headers?: AccessHeadersInput;
|
|
36
|
+
/** The host's own request, handed to Access as `state` is. */
|
|
37
|
+
readonly request?: unknown;
|
|
38
|
+
/**
|
|
39
|
+
* The host's own response (Express's `res`, Fastify's `reply`): Access sets
|
|
40
|
+
* a sign-in's or sign-out's cookie on it. Under Next, a Server Action needs
|
|
41
|
+
* none: Access sets it through Next's `cookies()`.
|
|
42
|
+
*/
|
|
43
|
+
readonly response?: unknown;
|
|
44
|
+
};
|
|
45
|
+
/** What a verb's `call` resolves, `null` and `undefined` taken out. */
|
|
46
|
+
type CallResult<Verb> = Verb extends {
|
|
47
|
+
call(input: never): Promise<infer Result>;
|
|
48
|
+
} ? Exclude<Result, null | undefined> : unknown;
|
|
49
|
+
/** The `args` a verb's `call` reads. */
|
|
50
|
+
type CallArgs<Verb> = Verb extends {
|
|
51
|
+
call(input: infer Input): Promise<unknown>;
|
|
52
|
+
} ? Input extends {
|
|
53
|
+
readonly args: infer Args;
|
|
54
|
+
} ? Args : unknown : unknown;
|
|
55
|
+
/** The provider's `client`, when its type states one. */
|
|
56
|
+
type ClientOf<A> = A extends {
|
|
57
|
+
readonly client: infer Client;
|
|
58
|
+
} ? Exclude<Client, undefined> : never;
|
|
59
|
+
/** The verbs a provider's `verbs` type states as present (an optional one is no verb). */
|
|
60
|
+
type PresentVerbs<Verbs> = {
|
|
61
|
+
[V in keyof Verbs]-?: undefined extends Verbs[V] ? never : V;
|
|
62
|
+
}[keyof Verbs] & ProviderVerbName;
|
|
63
|
+
/** The verbs a provider's `client` may state. */
|
|
64
|
+
type ProviderVerbName = keyof NonNullable<AvAccessProvider["client"]>["verbs"];
|
|
65
|
+
/** `$access` of a client of `C` whose provider is `A`: `can`, and the verbs its `client` offers. */
|
|
66
|
+
type ServerAccess<C extends AvContract, A> = [ClientOf<A>] extends [never] ? Pick<AvAccessVerbs<AccessQuestionOf<C>, never, {
|
|
67
|
+
signIn: never;
|
|
68
|
+
signUp: never;
|
|
69
|
+
resetPassword: never;
|
|
70
|
+
switchUser: never;
|
|
71
|
+
setPin: never;
|
|
72
|
+
enrollDevice: never;
|
|
73
|
+
revokeDevice: never;
|
|
74
|
+
}, ServerAccessOptions>, "can"> : ClientOf<A> extends {
|
|
75
|
+
readonly verbs: infer Verbs;
|
|
76
|
+
} ? Pick<AvAccessVerbs<AccessQuestionOf<C>, CallResult<VerbOf<Verbs, "me">>, {
|
|
77
|
+
signIn: CallArgs<VerbOf<Verbs, "signIn">>;
|
|
78
|
+
signUp: CallArgs<VerbOf<Verbs, "signUp">>;
|
|
79
|
+
resetPassword: CallArgs<VerbOf<Verbs, "resetPassword">>;
|
|
80
|
+
switchUser: CallArgs<VerbOf<Verbs, "switchUser">>;
|
|
81
|
+
setPin: CallArgs<VerbOf<Verbs, "setPin">>;
|
|
82
|
+
enrollDevice: CallArgs<VerbOf<Verbs, "enrollDevice">>;
|
|
83
|
+
revokeDevice: CallArgs<VerbOf<Verbs, "revokeDevice">>;
|
|
84
|
+
}, ServerAccessOptions>, "can" | PresentVerbs<Verbs>> : never;
|
|
85
|
+
/** One verb of a provider's `verbs` type, or `never` when it states none. */
|
|
86
|
+
type VerbOf<Verbs, V extends string> = V extends keyof Verbs ? Exclude<Verbs[V], undefined> : never;
|
|
87
|
+
/** The configuration's `access` provider; `undefined` without one. */
|
|
88
|
+
export type AccessProviderOf<Cfg> = Cfg extends {
|
|
89
|
+
readonly access: infer A extends AvAccessProvider;
|
|
90
|
+
} ? A : undefined;
|
|
91
|
+
/**
|
|
92
|
+
* `framework.serverClient`'s `$access` member over the Client Contract's view
|
|
93
|
+
* `C`: `{ $access }` with `access`, nothing (`unknown`) without it.
|
|
94
|
+
*/
|
|
95
|
+
export type ServerClientAccess<C, A> = [A] extends [undefined] ? unknown : C extends AvContract ? {
|
|
96
|
+
/**
|
|
97
|
+
* The policy's answers for the UI (`can`) and, when the provider
|
|
98
|
+
* states its `client`, the Aventara Access verbs, each run in
|
|
99
|
+
* process with the headers the call names.
|
|
100
|
+
*/
|
|
101
|
+
readonly $access: ServerAccess<C, A>;
|
|
102
|
+
} : unknown;
|
|
103
|
+
/** What `$access` reads off the Framework. */
|
|
104
|
+
type ServerClientAccessDependencies = {
|
|
105
|
+
/** The Client Contract, whose unique `where`s a question's is encoded by. */
|
|
106
|
+
readonly contract: AvContract;
|
|
107
|
+
/** The `can` route's answerer. */
|
|
108
|
+
readonly answer: AccessQuestionsAnswerer;
|
|
109
|
+
/** The provider's `client`, when it has one. */
|
|
110
|
+
readonly client: AvAccessProvider["client"];
|
|
111
|
+
/** The provider's `state`, called on the provider: `can`'s state from a call's `headers`. */
|
|
112
|
+
readonly state: AvAccessProvider["state"];
|
|
113
|
+
};
|
|
114
|
+
/** A server-side `$access`, as `serverClient` carries it. */
|
|
115
|
+
export declare function createServerClientAccess(dependencies: ServerClientAccessDependencies): object;
|
|
116
|
+
export {};
|