@aventara/core 1.0.0-rc.3 → 1.0.0-rc.8

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.
Files changed (96) hide show
  1. package/CHANGELOG.md +231 -1
  2. package/ERRORS.md +6 -6
  3. package/README.md +76 -12
  4. package/dist/adapters/adapter-field.d.ts +8 -0
  5. package/dist/adapters/adapter-identifier.d.ts +9 -0
  6. package/dist/adapters/adapter-identifier.js +6 -1
  7. package/dist/adapters/adapter-operation.d.ts +13 -0
  8. package/dist/compiler/contract.compiler.d.ts +6 -0
  9. package/dist/compiler/contract.compiler.js +99 -15
  10. package/dist/config/computed-fields.js +2 -9
  11. package/dist/config/config-diagnostic.guidance.js +3 -3
  12. package/dist/config/config.resolver.d.ts +14 -0
  13. package/dist/config/config.resolver.js +31 -0
  14. package/dist/config/config.validator.js +82 -0
  15. package/dist/config/exact-framework-config.d.ts +5 -0
  16. package/dist/config/framework-config.d.ts +30 -1
  17. package/dist/contracts/client-contract-structure.validator.d.ts +8 -0
  18. package/dist/contracts/client-contract-structure.validator.js +6 -1
  19. package/dist/contracts/contract.d.ts +15 -1
  20. package/dist/contracts/field.d.ts +28 -0
  21. package/dist/contracts/scalar-value-type.d.ts +3 -1
  22. package/dist/framework/contract-view.d.ts +41 -10
  23. package/dist/framework/framework-construction.error.d.ts +2 -1
  24. package/dist/framework/framework-construction.error.js +11 -1
  25. package/dist/framework/framework.d.ts +4 -4
  26. package/dist/framework/framework.factory.js +39 -11
  27. package/dist/framework/operation-facade.d.ts +29 -10
  28. package/dist/framework/operation-facade.js +2 -1
  29. package/dist/index.d.ts +10 -4
  30. package/dist/internal.d.ts +2 -2
  31. package/dist/internal.js +2 -0
  32. package/dist/operations/operation-arguments.d.ts +44 -28
  33. package/dist/operations/operation-call.d.ts +9 -5
  34. package/dist/operations/operation-result.d.ts +6 -4
  35. package/dist/operations/operation-validator-core.js +6 -0
  36. package/dist/policy/policy-admission.d.ts +22 -0
  37. package/dist/policy/policy-admission.js +409 -0
  38. package/dist/policy/policy-after-update.runtime.d.ts +29 -0
  39. package/dist/policy/policy-after-update.runtime.js +132 -0
  40. package/dist/policy/policy-condition.d.ts +15 -0
  41. package/dist/policy/policy-condition.js +36 -0
  42. package/dist/policy/policy-engine.d.ts +118 -0
  43. package/dist/policy/policy-engine.js +1 -0
  44. package/dist/policy/policy-nested-write.runtime.d.ts +38 -0
  45. package/dist/policy/policy-nested-write.runtime.js +453 -0
  46. package/dist/policy/policy-read.runtime.d.ts +81 -0
  47. package/dist/policy/policy-read.runtime.js +503 -0
  48. package/dist/policy/policy-request.d.ts +41 -0
  49. package/dist/policy/policy-request.js +63 -0
  50. package/dist/policy/policy-session.d.ts +93 -0
  51. package/dist/policy/policy-session.js +235 -0
  52. package/dist/policy/policy-startup.validator.d.ts +8 -0
  53. package/dist/policy/policy-startup.validator.js +104 -0
  54. package/dist/policy/policy-write.runtime.d.ts +37 -0
  55. package/dist/policy/policy-write.runtime.js +409 -0
  56. package/dist/prebuilt/core-version.generated.d.ts +2 -0
  57. package/dist/prebuilt/core-version.generated.js +1 -0
  58. package/dist/prebuilt/prebuilt-changes.d.ts +28 -0
  59. package/dist/prebuilt/prebuilt-changes.js +65 -0
  60. package/dist/prebuilt/prebuilt-contracts.d.ts +40 -0
  61. package/dist/prebuilt/prebuilt-contracts.emitter.d.ts +16 -0
  62. package/dist/prebuilt/prebuilt-contracts.emitter.js +50 -0
  63. package/dist/prebuilt/prebuilt-contracts.js +1 -0
  64. package/dist/prebuilt/prebuilt-contracts.loader.d.ts +21 -0
  65. package/dist/prebuilt/prebuilt-contracts.loader.js +77 -0
  66. package/dist/prebuilt/prebuilt-inputs.fingerprint.d.ts +16 -0
  67. package/dist/prebuilt/prebuilt-inputs.fingerprint.js +192 -0
  68. package/dist/protocol/absent-route.responder.d.ts +4 -4
  69. package/dist/protocol/http-request.decoder.d.ts +2 -2
  70. package/dist/protocol/http-request.reader.d.ts +2 -2
  71. package/dist/protocol/protocol-headers.d.ts +3 -3
  72. package/dist/runtime/computed-read-runtime.d.ts +6 -1
  73. package/dist/runtime/computed-read-runtime.js +226 -52
  74. package/dist/runtime/context-executor.runtime.d.ts +2 -2
  75. package/dist/runtime/context-executor.runtime.js +4 -2
  76. package/dist/runtime/create-requirement.js +1 -1
  77. package/dist/runtime/diagnostics-reporter.d.ts +5 -5
  78. package/dist/runtime/execution-options.d.ts +8 -0
  79. package/dist/runtime/framework-warning.emitter.d.ts +6 -0
  80. package/dist/runtime/framework-warning.emitter.js +8 -0
  81. package/dist/runtime/level-lifecycle.runtime.js +3 -2
  82. package/dist/runtime/operation-code.guidance.js +3 -3
  83. package/dist/runtime/operation-request.d.ts +16 -9
  84. package/dist/runtime/operation-runtime.d.ts +15 -7
  85. package/dist/runtime/operation-runtime.js +139 -14
  86. package/dist/runtime/owned-execution.registry.d.ts +2 -2
  87. package/dist/runtime/readonly-value.d.ts +5 -1
  88. package/dist/runtime/readonly-value.js +67 -28
  89. package/dist/runtime/referential-action.d.ts +1 -1
  90. package/dist/transactions/sql-statement.d.ts +2 -2
  91. package/dist/transactions/transaction-plan-assembler.d.ts +2 -2
  92. package/dist/transactions/transaction-plan-validator.d.ts +5 -5
  93. package/dist/transactions/transaction-request.d.ts +8 -8
  94. package/dist/transactions/transaction-runtime.d.ts +5 -2
  95. package/dist/transactions/transaction-runtime.js +33 -6
  96. package/package.json +2 -2
