openxiangda 2.0.0-alpha.26 → 2.0.0-alpha.27

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/dist/nest.d.ts CHANGED
@@ -4,6 +4,6 @@
4
4
  * Applications receive only current-user-union, Perspective read projection,
5
5
  * application-principal, Data API, runtime, event and bounded operation APIs.
6
6
  */
7
- export { bootstrapOpenXiangdaApplication, OpenXiangdaModule, OpenXiangdaAuthzGuard, RequireCapability, OpenXiangdaOperation, CurrentPrincipal, CurrentUser, CurrentPerspective, currentUserFromVerifiedContext, currentUserFromPrincipal, CurrentApplicationPrincipal, OpenXiangdaDataApiService, OpenXiangdaBusinessDataApiService, OpenXiangdaApplicationDataApiService, OpenXiangdaApplicationCredentials, OpenXiangdaRuntimeLeaseService, OpenXiangdaRuntimeSecrets, OpenXiangdaStandardOperations, OpenXiangdaEventContext, OpenXiangdaEventHandler, OpenXiangdaEventRegistry, OpenXiangdaEventRetryableError, OpenXiangdaEventDeterministicError, eventSigningSecretsFromEnvironment, assertValidTimeInterval, idempotentTransaction, } from 'openxiangda-nest';
8
- export type { OpenXiangdaApplicationBootstrapOptions, OpenXiangdaModuleOptions, OpenXiangdaOAuthClientOptions, OpenXiangdaFetch, OpenXiangdaCurrentUser, OpenXiangdaServicePrincipal, OpenXiangdaConnectedDevelopmentStatus, OpenXiangdaDomainEventEmitInput, OpenXiangdaDomainEventEmitResult, OpenXiangdaBusinessActionContext, OpenXiangdaRuntimeLeaseState, OpenXiangdaEventHandlerContext, OpenXiangdaEventHandlerDeclaration, OpenXiangdaEventConsumer, TimeInterval, MeetingReservationOperationInput, CourseSelectionOperationInput, VisitorReservationOperationInput, } from 'openxiangda-nest';
7
+ export { bootstrapOpenXiangdaApplication, OpenXiangdaModule, OpenXiangdaAuthzGuard, RequireCapability, OpenXiangdaOperation, CurrentPrincipal, CurrentUser, CurrentPerspective, currentUserFromVerifiedContext, currentUserFromPrincipal, CurrentApplicationPrincipal, OpenXiangdaDataApiService, OpenXiangdaBusinessDataApiService, OpenXiangdaBusinessNotificationService, OpenXiangdaNotificationService, OpenXiangdaApplicationDataApiService, OpenXiangdaApplicationCredentials, OpenXiangdaRuntimeLeaseService, OpenXiangdaRuntimeSecrets, OpenXiangdaStandardOperations, OpenXiangdaEventContext, OpenXiangdaEventHandler, OpenXiangdaEventRegistry, OpenXiangdaEventRetryableError, OpenXiangdaEventDeterministicError, eventSigningSecretsFromEnvironment, assertValidTimeInterval, idempotentTransaction, } from 'openxiangda-nest';
8
+ export type { OpenXiangdaApplicationBootstrapOptions, OpenXiangdaModuleOptions, OpenXiangdaOAuthClientOptions, OpenXiangdaFetch, OpenXiangdaCurrentUser, OpenXiangdaServicePrincipal, OpenXiangdaConnectedDevelopmentStatus, OpenXiangdaDomainEventEmitInput, OpenXiangdaDomainEventEmitResult, OpenXiangdaBusinessActionContext, OpenXiangdaBusinessNotificationInput, OpenXiangdaEventBusinessNotificationInput, OpenXiangdaRuntimeLeaseState, OpenXiangdaEventHandlerContext, OpenXiangdaEventHandlerDeclaration, OpenXiangdaEventConsumer, TimeInterval, MeetingReservationOperationInput, CourseSelectionOperationInput, VisitorReservationOperationInput, } from 'openxiangda-nest';
9
9
  //# sourceMappingURL=nest.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"nest.d.ts","sourceRoot":"","sources":["../src/nest.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,+BAA+B,EAC/B,iBAAiB,EACjB,qBAAqB,EACrB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,WAAW,EACX,kBAAkB,EAClB,8BAA8B,EAC9B,wBAAwB,EACxB,2BAA2B,EAC3B,yBAAyB,EACzB,iCAAiC,EACjC,oCAAoC,EACpC,iCAAiC,EACjC,8BAA8B,EAC9B,yBAAyB,EACzB,6BAA6B,EAC7B,uBAAuB,EACvB,uBAAuB,EACvB,wBAAwB,EACxB,8BAA8B,EAC9B,kCAAkC,EAClC,kCAAkC,EAClC,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EACV,sCAAsC,EACtC,wBAAwB,EACxB,6BAA6B,EAC7B,gBAAgB,EAChB,sBAAsB,EACtB,2BAA2B,EAC3B,qCAAqC,EACrC,+BAA+B,EAC/B,gCAAgC,EAChC,gCAAgC,EAChC,4BAA4B,EAC5B,8BAA8B,EAC9B,kCAAkC,EAClC,wBAAwB,EACxB,YAAY,EACZ,gCAAgC,EAChC,6BAA6B,EAC7B,gCAAgC,GACjC,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"nest.d.ts","sourceRoot":"","sources":["../src/nest.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,+BAA+B,EAC/B,iBAAiB,EACjB,qBAAqB,EACrB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,WAAW,EACX,kBAAkB,EAClB,8BAA8B,EAC9B,wBAAwB,EACxB,2BAA2B,EAC3B,yBAAyB,EACzB,iCAAiC,EACjC,sCAAsC,EACtC,8BAA8B,EAC9B,oCAAoC,EACpC,iCAAiC,EACjC,8BAA8B,EAC9B,yBAAyB,EACzB,6BAA6B,EAC7B,uBAAuB,EACvB,uBAAuB,EACvB,wBAAwB,EACxB,8BAA8B,EAC9B,kCAAkC,EAClC,kCAAkC,EAClC,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EACV,sCAAsC,EACtC,wBAAwB,EACxB,6BAA6B,EAC7B,gBAAgB,EAChB,sBAAsB,EACtB,2BAA2B,EAC3B,qCAAqC,EACrC,+BAA+B,EAC/B,gCAAgC,EAChC,gCAAgC,EAChC,oCAAoC,EACpC,yCAAyC,EACzC,4BAA4B,EAC5B,8BAA8B,EAC9B,kCAAkC,EAClC,wBAAwB,EACxB,YAAY,EACZ,gCAAgC,EAChC,6BAA6B,EAC7B,gCAAgC,GACjC,MAAM,kBAAkB,CAAC"}
package/dist/nest.js CHANGED
@@ -4,5 +4,5 @@
4
4
  * Applications receive only current-user-union, Perspective read projection,
5
5
  * application-principal, Data API, runtime, event and bounded operation APIs.
6
6
  */
7
- export { bootstrapOpenXiangdaApplication, OpenXiangdaModule, OpenXiangdaAuthzGuard, RequireCapability, OpenXiangdaOperation, CurrentPrincipal, CurrentUser, CurrentPerspective, currentUserFromVerifiedContext, currentUserFromPrincipal, CurrentApplicationPrincipal, OpenXiangdaDataApiService, OpenXiangdaBusinessDataApiService, OpenXiangdaApplicationDataApiService, OpenXiangdaApplicationCredentials, OpenXiangdaRuntimeLeaseService, OpenXiangdaRuntimeSecrets, OpenXiangdaStandardOperations, OpenXiangdaEventContext, OpenXiangdaEventHandler, OpenXiangdaEventRegistry, OpenXiangdaEventRetryableError, OpenXiangdaEventDeterministicError, eventSigningSecretsFromEnvironment, assertValidTimeInterval, idempotentTransaction, } from 'openxiangda-nest';
7
+ export { bootstrapOpenXiangdaApplication, OpenXiangdaModule, OpenXiangdaAuthzGuard, RequireCapability, OpenXiangdaOperation, CurrentPrincipal, CurrentUser, CurrentPerspective, currentUserFromVerifiedContext, currentUserFromPrincipal, CurrentApplicationPrincipal, OpenXiangdaDataApiService, OpenXiangdaBusinessDataApiService, OpenXiangdaBusinessNotificationService, OpenXiangdaNotificationService, OpenXiangdaApplicationDataApiService, OpenXiangdaApplicationCredentials, OpenXiangdaRuntimeLeaseService, OpenXiangdaRuntimeSecrets, OpenXiangdaStandardOperations, OpenXiangdaEventContext, OpenXiangdaEventHandler, OpenXiangdaEventRegistry, OpenXiangdaEventRetryableError, OpenXiangdaEventDeterministicError, eventSigningSecretsFromEnvironment, assertValidTimeInterval, idempotentTransaction, } from 'openxiangda-nest';
8
8
  //# sourceMappingURL=nest.js.map