@@ -1,3 +1,4 @@
1
+ import { adapterPrimaryIdentifier } from "../adapters/adapter-identifier.js";
1
2
  import { lookupField, lookupResource } from "../contracts/contract-lookup.js";
2
3
  import { AvComputedValueError } from "./computed-value.error.js";
3
4
  import { frozenCopy } from "./readonly-value.js";
@@ -37,12 +38,6 @@ function appendUniqueProjection(values, value) {
37
38
  values.push(value);
38
39
  }
39
40
  }
40
- function adapterIdentifier(model, resource) {
41
- const resources = model.resources;
42
- return ((Object.hasOwn(resources, resource)
43
- ? resources[resource]?.identifiers?.[0]
44
- : undefined) ?? []);
45
- }
46
41
  function supportsProjection(request) {
47
42
  if (request.family === "upsert") {
48
43
  return true;
@@ -59,7 +54,7 @@ function addScalarProjection(core, model, active, compute, resourceName, fieldNa
59
54
  if (stage !== undefined) {
60
55
  appendUnique(computed, fieldName);
61
56
  if (stage.dependsOn.length === 0) {
62
- for (const member of adapterIdentifier(model, resourceName)) {
57
+ for (const member of adapterPrimaryIdentifier(model, resourceName)) {
63
58
  appendUniqueProjection(adapterSelect, member);
64
59
  }
65
60
  }
@@ -89,18 +84,27 @@ function projectionEntry(entry) {
89
84
  function withoutProjectionChoice(value) {
90
85
  return Object.fromEntries(Object.entries(value).filter(([key]) => key !== "select" && key !== "include"));
91
86
  }
92
- function defaultProjectionLevel(core, model, active, compute, resourceName) {
87
+ export function defaultProjectionFields(active, resourceName) {
93
88
  const activeResource = lookupResource(active, resourceName);
89
+ if (activeResource === undefined) {
90
+ return [];
91
+ }
92
+ const fields = [];
93
+ for (const [fieldName, field] of Object.entries(activeResource.fields)) {
94
+ if (field.kind === "scalar" &&
95
+ field.capabilities.select === true &&
96
+ field.governed !== true) {
97
+ fields.push(fieldName);
98
+ }
99
+ }
100
+ return fields;
101
+ }
102
+ function defaultProjectionLevel(core, model, active, compute, resourceName) {
94
103
  const adapterSelect = [];
95
104
  const requested = [];
96
105
  const computed = [];
97
- if (activeResource !== undefined) {
98
- for (const [fieldName, field] of Object.entries(activeResource.fields)) {
99
- if (field.kind !== "scalar" || field.capabilities.select !== true) {
100
- continue;
101
- }
102
- addScalarProjection(core, model, active, compute, resourceName, fieldName, adapterSelect, requested, computed);
103
- }
106
+ for (const fieldName of defaultProjectionFields(active, resourceName)) {
107
+ addScalarProjection(core, model, active, compute, resourceName, fieldName, adapterSelect, requested, computed);
104
108
  }
105
109
  return {
106
110
  select: adapterSelect,
@@ -168,6 +172,12 @@ function planSelectLevel(core, model, active, compute, resourceName, select, col
168
172
  }
169
173
  for (const entry of select) {
170
174
  if (typeof entry === "string") {
175
+ if (entry === "$defaults") {
176
+ for (const fieldName of defaultProjectionFields(active, resourceName)) {
177
+ addScalarProjection(core, model, active, compute, resourceName, fieldName, adapterSelect, requested, computed);
178
+ }
179
+ continue;
180
+ }
171
181
  if (entry === "$count") {
172
182
  adapterSelect.push(entry);
173
183
  if (collectReducers) {
@@ -292,7 +302,175 @@ export function planReadOperation(core, model, active, compute, request, args) {
292
302
  materialize: defaults.plan.computed.length > 0,
293
303
  };
294
304
  }
295
- async function materializeRelation(active, compute, request, relation, value, contextFactory) {
305
+ function resolveReadLevel(active, compute, plan, tail) {
306
+ const resource = lookupResource(active, plan.resource);
307
+ if (resource === undefined) {
308
+ return undefined;
309
+ }
310
+ const stages = [];
311
+ for (const fieldName of plan.computed) {
312
+ const field = lookupField(resource, fieldName);
313
+ const stage = readCompute(compute?.[plan.resource]?.[fieldName]?.read);
314
+ if (field?.kind !== "scalar" || stage === undefined) {
315
+ continue;
316
+ }
317
+ stages.push({
318
+ field: fieldName,
319
+ definition: field,
320
+ compute: stage.compute,
321
+ });
322
+ }
323
+ const relations = [];
324
+ let restFree = true;
325
+ for (const relation of [...plan.relations].reverse()) {
326
+ const level = resolveReadLevel(active, compute, relation.projection, tail && restFree);
327
+ relations.unshift({ relation, level });
328
+ restFree &&= level?.computeFree ?? true;
329
+ }
330
+ return {
331
+ plan,
332
+ stages,
333
+ relations,
334
+ computeFree: stages.length === 0 && restFree,
335
+ atOnce: tail && stages.length <= 1 && restFree,
336
+ plainKeys: plan.requested.every((field) => !(field in Object.prototype)),
337
+ };
338
+ }
339
+ function isNestedContainer(value) {
340
+ return (value !== null &&
341
+ typeof value === "object" &&
342
+ !(value instanceof Date) &&
343
+ !(value instanceof Uint8Array));
344
+ }
345
+ function isThenable(value) {
346
+ return (value !== null &&
347
+ (typeof value === "object" || typeof value === "function") &&
348
+ typeof value.then === "function");
349
+ }
350
+ function setOwn(target, key, value) {
351
+ if (key in target) {
352
+ Object.defineProperty(target, key, {
353
+ value,
354
+ writable: true,
355
+ enumerable: true,
356
+ configurable: true,
357
+ });
358
+ return;
359
+ }
360
+ target[key] = value;
361
+ }
362
+ function answerOf(level, record) {
363
+ const answer = {};
364
+ for (const fieldName of level.plan.requested) {
365
+ if (!Object.hasOwn(record, fieldName)) {
366
+ continue;
367
+ }
368
+ if (level.plainKeys) {
369
+ answer[fieldName] = record[fieldName];
370
+ }
371
+ else {
372
+ setOwn(answer, fieldName, record[fieldName]);
373
+ }
374
+ }
375
+ return answer;
376
+ }
377
+ function heldReadValue(active, level, stage, value) {
378
+ const validation = normalizeScalarFieldValue(active, stage.definition, value, "runtime", [
379
+ "resources",
380
+ level.plan.resource,
381
+ "fields",
382
+ stage.field,
383
+ "pipelines",
384
+ "compute",
385
+ "read",
386
+ ]);
387
+ if (!validation.ok) {
388
+ throw new AvComputedValueError(level.plan.resource, stage.field, "read", validation.issues);
389
+ }
390
+ return validation.value;
391
+ }
392
+ function projectedAtOnce(level, value) {
393
+ if (!isRecord(value) || level === undefined) {
394
+ return value;
395
+ }
396
+ const record = { ...value };
397
+ relationsAtOnce(level, record);
398
+ return answerOf(level, record);
399
+ }
400
+ function relationsAtOnce(level, record) {
401
+ for (const { relation, level: nested } of level.relations) {
402
+ if (!Object.hasOwn(record, relation.field)) {
403
+ continue;
404
+ }
405
+ const value = record[relation.field];
406
+ if (value === null || value === undefined) {
407
+ continue;
408
+ }
409
+ if (relation.cardinality === "many") {
410
+ if (Array.isArray(value)) {
411
+ record[relation.field] = Array.from(value, (entry) => projectedAtOnce(nested, entry));
412
+ }
413
+ continue;
414
+ }
415
+ record[relation.field] = projectedAtOnce(nested, value);
416
+ }
417
+ }
418
+ function computedAtOnce(active, level, stage, value, contextFactory) {
419
+ const record = { ...value };
420
+ const view = frozenCopy(record);
421
+ let computedValue;
422
+ try {
423
+ computedValue = stage.compute({
424
+ field: stage.field,
425
+ value: view[stage.field],
426
+ record: view,
427
+ ctx: contextFactory(),
428
+ });
429
+ }
430
+ catch (error) {
431
+ return Promise.reject(error);
432
+ }
433
+ const finish = (returned) => {
434
+ if (returned !== undefined) {
435
+ record[stage.field] = heldReadValue(active, level, stage, returned);
436
+ }
437
+ relationsAtOnce(level, record);
438
+ return answerOf(level, record);
439
+ };
440
+ const later = (error) => Promise.resolve().then(() => {
441
+ throw error;
442
+ });
443
+ let waits;
444
+ try {
445
+ waits = isThenable(computedValue);
446
+ }
447
+ catch (error) {
448
+ return later(error);
449
+ }
450
+ if (waits) {
451
+ const pending = computedValue;
452
+ return (async () => finish(await pending))();
453
+ }
454
+ try {
455
+ return finish(computedValue);
456
+ }
457
+ catch (error) {
458
+ return later(error);
459
+ }
460
+ }
461
+ function materializeRecord(active, level, value, contextFactory) {
462
+ if (level?.atOnce !== true) {
463
+ return materializeInTurn(active, level, value, contextFactory);
464
+ }
465
+ if (!isRecord(value)) {
466
+ return value;
467
+ }
468
+ const [stage] = level.stages;
469
+ return stage === undefined
470
+ ? projectedAtOnce(level, value)
471
+ : computedAtOnce(active, level, stage, value, contextFactory);
472
+ }
473
+ async function materializeRelation(active, level, relation, value, contextFactory) {
296
474
  if (value === null || value === undefined) {
297
475
  return value;
298
476
  }
@@ -300,66 +478,62 @@ async function materializeRelation(active, compute, request, relation, value, co
300
478
  if (!Array.isArray(value)) {
301
479
  return value;
302
480
  }
303
- return Promise.all(value.map((entry) => materializeRecord(active, compute, request, relation.projection, entry, contextFactory)));
481
+ return Promise.all(value.map((entry) => materializeRecord(active, level, entry, contextFactory)));
304
482
  }
305
- return materializeRecord(active, compute, request, relation.projection, value, contextFactory);
483
+ return materializeRecord(active, level, value, contextFactory);
306
484
  }
307
- async function materializeRecord(active, compute, request, plan, value, contextFactory) {
308
- if (!isRecord(value)) {
309
- return value;
310
- }
311
- const resource = lookupResource(active, plan.resource);
312
- if (resource === undefined) {
485
+ async function materializeInTurn(active, level, value, contextFactory) {
486
+ if (!isRecord(value) || level === undefined) {
313
487
  return value;
314
488
  }
489
+ const { stages } = level;
315
490
  const record = { ...value };
316
- for (const fieldName of plan.computed) {
317
- const field = lookupField(resource, fieldName);
318
- const stage = readCompute(compute?.[plan.resource]?.[fieldName]?.read);
319
- if (field?.kind !== "scalar" || stage === undefined) {
320
- continue;
491
+ let told;
492
+ if (stages.length > 1) {
493
+ told = { ...record };
494
+ for (const key of Object.keys(told)) {
495
+ const nested = told[key];
496
+ if (isNestedContainer(nested)) {
497
+ told[key] = frozenCopy(nested, true);
498
+ }
321
499
  }
322
- const view = frozenCopy(record);
500
+ }
501
+ for (const stage of stages) {
502
+ const view = frozenCopy(told ?? record);
323
503
  const computedValue = await stage.compute({
324
- field: fieldName,
325
- value: view[fieldName],
504
+ field: stage.field,
505
+ value: view[stage.field],
326
506
  record: view,
327
507
  ctx: contextFactory(),
328
508
  });
329
509
  if (computedValue === undefined) {
330
510
  continue;
331
511
  }
332
- const validation = normalizeScalarFieldValue(active, field, computedValue, "runtime", [
333
- "resources",
334
- plan.resource,
335
- "fields",
336
- fieldName,
337
- "pipelines",
338
- "compute",
339
- "read",
340
- ]);
341
- if (!validation.ok) {
342
- throw new AvComputedValueError(plan.resource, fieldName, "read", validation.issues);
512
+ const held = heldReadValue(active, level, stage, computedValue);
513
+ record[stage.field] = held;
514
+ if (told !== undefined) {
515
+ told[stage.field] = held;
343
516
  }
344
- record[fieldName] = validation.value;
345
517
  }
346
- for (const relation of plan.relations) {
518
+ for (const { relation, level: nested } of level.relations) {
347
519
  if (!Object.hasOwn(record, relation.field)) {
348
520
  continue;
349
521
  }
350
- record[relation.field] = await materializeRelation(active, compute, request, relation, record[relation.field], contextFactory);
522
+ record[relation.field] = await materializeRelation(active, nested, relation, record[relation.field], contextFactory);
351
523
  }
352
- return Object.fromEntries(plan.requested
353
- .filter((fieldName) => Object.hasOwn(record, fieldName))
354
- .map((fieldName) => [fieldName, record[fieldName]]));
524
+ return answerOf(level, record);
355
525
  }
356
- export async function materializeReadOperation(active, compute, request, plan, value, contextFactory) {
526
+ export async function materializeReadOperation(active, compute, _request, plan, value, contextFactory) {
357
527
  const projection = plan.projection;
358
528
  if (!plan.materialize || projection === undefined) {
359
529
  return value;
360
530
  }
531
+ const level = resolveReadLevel(active, compute, projection, true);
361
532
  if (Array.isArray(value)) {
362
- return Promise.all(value.map((entry) => materializeRecord(active, compute, request, projection, entry, contextFactory)));
533
+ const rows = value.map((entry) => materializeRecord(active, level, entry, contextFactory));
534
+ return rows.some((row) => row instanceof Promise)
535
+ ? Promise.all(rows)
536
+ : Array.from(rows);
363
537
  }
364
- return materializeRecord(active, compute, request, projection, value, contextFactory);
538
+ return materializeRecord(active, level, value, contextFactory);
365
539
  }
@@ -7,7 +7,7 @@ import type { AvOperationFamily } from "../operations/operation-identity.js";
7
7
  import { type AvDiagnosticsSink } from "./diagnostics-reporter.js";
8
8
  import type { AvComputeContext } from "./field-compute.js";
9
9
  import type { MisuseReporter } from "./misuse-reporter.js";
10
- import type { ApplicationOperationRequest } from "./operation-request.js";
10
+ import type { AvApplicationOperationRequest } from "./operation-request.js";
11
11
  import type { AvOperationResponse } from "./operation-response.js";
12
12
  import type { AvContext, AvTransactionContext, AvTransportContext } from "./pipeline.js";
13
13
  import type { AvState } from "./request-state.js";
@@ -35,7 +35,7 @@ export type ContextFacts = {
35
35
  readonly $args: unknown;
36
36
  };
37
37
  /** Runs one raw application-origin call in `transaction`, or standalone. Total. */
38
- export type RawOperationRunner<M extends AvAdapterModel> = (request: ApplicationOperationRequest, transaction: AvAdapterTransactionExecutor<M> | undefined) => Promise<AvOperationResponse>;
38
+ export type RawOperationRunner<M extends AvAdapterModel> = (request: AvApplicationOperationRequest, transaction: AvAdapterTransactionExecutor<M> | undefined) => Promise<AvOperationResponse>;
39
39
  export type ContextExecutorDependencies<M extends AvAdapterModel> = {
40
40
  /** The model, as construction read it once: the ctx's Resources. */
41
41
  readonly model: M;
@@ -195,9 +195,10 @@ export function createContextExecutor(dependencies) {
195
195
  const contextOf = (prototype, scope, facts, readOnly = false) => {
196
196
  const context = Object.create(prototype);
197
197
  Object.defineProperty(context, STATE, {
198
- value: { scope, facts, resources: new Map() },
198
+ value: { scope, facts, resources: undefined },
199
199
  });
200
- for (const [key, value] of Object.entries(facts)) {
200
+ for (const key of Object.keys(facts)) {
201
+ const value = facts[key];
201
202
  if (value !== undefined) {
202
203
  Object.defineProperty(context, key, { value, enumerable: true });
203
204
  }
@@ -232,6 +233,7 @@ export function createContextExecutor(dependencies) {
232
233
  Object.defineProperty(prototype, resource, {
233
234
  get() {
234
235
  const state = stateOf(this);
236
+ state.resources ??= new Map();
235
237
  let tree = state.resources.get(resource);
236
238
  if (tree === undefined) {
237
239
  tree = resourceTree(state.scope, resource, readOnly);
@@ -13,7 +13,7 @@ function edgesOf(table, resource) {
13
13
  return Array.isArray(listed) ? listed : [];
14
14
  }
15
15
  function referencesOf(edge) {
16
- return Array.isArray(edge.references) ? edge.references : [];
16
+ return Array.isArray(edge.referenceFields) ? edge.referenceFields : [];
17
17
  }
18
18
  function creatableScalar(contract, resource, field) {
19
19
  const found = lookupField(lookupResource(contract, resource), field);
@@ -1,7 +1,7 @@
1
1
  import type { AvRequestOrigin } from "../config/config-layer.js";
2
2
  import type { AvOperationFamily } from "../operations/operation-identity.js";
3
- import type { FrameworkTransactionRequest } from "../transactions/transaction-request.js";
4
- import type { FrameworkOperationRequest } from "./operation-request.js";
3
+ import type { AvTransactionRequest } from "../transactions/transaction-request.js";
4
+ import type { AvOperationRequest } from "./operation-request.js";
5
5
  import { type AvOperationErrorCode, type AvOperationFailureResponse } from "./operation-response.js";
6
6
  /**
7
7
  * One unplanned failure of an operation: the raw error, the code the caller
@@ -100,14 +100,14 @@ export declare function diagnosticsSinkOf(framework: object): AvDiagnosticsSink
100
100
  * Binds the configured sink to one operation request. The sink is not awaited,
101
101
  * and nothing it throws or rejects with reaches the response.
102
102
  */
103
- export declare function createDiagnosticsReporter(sink: AvDiagnosticsSink | undefined, request: FrameworkOperationRequest, operation?: number): DiagnosticsReporter;
103
+ export declare function createDiagnosticsReporter(sink: AvDiagnosticsSink | undefined, request: AvOperationRequest, operation?: number): DiagnosticsReporter;
104
104
  /** The diagnostics channel of one transaction request. */
105
105
  export type TransactionDiagnosticsChannel = {
106
106
  /** A plan-level report; `operation` attributes it to a recorded failing index. */
107
107
  readonly transaction: (operation?: number) => DiagnosticsReporter;
108
108
  /** A report carrying one plan operation's own identity and index. */
109
- readonly operation: (request: FrameworkOperationRequest, operation: number) => DiagnosticsReporter;
109
+ readonly operation: (request: AvOperationRequest, operation: number) => DiagnosticsReporter;
110
110
  };
111
111
  /** Binds the configured sink to one transaction request and its plan operations. */
112
- export declare function createTransactionDiagnosticsChannel(sink: AvDiagnosticsSink | undefined, request: FrameworkTransactionRequest): TransactionDiagnosticsChannel;
112
+ export declare function createTransactionDiagnosticsChannel(sink: AvDiagnosticsSink | undefined, request: AvTransactionRequest): TransactionDiagnosticsChannel;
113
113
  export {};
@@ -18,4 +18,12 @@ export type AvExecutionOptions = {
18
18
  * object), the Framework starts the request with `{}`.
19
19
  */
20
20
  readonly state?: AvState & object;
21
+ /**
22
+ * The principal the call or run is checked as by the configuration's
23
+ * `policy`. An application call names one to opt in: left out, it is server
24
+ * code and the policy does not check it. A `framework.client.*` call is
25
+ * checked either way, and `as` replaces the principal `policy.principal`
26
+ * would derive from its state. `null` is the anonymous principal.
27
+ */
28
+ readonly as?: unknown;
21
29
  };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Emits a framework warning: through `process.emitWarning` (type
3
+ * `AventaraWarning`, `code` as given) where the runtime has it, else through
4
+ * `console.warn`. A warning changes no behaviour.
5
+ */
6
+ export declare function emitFrameworkWarning(message: string, code: string): void;
@@ -0,0 +1,8 @@
1
+ export function emitFrameworkWarning(message, code) {
2
+ const emitWarning = globalThis.process?.emitWarning;
3
+ if (typeof emitWarning === "function") {
4
+ emitWarning(message, { type: "AventaraWarning", code });
5
+ return;
6
+ }
7
+ globalThis.console?.warn(`AventaraWarning: ${message}`);
8
+ }
@@ -20,8 +20,9 @@ export async function runLevelLifecycle(levels, context, inner, settle, reuse, r
20
20
  }
21
21
  const result = await settle(await inner());
22
22
  const after = [...levels].reverse().flatMap((level) => level.hooks.after);
23
- for (const hook of after) {
24
- await hook(frozenCopy(result), context());
23
+ const settled = after.length > 0 ? frozenCopy(result, true) : undefined;
24
+ for (const [index, hook] of after.entries()) {
25
+ await hook(index === after.length - 1 ? settled : frozenCopy(settled), context());
25
26
  }
26
27
  return result;
27
28
  };
@@ -66,7 +66,7 @@ export const OPERATION_CODE_GUIDANCE = {
66
66
  },
67
67
  A2003: {
68
68
  message: "Not found.",
69
- cause: "A unique operation (find.unique, update.unique, delete.unique) matched no row, or a $connect names a record that does not exist. The operation fails, so no after hook runs.",
69
+ cause: "A unique operation (find.unique, update.unique, delete.unique) matched no row, or a $connect names a record that does not exist. The operation fails, so no after hook runs. Under a policy, a row outside the caller's read scope is a miss too, and so is a single-row update or delete of a row the caller cannot read, so the answer does not reveal that the row exists. A row the caller can read but not change answers A4002.",
70
70
  fix: "Catch AvNotFoundError, or use a first-style operation, which answers A1001 with null. Check the $connect identifier.",
71
71
  },
72
72
  A2004: {
@@ -161,7 +161,7 @@ export const OPERATION_CODE_GUIDANCE = {
161
161
  },
162
162
  A4002: {
163
163
  message: "Policy denied.",
164
- cause: 'A guard or hook threw new AvError("A4002", ...) with a policy reason of your own.',
165
- fix: "Read cause.message for the policy's reason.",
164
+ cause: "The configuration's policy refused the request: its engine does not allow the operation, a field the request names, writes or returns, a row it reaches, a row it creates, or the row a change leaves (moved out of what the caller reads, or failing its grant's after condition, which rolls the write back); on a database without interactive transactions, a write the policy can only check after it runs; or a stage threw new AvError(\"A4002\", ...).",
165
+ fix: "Read cause.message, which names what the policy refused. Call as a principal the policy allows, leave the refused field or relation out (a governed one is named only where every row grants it), or change only rows the caller may change and still read afterwards.",
166
166
  },
167
167
  };
@@ -1,11 +1,12 @@
1
1
  import type { AvOperationIdentity } from "../operations/operation-identity.js";
2
+ import type { PolicySession } from "../policy/policy-session.js";
2
3
  import type { AvTransportContext } from "./pipeline.js";
3
4
  import type { AvState } from "./request-state.js";
4
5
  /**
5
6
  * The protocol version and Contract hash a remote caller says it was built for:
6
7
  * the Client Contract's for a client, the Agent Contract's for an agent.
7
8
  */
8
- export type ClientProtocolIdentity = {
9
+ export type AvRequestProtocol = {
9
10
  readonly version: number;
10
11
  readonly hash: string;
11
12
  };
@@ -20,7 +21,7 @@ type RuntimeOperationBase = AvOperationIdentity & {
20
21
  readonly state?: AvState;
21
22
  };
22
23
  /** An in-process request, run under the Application Contract. */
23
- export type ApplicationOperationRequest = RuntimeOperationBase & {
24
+ export type AvApplicationOperationRequest = RuntimeOperationBase & {
24
25
  readonly origin: "application";
25
26
  readonly protocol?: never;
26
27
  readonly transport?: never;
@@ -29,24 +30,24 @@ export type ApplicationOperationRequest = RuntimeOperationBase & {
29
30
  * A remote request, run under the Client Contract; `protocol` is the identity
30
31
  * the client sent.
31
32
  */
32
- export type ClientOperationRequest = RuntimeOperationBase & {
33
+ export type AvClientOperationRequest = RuntimeOperationBase & {
33
34
  readonly origin: "client";
34
- readonly protocol: ClientProtocolIdentity;
35
+ readonly protocol: AvRequestProtocol;
35
36
  readonly transport?: AvTransportContext;
36
37
  };
37
38
  /**
38
39
  * An AI agent's request, run under the Agent Contract; `protocol` is the
39
40
  * identity of the Agent Contract the caller (an MCP server) was built from.
40
41
  */
41
- export type AgentOperationRequest = RuntimeOperationBase & {
42
+ export type AvAgentOperationRequest = RuntimeOperationBase & {
42
43
  readonly origin: "agent";
43
- readonly protocol: ClientProtocolIdentity;
44
+ readonly protocol: AvRequestProtocol;
44
45
  readonly transport?: AvTransportContext;
45
46
  };
46
47
  /** A request from outside the server: a client's or an agent's. */
47
- export type RemoteOperationRequest = ClientOperationRequest | AgentOperationRequest;
48
+ export type RemoteOperationRequest = AvClientOperationRequest | AvAgentOperationRequest;
48
49
  /** One operation request, as `framework.execute` takes it. */
49
- export type FrameworkOperationRequest = ApplicationOperationRequest | ClientOperationRequest | AgentOperationRequest;
50
+ export type AvOperationRequest = AvApplicationOperationRequest | AvClientOperationRequest | AvAgentOperationRequest;
50
51
  type WithoutArguments<Request> = Request extends unknown ? Omit<Request, "arguments"> : never;
51
52
  /**
52
53
  * A request as every stage after the Framework Validation Layer sees it:
@@ -56,12 +57,18 @@ type WithoutArguments<Request> = Request extends unknown ? Omit<Request, "argume
56
57
  * unreachable by type. Distributive, so the origin and family/variant
57
58
  * correlations survive the omission.
58
59
  */
59
- export type OperationExecutionRequest = WithoutArguments<FrameworkOperationRequest>;
60
+ export type OperationExecutionRequest = WithoutArguments<AvOperationRequest>;
60
61
  /**
61
62
  * A request whose stages run: its `state` settled once, the seed or the `{}`
62
63
  * the runtime made for it, so every stage is told the same object.
63
64
  */
64
65
  export type StatefulOperationRequest = OperationExecutionRequest & {
65
66
  readonly state: AvState;
67
+ /**
68
+ * The request's policy session, when the policy checks it: a client's or an
69
+ * agent's request, or an application call made `as` a principal. Absent, nothing
70
+ * is checked: an application call, or a call a stage made through `ctx`.
71
+ */
72
+ readonly policy?: PolicySession;
66
73
  };
67
74
  export {};
@@ -7,14 +7,15 @@ import type { ResolvedFrameworkConfig } from "../config/config.resolver.js";
7
7
  import type { AvRequestOrigin, RemoteRequestOrigin } from "../config/config-layer.js";
8
8
  import type { AvContract } from "../contracts/contract.js";
9
9
  import type { OperationValidationOptions, SemanticOperationArguments } from "../operations/operation-validator.js";
10
- import type { FrameworkTransactionRequest } from "../transactions/transaction-request.js";
10
+ import { type PolicyCaller } from "../policy/policy-request.js";
11
+ import type { AvTransactionRequest } from "../transactions/transaction-request.js";
11
12
  import { type ArgumentsSource } from "./caller-data.door.js";
12
13
  import { type ContextExecutor, type ExecutorScope } from "./context-executor.runtime.js";
13
14
  import { type RecordWriteFacts } from "./create-requirement.js";
14
15
  import { type DiagnosticsReporter } from "./diagnostics-reporter.js";
15
16
  import { type OuterFailureLayer } from "./error-boundary.js";
16
17
  import { type FieldStagePlan } from "./field-stage.plan.js";
17
- import type { ClientProtocolIdentity, FrameworkOperationRequest, StatefulOperationRequest } from "./operation-request.js";
18
+ import type { AvOperationRequest, AvRequestProtocol, StatefulOperationRequest } from "./operation-request.js";
18
19
  import type { AvOperationFailureResponse, AvOperationResponse } from "./operation-response.js";
19
20
  import type { AvState } from "./request-state.js";
20
21
  import { type NormalizationWrite } from "./scalar-runtime.js";
@@ -65,6 +66,11 @@ export declare function withRequestState<Request extends {
65
66
  }>(request: Request): Request & {
66
67
  readonly state: AvState;
67
68
  };
69
+ /**
70
+ * `request` with its policy session when the policy checks it, or the failure
71
+ * deriving its principal answered.
72
+ */
73
+ export declare function withRequestPolicy<M extends AvAdapterModel>(dependencies: Pick<OperationRuntimeDependencies<M>, "adapter" | "config" | "contracts">, request: StatefulOperationRequest, caller: PolicyCaller | undefined, report: DiagnosticsReporter): StatefulOperationRequest | AvOperationFailureResponse | Promise<StatefulOperationRequest | AvOperationFailureResponse>;
68
74
  /**
69
75
  * Builds what every operation's pipeline shares, the ctx executor included: its
70
76
  * raw calls run the application's operation path with every developer stage
@@ -79,13 +85,15 @@ export declare function createPipelineDependencies<M extends AvAdapterModel>(dep
79
85
  * request's arguments are the caller's own objects, copied at the door, or data
80
86
  * no caller holds (see {@link ArgumentsSource}).
81
87
  */
82
- export type FrameworkOperationExecutor = (request: FrameworkOperationRequest, report: DiagnosticsReporter, source: ArgumentsSource) => Promise<AvOperationResponse>;
88
+ export type FrameworkOperationExecutor = (request: AvOperationRequest, report: DiagnosticsReporter, source: ArgumentsSource,
89
+ /** The principal an application call runs as (`{ as }`), when it names one. */
90
+ caller?: PolicyCaller) => Promise<AvOperationResponse>;
83
91
  /** Selects the Contract of the request's origin. */
84
- export declare function activeContract(contracts: AvContracts, request: FrameworkOperationRequest | FrameworkTransactionRequest): AvContract;
92
+ export declare function activeContract(contracts: AvContracts, request: AvOperationRequest | AvTransactionRequest): AvContract;
85
93
  /** Creates the transport-independent operation executor owned by core. */
86
94
  export declare function createOperationRuntime<M extends AvAdapterModel>(dependencies: OperationRuntimeDependencies<M>, pipelineDependencies: OperationPipelineDependencies<M>): FrameworkOperationExecutor;
87
95
  /** Validates the request-level protocol identity. */
88
- export declare function validateProtocolIdentity(contracts: AvContracts, request: FrameworkOperationRequest | FrameworkTransactionRequest): AvOperationFailureResponse | undefined;
96
+ export declare function validateProtocolIdentity(contracts: AvContracts, request: AvOperationRequest | AvTransactionRequest): AvOperationFailureResponse | undefined;
89
97
  /**
90
98
  * The stale-client check on one remote protocol identity: the protocol version first
91
99
  * (`A2006`), then the hash of the origin's Contract (`A2005`): the ClientContract's
@@ -94,7 +102,7 @@ export declare function validateProtocolIdentity(contracts: AvContracts, request
94
102
  * (`@aventara/core/protocol`) call it too, so a stale client hears the same envelope
95
103
  * whichever layer answers.
96
104
  */
97
- export declare function protocolIdentityFailure(contracts: AvContracts, protocol: ClientProtocolIdentity, origin: RemoteRequestOrigin): AvOperationFailureResponse | undefined;
105
+ export declare function protocolIdentityFailure(contracts: AvContracts, protocol: AvRequestProtocol, origin: RemoteRequestOrigin): AvOperationFailureResponse | undefined;
98
106
  export type ValidatedOperationArguments = {
99
107
  readonly ok: true;
100
108
  readonly arguments: SemanticOperationArguments;
@@ -103,7 +111,7 @@ export type ValidatedOperationArguments = {
103
111
  readonly response: AvOperationFailureResponse;
104
112
  };
105
113
  /** Validates one operation's arguments through the Framework Validation Layer. */
106
- export declare function validateOperationRequest(contract: AvContract, request: FrameworkOperationRequest, options?: OperationValidationOptions, write?: NormalizationWrite): ValidatedOperationArguments;
114
+ export declare function validateOperationRequest(contract: AvContract, request: AvOperationRequest, options?: OperationValidationOptions, write?: NormalizationWrite): ValidatedOperationArguments;
107
115
  /** The subject a refused output of an operation's pipes is reported under at a plan step. */
108
116
  export declare const STEP_PIPES = "Pipes";
109
117
  /**