package/dist/nest.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"nest.js","sourceRoot":"","sources":["../src/nest.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,+BAA+B,EAC/B,iBAAiB,EACjB,qBAAqB,EACrB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,WAAW,EACX,kBAAkB,EAClB,8BAA8B,EAC9B,wBAAwB,EACxB,2BAA2B,EAC3B,yBAAyB,EACzB,iCAAiC,EACjC,oCAAoC,EACpC,iCAAiC,EACjC,8BAA8B,EAC9B,yBAAyB,EACzB,6BAA6B,EAC7B,uBAAuB,EACvB,uBAAuB,EACvB,wBAAwB,EACxB,8BAA8B,EAC9B,kCAAkC,EAClC,kCAAkC,EAClC,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"nest.js","sourceRoot":"","sources":["../src/nest.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,+BAA+B,EAC/B,iBAAiB,EACjB,qBAAqB,EACrB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,WAAW,EACX,kBAAkB,EAClB,8BAA8B,EAC9B,wBAAwB,EACxB,2BAA2B,EAC3B,yBAAyB,EACzB,iCAAiC,EACjC,sCAAsC,EACtC,8BAA8B,EAC9B,oCAAoC,EACpC,iCAAiC,EACjC,8BAA8B,EAC9B,yBAAyB,EACzB,6BAA6B,EAC7B,uBAAuB,EACvB,uBAAuB,EACvB,wBAAwB,EACxB,8BAA8B,EAC9B,kCAAkC,EAClC,kCAAkC,EAClC,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda",
3
- "version": "2.0.0-alpha.26",
3
+ "version": "2.0.0-alpha.27",
4
4
  "description": "Unified OpenXiangda 2.0 CLI, SDK, React runtime, NestJS integration and AI skill distribution.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -49,12 +49,12 @@
49
49
  "dependencies": {
50
50
  "dayjs": "1.11.18",
51
51
  "docx-preview": "0.3.7",
52
- "openxiangda-cli": "2.0.0-alpha.113",
53
- "openxiangda-contracts": "2.0.0-alpha.54",
54
- "openxiangda-devkit-core": "2.0.0-alpha.71",
55
- "openxiangda-mcp": "2.0.0-alpha.71",
56
- "openxiangda-nest": "2.0.0-alpha.65",
57
- "openxiangda-skill-kit": "2.0.0-alpha.89",
52
+ "openxiangda-cli": "2.0.0-alpha.114",
53
+ "openxiangda-contracts": "2.0.0-alpha.55",
54
+ "openxiangda-devkit-core": "2.0.0-alpha.72",
55
+ "openxiangda-mcp": "2.0.0-alpha.72",
56
+ "openxiangda-nest": "2.0.0-alpha.66",
57
+ "openxiangda-skill-kit": "2.0.0-alpha.90",
58
58
  "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
59
59
  },
60
60
  "peerDependencies": {
@@ -4,7 +4,7 @@
4
4
  {
5
5
  "name": "openxiangda-v2",
6
6
  "description": "Use when researching, designing, building, testing, or delivering a complete OpenXiangda 2.0 application.",
7
- "sha256": "08bd64cb7f0dee4889fd34e9081b3b01dcafa714273c51bae57ee9e22ccfcf9e"
7
+ "sha256": "98417ad9a7d8f02cc88435d4642ba12f345dee9f9e455133efe3b124e40fdb75"
8
8
  }
9
9
  ]
10
10
  }
@@ -10,14 +10,14 @@ Treat requirement discovery, architecture, authorization, development, testing a
10
10
  Before a workspace exists, use this Skill's exact npm version:
11
11
 
12
12
  ```bash
13
- pnpm dlx openxiangda@2.0.0-alpha.26 login --base-url <platform>
14
- pnpm dlx openxiangda@2.0.0-alpha.26 create <directory>
13
+ pnpm dlx openxiangda@2.0.0-alpha.27 login --base-url <platform>
14
+ pnpm dlx openxiangda@2.0.0-alpha.27 create <directory>
15
15
  ```
16
16
 
17
17
  Inside an application, use only its locked executable through `pnpm openxiangda`. Never invoke a bare global `openxiangda`, because that executable may belong to stable 1.x. Moving tags such as `latest` and `alpha` are forbidden. To install or refresh this same Skill from the package, run:
18
18
 
19
19
  ```bash
20
- pnpm dlx openxiangda@2.0.0-alpha.26 skill install --force
20
+ pnpm dlx openxiangda@2.0.0-alpha.27 skill install --force
21
21
  ```
22
22
 
23
23
  Use this order:
@@ -7,13 +7,96 @@ Keep normal record CRUD in the platform Data API. Use `apps/server` only for typ
7
7
 
8
8
  Every interactive business action uses `@OpenXiangdaOperation(operation)`. The
9
9
  guard checks `operation.requiredCapability` once at App API ingress. Inside that
10
- declared action, inject `OpenXiangdaBusinessDataApiService` or use
11
- `OpenXiangdaStandardOperations`: Native Data then runs as the trusted backend
10
+ declared action, inject `OpenXiangdaBusinessDataApiService`,
11
+ `OpenXiangdaBusinessNotificationService`, or use
12
+ `OpenXiangdaStandardOperations`: Native Data and Notification Hub then run as the trusted backend
12
13
  for this exact application, environment, version and operation. Do not forward
13
14
  the caller's resource, row or field permissions into that internal Data call,
14
15
  and do not author an `authorizationJSON` permission mirror. The platform still
15
16
  records the initiating user and action code in records, files, events and audit.
16
17
 
18
+ For an informational business notification, inject
19
+ `OpenXiangdaBusinessNotificationService` in the same declared action. The SDK
20
+ uses the short-lived gateway invocation, never the caller's original login
21
+ token. The platform rechecks the operation and capability, promotes the call to
22
+ the exact application/environment principal, and records the initiating user
23
+ and action. Do not grant ordinary users `app:notification2:send` and do not use
24
+ the privileged `OpenXiangdaNotificationService` as an application workaround.
25
+
26
+ ```ts
27
+ import { Inject } from '@nestjs/common';
28
+ import {
29
+ OPENXIANGDA_NOTIFICATION_BUSINESS_SEND_V2,
30
+ OPENXIANGDA_NOTIFICATION_EVENT_SEND_V2,
31
+ } from 'openxiangda/contracts';
32
+ import {
33
+ OpenXiangdaBusinessNotificationService,
34
+ } from 'openxiangda/nest';
35
+
36
+ constructor(
37
+ @Inject(OpenXiangdaBusinessNotificationService)
38
+ private readonly notifications: OpenXiangdaBusinessNotificationService,
39
+ ) {}
40
+
41
+ await this.notifications.send({
42
+ schemaVersion: OPENXIANGDA_NOTIFICATION_BUSINESS_SEND_V2,
43
+ eventId: body.eventId,
44
+ correlationId: `proposal:${proposal.id}`,
45
+ messageKey: `proposal:${proposal.id}:invite:${member.id}`,
46
+ sourceSequence: proposal.revision,
47
+ recipients: [{ userId: member.userId }],
48
+ title: '待您附议',
49
+ summary: proposal.title,
50
+ navigationTarget: {
51
+ kind: 'APP_ROUTE',
52
+ appCode: 'proposal_app',
53
+ routeCode: 'proposal.detail',
54
+ pathParams: { id: proposal.id },
55
+ access: 'AUTHENTICATED',
56
+ },
57
+ idempotencyKey: `proposal:${proposal.id}:invite:${member.id}:v${proposal.revision}`,
58
+ });
59
+
60
+ ```
61
+
62
+ The fixed template accepts only `title` and optional `summary`. Reuse the same
63
+ `eventId` or `idempotencyKey` for retries. Reuse `messageKey` with increasing
64
+ `sourceSequence` for state convergence; do not generate a new logical message
65
+ for each retry.
66
+
67
+ The generated
68
+ `notificationTemplateCodes.applicationInformational` constant documents the
69
+ selected platform template; `send()` chooses it automatically and does not
70
+ accept a caller-supplied template code.
71
+
72
+ Inside a signed Application Events/date-trigger handler, call
73
+ `sendFromEvent()` on the same service. The SDK obtains the application
74
+ credential and forwards the current eventId, deliveryId and subscription code.
75
+ Declare only dot paths beneath the immutable event `data`; Notification Hub
76
+ resolves the actual Native user IDs and presentation text. Do not copy values
77
+ from the event into a caller-supplied `recipients` array and never manufacture a
78
+ user token.
79
+
80
+ ```ts
81
+ await this.notifications.sendFromEvent({
82
+ schemaVersion: OPENXIANGDA_NOTIFICATION_EVENT_SEND_V2,
83
+ correlationId: `proposal:${event.data.proposalId}`,
84
+ messageKey: `proposal:${event.data.proposalId}:reminder`,
85
+ sourceSequence: event.data.revision,
86
+ recipientPaths: ['assigneeUserIds'],
87
+ titlePath: 'notification.title',
88
+ summaryPath: 'notification.summary',
89
+ navigationTarget: {
90
+ kind: 'APP_ROUTE',
91
+ appCode: 'proposal_app',
92
+ routeCode: 'proposal.detail',
93
+ pathParams: { id: event.data.proposalId },
94
+ access: 'AUTHENTICATED',
95
+ },
96
+ idempotencyKey: event.id,
97
+ });
98
+ ```
99
+
17
100
  `OpenXiangdaDataApiService` deliberately retains ordinary current-user Data
18
101
  authorization and automatically inherits the request Perspective for reads.
19
102
  Its writes still authorize against the complete role union. Do not use it to
@@ -11,6 +11,7 @@ Use these optional modules only when the application has a real standard approva
11
11
  - Workflow Kernel v2 owns definitions, instances, tasks, participants, commands, transitions and audit facts.
12
12
  - Application Events v2 owns durable facts and delivery.
13
13
  - Notification Hub v2 owns templates, rules, logical messages, channel delivery, callbacks and message operations.
14
+ - Named Action business notifications use `OpenXiangdaBusinessNotificationService` and the platform-owned `application.informational.standard` template. App Gateway authorizes the current user once; Notification Hub revalidates the action proof, sends as the application principal, and retains initiating user/action audit. Ordinary users never need `app:notification2:send`.
14
15
  - Native Identity/AuthZ v2 owns the current user and the union of that user's current application roles. Perspective is read-only and never changes workflow participation or action authorization.
15
16
  - Never import, query, migrate or call 1.x workflow/message code, tables, APIs, queues, templates or callbacks.
16
17
 
@@ -47,6 +48,22 @@ A node may reference a notification profile that selects:
47
48
 
48
49
  Template variables must be declared by JSON Schema and stay within the field policy visible to the recipient. The Notification Hub does not query arbitrary application tables to complete a template.
49
50
 
51
+ For a non-Workflow informational message, the public business contract accepts
52
+ only `title`, optional `summary`, recipients and a canonical navigation target.
53
+ Supply stable `eventId`, `idempotencyKey`, `messageKey` and monotonic
54
+ `sourceSequence`: duplicate events and idempotency keys replay the stored
55
+ message, while a later source sequence converges the same logical message.
56
+ Never forward a user token, application credential or global Notification Hub
57
+ permission from application code.
58
+
59
+ A signed event/date-trigger consumer uses
60
+ `OpenXiangdaBusinessNotificationService.sendFromEvent()`. The application
61
+ declares safe paths under event `data` for recipients, title and summary;
62
+ Notification Hub verifies the active durable delivery and resolves those paths
63
+ itself. The audit actor remains the application event consumer with eventId,
64
+ deliveryId and subscription code as initiating source. Missing or forged event
65
+ proof, arbitrary recipient values and user impersonation are rejected.
66
+
50
67
  ## Detail navigation
51
68
 
52
69
  Use a canonical navigation target with `PLATFORM_ROUTE`, `APP_ROUTE` or allowlisted `EXTERNAL_URL`, stable route code, path/query parameters and an authenticated access requirement. Do not place environment domains, bearer tokens, cookies, identity-switching credentials or secrets in template strings.
@@ -8,7 +8,7 @@
8
8
  - Never write resource-level `schemaVersion`, `appCode`, `schema`, `surface`, `capabilities`, `fieldPolicies` or `platform/data` modules. The compiler derives the strict DataResource, CRUD capabilities, Surface and AI Schema. The application manifest still starts with its one top-level `schemaVersion: 3`.
9
9
  - Ordinary list/get/create/update/delete, filters, export and batch operations use the platform Native Data API. Do not create Function CRUD or NestJS wrappers.
10
10
  - Add NestJS only for a named business action that needs a cross-resource transaction, an invariant or an external side effect.
11
- - Every interactive business action binds `@OpenXiangdaOperation(operation)` and injects `OpenXiangdaBusinessDataApiService` or `OpenXiangdaStandardOperations`. The platform checks the action capability once at App API ingress; the trusted backend then has full Data access only to its exact application/environment while audit retains the initiating user. Do not author `authorizationJSON` or reapply the user's resource, row and field permissions inside the action.
11
+ - Every interactive business action binds `@OpenXiangdaOperation(operation)` and injects `OpenXiangdaBusinessDataApiService`, `OpenXiangdaBusinessNotificationService`, or `OpenXiangdaStandardOperations`. The platform checks the action capability once at App API ingress; the trusted backend then has full Data/managed business-notification access only to its exact application/environment while audit retains the initiating user and action. Do not author `authorizationJSON`, forward user tokens, grant ordinary users `app:notification2:send`, or reapply the user's resource, row and field permissions inside the action.
12
12
  - A NestJS backend declares only `enabled`, `isolation: 'shared' | 'dedicated'` and `resourceProfile: 'light' | 'standard'`. Never put raw Kubernetes resources, replicas, ports or environment maps in application metadata; the platform owns capacity and scaling.
13
13
  - Use the current logged-in user and the union of that user's application roles. An optional declared Perspective projects only read visibility (pages, rows and fields); create/update/delete, workflows and custom actions continue to authorize against the complete union. Standard Data API calls inherit `X-OpenXiangda-Perspective` automatically. Use `@CurrentPerspective()` only when custom Nest code reads outside the standard Data API, and apply an equivalent read projection explicitly. Business code must not persist a platform Token or authorization result and must not implement a second identity path.
14
14
  - Fields inherit the resource read/create/update capabilities. Use field `access` only to tighten them; arrays are all-of and `false` is explicit deny. There is no `write` fallback.