@cosmicdrift/kumiko-types 0.413.0 → 0.414.0
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/access-predicates.d.ts +1 -0
- package/dist/access-predicates.js +3 -3
- package/dist/config.d.ts +2 -2
- package/dist/db-connection.d.ts +0 -3
- package/dist/define-handler.d.ts +1 -1
- package/dist/delivery-attempt-types.d.ts +22 -0
- package/dist/delivery-attempt-types.js +31 -0
- package/dist/entity-table-meta-types.d.ts +2 -0
- package/dist/event-store-executor-types.d.ts +4 -1
- package/dist/feature.d.ts +1 -0
- package/dist/fields.d.ts +2 -0
- package/dist/handler-context-facets.d.ts +5 -2
- package/dist/handler-context-services.d.ts +3 -1
- package/dist/handlers.d.ts +16 -15
- package/dist/handlers.js +4 -32
- package/dist/hooks.d.ts +1 -0
- package/dist/identifiers.d.ts +7 -0
- package/dist/identifiers.js +20 -0
- package/dist/minor-units.d.ts +3 -0
- package/dist/minor-units.js +11 -0
- package/dist/named-list-filters.d.ts +26 -0
- package/dist/observability/provider.d.ts +0 -2
- package/dist/screen-dashboard.d.ts +139 -22
- package/dist/screen-entity-edit.d.ts +2 -0
- package/dist/screen-entity-list.d.ts +75 -19
- package/dist/screen-list-table.d.ts +23 -3
- package/dist/screen-list-table.js +8 -1
- package/dist/screen-projection.d.ts +48 -4
- package/dist/screen-record-guidance.d.ts +88 -0
- package/dist/screen-record-guidance.js +1 -0
- package/dist/screen.d.ts +9 -5
- package/dist/screen.js +1 -1
- package/package.json +9 -1
- package/src/changes.json +6 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AccessRule } from "./handlers.ts";
|
|
2
2
|
export declare function isOpenToAllGranted(rule: AccessRule): boolean;
|
|
3
|
+
export declare const ANONYMOUS_ROLE: "anonymous";
|
|
3
4
|
export declare function isUiAccessGranted(access: AccessRule | undefined, userRoles: readonly string[] | undefined): boolean;
|
|
4
5
|
export declare function isAnySignedInGranted(rule: AccessRule): boolean;
|
|
@@ -11,7 +11,7 @@ export function isOpenToAllGranted(rule) {
|
|
|
11
11
|
return false;
|
|
12
12
|
return openToAll.reason.trim().length > 0;
|
|
13
13
|
}
|
|
14
|
-
const
|
|
14
|
+
export const ANONYMOUS_ROLE = "anonymous";
|
|
15
15
|
// UI-visibility predicate — DEFAULT-VISIBLE, the opposite stance from
|
|
16
16
|
// `hasAccess` and the default-deny doc on AccessRule (handlers.ts): an unset `access`
|
|
17
17
|
// means every signed-in user may see the screen/nav/workspace, not nobody.
|
|
@@ -28,7 +28,7 @@ export function isUiAccessGranted(access, userRoles) {
|
|
|
28
28
|
return false;
|
|
29
29
|
if (!("roles" in access))
|
|
30
30
|
return false;
|
|
31
|
-
if (isAnySignedInGranted(access) && !userRoles.includes(
|
|
31
|
+
if (isAnySignedInGranted(access) && !userRoles.includes(ANONYMOUS_ROLE))
|
|
32
32
|
return true;
|
|
33
33
|
return access.roles.some((role) => userRoles.includes(role));
|
|
34
34
|
}
|
|
@@ -38,5 +38,5 @@ export function isAnySignedInGranted(rule) {
|
|
|
38
38
|
if (!("roles" in rule) || !("anySignedIn" in rule))
|
|
39
39
|
return false;
|
|
40
40
|
const anySignedIn = rule.anySignedIn;
|
|
41
|
-
return
|
|
41
|
+
return anySignedIn === true && Array.isArray(rule.roles) && rule.roles.includes(ANONYMOUS_ROLE);
|
|
42
42
|
}
|
package/dist/config.d.ts
CHANGED
|
@@ -56,7 +56,7 @@ export type ConfigKeyDefinition<T extends ConfigKeyType = ConfigKeyType> = {
|
|
|
56
56
|
readonly options?: readonly string[];
|
|
57
57
|
/** Only on `type: "select"`: loads the options from this query (QN), contract
|
|
58
58
|
* `{ rows: { value, label, description?, group? }[] }`, labels shown verbatim. Mutually exclusive with
|
|
59
|
-
* `options
|
|
59
|
+
* `options`. The write side does not check the value against
|
|
60
60
|
* the query result; the consuming feature validates it where it uses it. */
|
|
61
61
|
readonly optionsQuery?: string;
|
|
62
62
|
/** Payload sent with every `optionsQuery` call. Requires `optionsQuery`. `{ field }`
|
|
@@ -70,7 +70,6 @@ export type ConfigKeyDefinition<T extends ConfigKeyType = ConfigKeyType> = {
|
|
|
70
70
|
};
|
|
71
71
|
readonly computed?: ConfigComputedFn<T>;
|
|
72
72
|
readonly validate?: ConfigValidateFn<T>;
|
|
73
|
-
readonly allowPerRequest?: boolean;
|
|
74
73
|
readonly required?: boolean;
|
|
75
74
|
readonly env?: string;
|
|
76
75
|
readonly inheritedToTenant?: boolean;
|
|
@@ -180,6 +179,7 @@ export type JobDefinition = {
|
|
|
180
179
|
readonly runOnBoot?: boolean | undefined;
|
|
181
180
|
readonly bootGate?: boolean | undefined;
|
|
182
181
|
readonly perTenant?: boolean | undefined;
|
|
182
|
+
readonly includeSystemTenant?: boolean | undefined;
|
|
183
183
|
readonly runIn?: JobRunIn | undefined;
|
|
184
184
|
readonly escapeHatch?: EscapeHatchDeclaration | undefined;
|
|
185
185
|
readonly tenantVisibleFailure?: {
|
package/dist/db-connection.d.ts
CHANGED
|
@@ -2,9 +2,6 @@ import type { SQL, TransactionSQL } from "bun";
|
|
|
2
2
|
export type RawDbClient = {
|
|
3
3
|
unsafe: (sql: string, params?: readonly unknown[]) => PromiseLike<readonly unknown[]>;
|
|
4
4
|
begin: <T>(fn: (tx: DbTx) => Promise<T>) => Promise<T>;
|
|
5
|
-
end?: (options?: {
|
|
6
|
-
timeout?: number;
|
|
7
|
-
}) => Promise<void>;
|
|
8
5
|
};
|
|
9
6
|
export type DbConnection = SQL | RawDbClient;
|
|
10
7
|
export type DbTx = TransactionSQL | RawDbClient;
|
package/dist/define-handler.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export type WriteHandlerDefinition<TName extends string = string, TSchema extend
|
|
|
9
9
|
readonly access: AccessRule;
|
|
10
10
|
readonly description?: string;
|
|
11
11
|
readonly agent?: AgentHandlerHints;
|
|
12
|
-
/** Set by the entity convention create/update handlers;
|
|
12
|
+
/** Set by the entity convention create/update handlers; a hand-written handler may declare it for its form, its own schema still decides what it accepts. See WriteHandlerDef.excludedFields. */
|
|
13
13
|
readonly excludedFields?: readonly string[];
|
|
14
14
|
readonly unsafeSkipTransitionGuard?: boolean;
|
|
15
15
|
readonly rateLimit?: RateLimitDeclaration;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { DbConnection } from "./db-connection.ts";
|
|
2
|
+
import type { NotifyPriority, SessionUser } from "./handlers.ts";
|
|
3
|
+
import type { TenantId } from "./identifiers.ts";
|
|
4
|
+
export type NotifyDeliveryStatus = "queued" | "sent" | "failed" | "skipped";
|
|
5
|
+
export declare const DELIVERY_FAILURE_CODES: readonly ["timeout", "network_error", "redirect_blocked", "host_not_allowed", "missing_credentials", "invalid_address", "unexpected_response", "render_failed", "send_failed", "channel_error"];
|
|
6
|
+
export declare const DELIVERY_SKIP_REASONS: readonly ["channel_disabled", "preference_disabled", "rate_limited", "no_address", "unsubscribed", "duplicate_idempotency_key"];
|
|
7
|
+
export type DeliveryFailureCode = (typeof DELIVERY_FAILURE_CODES)[number];
|
|
8
|
+
export type DeliverySkipReason = (typeof DELIVERY_SKIP_REASONS)[number];
|
|
9
|
+
export type DeliveryErrorCode = DeliveryFailureCode | DeliverySkipReason | `http_${number}`;
|
|
10
|
+
export type ChatSendFailureCode = `http_${number}` | Exclude<DeliveryFailureCode, "render_failed" | "send_failed" | "channel_error">;
|
|
11
|
+
export declare const HTTP_ERROR_CODE_PATTERN: RegExp;
|
|
12
|
+
export declare function isDeliveryErrorCode(value: unknown): value is DeliveryErrorCode;
|
|
13
|
+
export type DeliveryAttemptLogEntry = {
|
|
14
|
+
readonly notificationType: string;
|
|
15
|
+
readonly channel: string;
|
|
16
|
+
readonly recipientAddress: string | null;
|
|
17
|
+
readonly status: NotifyDeliveryStatus;
|
|
18
|
+
readonly error: DeliveryErrorCode | null;
|
|
19
|
+
readonly priority: NotifyPriority;
|
|
20
|
+
};
|
|
21
|
+
export type DeliveryAttemptLogFn = (entry: DeliveryAttemptLogEntry) => Promise<void>;
|
|
22
|
+
export type DeliveryAttemptLogFactory = (user: SessionUser, tenantId: TenantId, deliveryDb?: DbConnection) => DeliveryAttemptLogFn;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Closed vocabulary for delivery failures and skips. Only these values (plus
|
|
2
|
+
// `http_<status>`) are ever stored or returned as a delivery error, never a raw
|
|
3
|
+
// error message: provider errors can carry URLs, tokens and addresses.
|
|
4
|
+
export const DELIVERY_FAILURE_CODES = [
|
|
5
|
+
"timeout",
|
|
6
|
+
"network_error",
|
|
7
|
+
"redirect_blocked",
|
|
8
|
+
"host_not_allowed",
|
|
9
|
+
"missing_credentials",
|
|
10
|
+
"invalid_address",
|
|
11
|
+
"unexpected_response",
|
|
12
|
+
"render_failed",
|
|
13
|
+
"send_failed",
|
|
14
|
+
"channel_error",
|
|
15
|
+
];
|
|
16
|
+
export const DELIVERY_SKIP_REASONS = [
|
|
17
|
+
"channel_disabled",
|
|
18
|
+
"preference_disabled",
|
|
19
|
+
"rate_limited",
|
|
20
|
+
"no_address",
|
|
21
|
+
"unsubscribed",
|
|
22
|
+
"duplicate_idempotency_key",
|
|
23
|
+
];
|
|
24
|
+
export const HTTP_ERROR_CODE_PATTERN = /^http_(\d{3})$/;
|
|
25
|
+
export function isDeliveryErrorCode(value) {
|
|
26
|
+
if (typeof value !== "string")
|
|
27
|
+
return false;
|
|
28
|
+
return (HTTP_ERROR_CODE_PATTERN.test(value) ||
|
|
29
|
+
DELIVERY_FAILURE_CODES.some((code) => code === value) ||
|
|
30
|
+
DELIVERY_SKIP_REASONS.some((reason) => reason === value));
|
|
31
|
+
}
|
|
@@ -30,6 +30,7 @@ export type EntityTableMeta = {
|
|
|
30
30
|
readonly source: "managed" | "unmanaged";
|
|
31
31
|
readonly piiSubjectFields?: readonly string[];
|
|
32
32
|
readonly tenancy?: "global";
|
|
33
|
+
readonly systemRowsReadable?: true;
|
|
33
34
|
};
|
|
34
35
|
export type BuildEntityTableMetaOptions = {
|
|
35
36
|
readonly featureName?: string;
|
|
@@ -42,4 +43,5 @@ export type UnmanagedTableInput = {
|
|
|
42
43
|
readonly indexes?: readonly IndexMeta[];
|
|
43
44
|
readonly compositePrimaryKey?: CompositePrimaryKeyMeta;
|
|
44
45
|
readonly tenancy?: EntityTenancy;
|
|
46
|
+
readonly systemRowsReadable?: true;
|
|
45
47
|
};
|
|
@@ -15,6 +15,7 @@ export type ParentVisibilityOption = {
|
|
|
15
15
|
}>;
|
|
16
16
|
readonly canReadEntity?: (entityName: string) => boolean;
|
|
17
17
|
readonly resolveTable?: (entityName: string) => SchemaTable;
|
|
18
|
+
readonly globalHostIds?: ReadonlyMap<string, readonly string[]>;
|
|
18
19
|
};
|
|
19
20
|
export type ParentVisibilityGateOption = {
|
|
20
21
|
readonly parentVisibility?: ParentVisibilityOption;
|
|
@@ -91,7 +92,9 @@ export type EventStoreExecutor<Row extends Record<string, unknown> = Record<stri
|
|
|
91
92
|
}) => Promise<WriteResult<SaveContext>>;
|
|
92
93
|
delete: (payload: {
|
|
93
94
|
id: EntityId;
|
|
94
|
-
}, user: SessionUser, db: TenantDb, options?: StreamTenantOption & LockGuardOption & ParentVisibilityGateOption
|
|
95
|
+
}, user: SessionUser, db: TenantDb, options?: StreamTenantOption & LockGuardOption & ParentVisibilityGateOption & {
|
|
96
|
+
expect?: Readonly<Record<string, string | number | boolean | null>>;
|
|
97
|
+
}) => Promise<WriteResult<DeleteContext>>;
|
|
95
98
|
forget: (payload: {
|
|
96
99
|
id: EntityId;
|
|
97
100
|
}, user: SessionUser, db: TenantDb, options?: StreamTenantOption & ParentVisibilityGateOption) => Promise<WriteResult<DeleteContext>>;
|
package/dist/feature.d.ts
CHANGED
package/dist/fields.d.ts
CHANGED
|
@@ -728,6 +728,8 @@ export type EntityDefinition<F extends FieldsMap = FieldsMap, T extends EntityTe
|
|
|
728
728
|
* only for genuinely tenant-independent aggregates like `user`. The first
|
|
729
729
|
* event (create) is what's routed; updates resolve the stream tenant upstream. */
|
|
730
730
|
readonly systemStream?: boolean;
|
|
731
|
+
/** Tenant sessions may read this entity's SYSTEM_TENANT_ID rows (shared templates, config defaults, catalogs). Without it the table's row level security hides them. Never set it on credential, session, secret or PII entities. */
|
|
732
|
+
readonly systemRowsReadable?: boolean;
|
|
731
733
|
/** Lets token scopes release `<entity>.created/.updated/.deleted` as webhook events. Off by default: bundle entities can hold secrets. */
|
|
732
734
|
readonly webhookEvents?: boolean;
|
|
733
735
|
/** Schema version stamped on every lifecycle event (default 1, integer 1..999). Bump it together with a step in `eventMigrations`. */
|
|
@@ -40,9 +40,11 @@ export type HandlerEvents<TMap extends object = KumikoEventTypeMap> = {
|
|
|
40
40
|
readonly load: (aggregateId: string, options?: {
|
|
41
41
|
readonly asOf?: Temporal.Instant;
|
|
42
42
|
readonly atVersion?: never;
|
|
43
|
+
readonly includeArchived?: boolean;
|
|
43
44
|
} | {
|
|
44
45
|
readonly atVersion?: number;
|
|
45
46
|
readonly asOf?: never;
|
|
47
|
+
readonly includeArchived?: boolean;
|
|
46
48
|
}) => Promise<readonly import("./event-store-types.ts").StoredEvent[]>;
|
|
47
49
|
readonly archive: (aggregateId: string, args: {
|
|
48
50
|
readonly aggregateType: string;
|
|
@@ -85,7 +87,7 @@ export type HandlerObservability = {
|
|
|
85
87
|
readonly metricsFor: (featureName: string) => MetricsHandle;
|
|
86
88
|
readonly meter?: Meter;
|
|
87
89
|
};
|
|
88
|
-
export type JobEvents = Pick<HandlerEvents, "append">;
|
|
90
|
+
export type JobEvents = Pick<HandlerEvents, "append" | "load">;
|
|
89
91
|
export type JobDispatch = Pick<HandlerDispatch, "write" | "writeAs" | "queryAs" | "queryAsMember">;
|
|
90
92
|
export type JobObservability = Pick<HandlerObservability, "log" | "metricsFor" | "meter">;
|
|
91
93
|
type PhaseWithoutTransaction = "events" | "notify" | "dispatch" | "lifecycle";
|
|
@@ -97,7 +99,8 @@ export type ReservationContext<TMap extends object = KumikoEventTypeMap> = Omit<
|
|
|
97
99
|
readonly dispatch: Omit<HandlerDispatch, "write" | "writeAs">;
|
|
98
100
|
readonly lifecycle: Omit<HandlerLifecycle, "afterCommit" | "afterRollback">;
|
|
99
101
|
};
|
|
100
|
-
/** ctx of `beforeTransaction`: external I/O and reads only, so the outside-transaction handles are gone too.
|
|
102
|
+
/** ctx of `beforeTransaction`: external I/O and reads only, so the outside-transaction handles are gone too.
|
|
103
|
+
* A long external call that must also write (metering, provenance) belongs in `transaction: "perCommand"`. */
|
|
101
104
|
export type BeforeTransactionContext<TMap extends object = KumikoEventTypeMap> = Omit<ReservationContext<TMap>, "db" | "systemDb"> & {
|
|
102
105
|
readonly db: TenantDb & {
|
|
103
106
|
readonly outsideTransaction: undefined;
|
|
@@ -6,7 +6,7 @@ import type { EntityCache } from "./entity-cache.ts";
|
|
|
6
6
|
import type { EnvelopeCipher } from "./envelope-cipher-types.ts";
|
|
7
7
|
import type { FileContext } from "./file-handle-types.ts";
|
|
8
8
|
import type { FileProviderResolver } from "./file-provider-resolver-types.ts";
|
|
9
|
-
import type { EscapeHatchAuditSink, JobRunnerRef, NotifyFactory, NotifyFn, SessionUser, TenantWriteTarget } from "./handlers.ts";
|
|
9
|
+
import type { DeliveryAttemptLogFactory, DeliveryAttemptLogFn, EscapeHatchAuditSink, JobRunnerRef, NotifyFactory, NotifyFn, SessionUser, TenantWriteTarget } from "./handlers.ts";
|
|
10
10
|
import type { TenantId } from "./identifiers.ts";
|
|
11
11
|
import type { KmsAdapter } from "./kms-adapter-types.ts";
|
|
12
12
|
import type { RateLimitResolver } from "./rate-limit-types.ts";
|
|
@@ -44,6 +44,8 @@ export type ContextServices = {
|
|
|
44
44
|
readonly _notifyFactory?: NotifyFactory;
|
|
45
45
|
readonly _notifyInTransactionFactory?: (user: SessionUser, tenantId: TenantId) => NotifyFn;
|
|
46
46
|
readonly _notifyAfterCommitFactory?: (user: SessionUser, tenantId: TenantId) => NotifyFn;
|
|
47
|
+
readonly _deliveryAttemptLogFactory?: DeliveryAttemptLogFactory;
|
|
48
|
+
readonly _logOwnDeliveryAttempt?: DeliveryAttemptLogFn;
|
|
47
49
|
readonly _escapeHatchAuditSink?: EscapeHatchAuditSink;
|
|
48
50
|
readonly extensions: Readonly<Record<string, unknown>>;
|
|
49
51
|
};
|
package/dist/handlers.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { ZodType } from "zod";
|
|
|
4
4
|
import type { CacheSyncBus } from "./cache-sync-types.ts";
|
|
5
5
|
import type { ConfigAccessor, ConfigAccessorFactory, ConfigResolver } from "./config.ts";
|
|
6
6
|
import type { DbConnection, DbTx } from "./db-connection.ts";
|
|
7
|
+
import type { DeliveryAttemptLogFactory, DeliveryAttemptLogFn, DeliveryErrorCode, NotifyDeliveryStatus } from "./delivery-attempt-types.ts";
|
|
7
8
|
import type { DerivativesContext } from "./derivatives-types.ts";
|
|
8
9
|
import type { EntityCache } from "./entity-cache.ts";
|
|
9
10
|
import type { EventMetadata, WriteOrigin } from "./event-store-types.ts";
|
|
@@ -49,7 +50,7 @@ export declare function crossTenantReadsGrantFor(declaration: EscapeHatchDeclara
|
|
|
49
50
|
export declare function escapeHatchGrantsProblem(declaration: {
|
|
50
51
|
readonly grants?: unknown;
|
|
51
52
|
}): string | undefined;
|
|
52
|
-
export { isAnySignedInGranted, isOpenToAllGranted, isUiAccessGranted, } from "./access-predicates.ts";
|
|
53
|
+
export { ANONYMOUS_ROLE, isAnySignedInGranted, isOpenToAllGranted, isUiAccessGranted, } from "./access-predicates.ts";
|
|
53
54
|
export type SessionUserOrigin = "member-resolution";
|
|
54
55
|
export type SessionUser = {
|
|
55
56
|
readonly id: string;
|
|
@@ -162,15 +163,7 @@ export type NotifyOptions = {
|
|
|
162
163
|
readonly immediate?: boolean;
|
|
163
164
|
readonly locale?: string;
|
|
164
165
|
};
|
|
165
|
-
export type
|
|
166
|
-
export declare const DELIVERY_FAILURE_CODES: readonly ["timeout", "network_error", "redirect_blocked", "host_not_allowed", "missing_credentials", "invalid_address", "unexpected_response", "render_failed", "send_failed", "channel_error"];
|
|
167
|
-
export declare const DELIVERY_SKIP_REASONS: readonly ["channel_disabled", "preference_disabled", "rate_limited", "no_address", "unsubscribed", "duplicate_idempotency_key"];
|
|
168
|
-
export type DeliveryFailureCode = (typeof DELIVERY_FAILURE_CODES)[number];
|
|
169
|
-
export type DeliverySkipReason = (typeof DELIVERY_SKIP_REASONS)[number];
|
|
170
|
-
export type DeliveryErrorCode = DeliveryFailureCode | DeliverySkipReason | `http_${number}`;
|
|
171
|
-
export type ChatSendFailureCode = `http_${number}` | Exclude<DeliveryFailureCode, "render_failed" | "send_failed" | "channel_error">;
|
|
172
|
-
export declare const HTTP_ERROR_CODE_PATTERN: RegExp;
|
|
173
|
-
export declare function isDeliveryErrorCode(value: unknown): value is DeliveryErrorCode;
|
|
166
|
+
export { type ChatSendFailureCode, DELIVERY_FAILURE_CODES, DELIVERY_SKIP_REASONS, type DeliveryAttemptLogEntry, type DeliveryAttemptLogFactory, type DeliveryAttemptLogFn, type DeliveryErrorCode, type DeliveryFailureCode, type DeliverySkipReason, HTTP_ERROR_CODE_PATTERN, isDeliveryErrorCode, type NotifyDeliveryStatus, } from "./delivery-attempt-types.ts";
|
|
174
167
|
export type NotifyDelivery = {
|
|
175
168
|
readonly channel: string;
|
|
176
169
|
readonly recipientId: string | null;
|
|
@@ -244,6 +237,8 @@ type SharedContextFields = {
|
|
|
244
237
|
readonly _notifyFactory?: NotifyFactory;
|
|
245
238
|
readonly _notifyInTransactionFactory?: (user: SessionUser, tenantId: TenantId) => NotifyFn;
|
|
246
239
|
readonly _notifyAfterCommitFactory?: (user: SessionUser, tenantId: TenantId) => NotifyFn;
|
|
240
|
+
readonly _deliveryAttemptLogFactory?: DeliveryAttemptLogFactory;
|
|
241
|
+
readonly _logOwnDeliveryAttempt?: DeliveryAttemptLogFn;
|
|
247
242
|
readonly _escapeHatchAuditSink?: EscapeHatchAuditSink;
|
|
248
243
|
readonly secrets?: import("./secrets-types.ts").SecretsContext;
|
|
249
244
|
readonly masterKeyProvider?: import("./secrets-types.ts").MasterKeyProvider;
|
|
@@ -268,9 +263,10 @@ export type AppContext = SharedContextFields & {
|
|
|
268
263
|
readonly id: string;
|
|
269
264
|
readonly tenantId: TenantId;
|
|
270
265
|
} | null;
|
|
271
|
-
/** For job-handler calls triggered by an
|
|
272
|
-
* (`r.job` with `trigger: { on: ... }`): the qualified name
|
|
273
|
-
*
|
|
266
|
+
/** For job-handler calls triggered by an `on` entry
|
|
267
|
+
* (`r.job` with `trigger: { on: ... }`): the qualified name the entry matched,
|
|
268
|
+
* either a write/query-handler QN (e.g. `multi:write:order:open`) or an
|
|
269
|
+
* `r.defineEvent` QN (e.g. `inbound-mail-foundation:event:inbound-message-received`).
|
|
274
270
|
* For multi-trigger jobs (`on: [...]`), this is the only way for the
|
|
275
271
|
* handler to know which trigger fired. Cron and manual jobs leave the
|
|
276
272
|
* field undefined. */
|
|
@@ -348,7 +344,6 @@ export type JobContext = {
|
|
|
348
344
|
readonly dispatch: JobDispatch;
|
|
349
345
|
readonly triggerName?: string;
|
|
350
346
|
readonly triggerEvent?: JobTriggerEvent;
|
|
351
|
-
readonly _tenantId?: TenantId;
|
|
352
347
|
readonly config?: ConfigAccessor;
|
|
353
348
|
readonly notify?: NotifyFn;
|
|
354
349
|
readonly services: ContextServices;
|
|
@@ -546,7 +541,9 @@ export type BeforeTransactionResult<TPrepared = unknown> = {
|
|
|
546
541
|
readonly release?: ReservationRelease;
|
|
547
542
|
} | WriteFailure;
|
|
548
543
|
export type BeforeTransactionPhase<TPayload = unknown, TPrepared = unknown> = (event: WriteEvent<TPayload>, ctx: BeforeTransactionContext) => Promise<BeforeTransactionResult<TPrepared>>;
|
|
549
|
-
/** `perCommand`: the handler runs without a transaction of its own.
|
|
544
|
+
/** `perCommand`: the handler runs without a transaction of its own. Use it for long external calls
|
|
545
|
+
* (minutes) that also need writes: no transaction or connection is held while the call runs, and
|
|
546
|
+
* `ctx.db.outsideTransaction` stays available for metering/provenance bookings. */
|
|
550
547
|
export type WriteHandlerTransactionMode = "perCommand";
|
|
551
548
|
/** `skip`: run as a nested write without the `beforeTransaction` phase. */
|
|
552
549
|
export type BeforeTransactionWhenNested = "skip";
|
|
@@ -558,6 +555,9 @@ export type WriteHandlerDef = {
|
|
|
558
555
|
readonly name: string;
|
|
559
556
|
readonly schema: ZodType;
|
|
560
557
|
readonly handler: WriteHandlerFn;
|
|
558
|
+
/** Set only by defineEntityWriteHandler, which wires the parent-ref visibility gate.
|
|
559
|
+
* The boot-validator requires it on a `parentRef` entity's update/delete/restore handler. */
|
|
560
|
+
readonly [ENTITY_CONVENTION_WRITE_BRAND]?: true;
|
|
561
561
|
readonly access: AccessRule;
|
|
562
562
|
readonly description?: string;
|
|
563
563
|
readonly agent?: AgentHandlerHints;
|
|
@@ -600,6 +600,7 @@ export type WriteHandlerDef = {
|
|
|
600
600
|
readonly perform?: import("./step.ts").PipelineDef;
|
|
601
601
|
};
|
|
602
602
|
export declare const ENTITY_CONVENTION_QUERY_BRAND = "__kumikoEntityConventionQuery";
|
|
603
|
+
export declare const ENTITY_CONVENTION_WRITE_BRAND = "__kumikoEntityConventionWrite";
|
|
603
604
|
export type QueryHandlerDef = {
|
|
604
605
|
readonly name: string;
|
|
605
606
|
readonly schema: ZodType;
|
package/dist/handlers.js
CHANGED
|
@@ -39,38 +39,8 @@ export function escapeHatchGrantsProblem(declaration) {
|
|
|
39
39
|
}
|
|
40
40
|
return undefined;
|
|
41
41
|
}
|
|
42
|
-
export { isAnySignedInGranted, isOpenToAllGranted, isUiAccessGranted, } from "./access-predicates.js";
|
|
43
|
-
|
|
44
|
-
// `http_<status>`) are ever stored or returned as a delivery error, never a raw
|
|
45
|
-
// error message: provider errors can carry URLs, tokens and addresses.
|
|
46
|
-
export const DELIVERY_FAILURE_CODES = [
|
|
47
|
-
"timeout",
|
|
48
|
-
"network_error",
|
|
49
|
-
"redirect_blocked",
|
|
50
|
-
"host_not_allowed",
|
|
51
|
-
"missing_credentials",
|
|
52
|
-
"invalid_address",
|
|
53
|
-
"unexpected_response",
|
|
54
|
-
"render_failed",
|
|
55
|
-
"send_failed",
|
|
56
|
-
"channel_error",
|
|
57
|
-
];
|
|
58
|
-
export const DELIVERY_SKIP_REASONS = [
|
|
59
|
-
"channel_disabled",
|
|
60
|
-
"preference_disabled",
|
|
61
|
-
"rate_limited",
|
|
62
|
-
"no_address",
|
|
63
|
-
"unsubscribed",
|
|
64
|
-
"duplicate_idempotency_key",
|
|
65
|
-
];
|
|
66
|
-
export const HTTP_ERROR_CODE_PATTERN = /^http_(\d{3})$/;
|
|
67
|
-
export function isDeliveryErrorCode(value) {
|
|
68
|
-
if (typeof value !== "string")
|
|
69
|
-
return false;
|
|
70
|
-
return (HTTP_ERROR_CODE_PATTERN.test(value) ||
|
|
71
|
-
DELIVERY_FAILURE_CODES.some((code) => code === value) ||
|
|
72
|
-
DELIVERY_SKIP_REASONS.some((reason) => reason === value));
|
|
73
|
-
}
|
|
42
|
+
export { ANONYMOUS_ROLE, isAnySignedInGranted, isOpenToAllGranted, isUiAccessGranted, } from "./access-predicates.js";
|
|
43
|
+
export { DELIVERY_FAILURE_CODES, DELIVERY_SKIP_REASONS, HTTP_ERROR_CODE_PATTERN, isDeliveryErrorCode, } from "./delivery-attempt-types.js";
|
|
74
44
|
export function normalizeEventPiiSubject(spec) {
|
|
75
45
|
if ("subjectField" in spec) {
|
|
76
46
|
return { kind: "user", ownerField: spec.subjectField, whenAbsent: undefined };
|
|
@@ -122,3 +92,5 @@ export function isRateLimitDisabled(v) {
|
|
|
122
92
|
// workspace can evaluate a module twice, and two Symbol() calls would not
|
|
123
93
|
// compare equal across those instances.
|
|
124
94
|
export const ENTITY_CONVENTION_QUERY_BRAND = "__kumikoEntityConventionQuery";
|
|
95
|
+
// Same brand for the update/delete/restore handlers built by defineEntityWriteHandler.
|
|
96
|
+
export const ENTITY_CONVENTION_WRITE_BRAND = "__kumikoEntityConventionWrite";
|
package/dist/hooks.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export type SaveContext = {
|
|
|
16
16
|
readonly isNew: boolean;
|
|
17
17
|
readonly entityName?: string | undefined;
|
|
18
18
|
readonly event: StoredEvent;
|
|
19
|
+
readonly claimedFileRefIds?: readonly string[] | undefined;
|
|
19
20
|
};
|
|
20
21
|
export type DeleteContext = {
|
|
21
22
|
readonly kind: "delete";
|
package/dist/identifiers.d.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
export type TenantId = string;
|
|
2
2
|
export declare const UUID_SHAPE_PATTERN = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$";
|
|
3
3
|
export declare function isUuid(value: unknown): value is string;
|
|
4
|
+
export type FileHandleParts = {
|
|
5
|
+
readonly id: string;
|
|
6
|
+
readonly claim: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function parseFileHandle(value: unknown): FileHandleParts | null;
|
|
9
|
+
export declare function isFileHandle(value: unknown): value is string;
|
|
10
|
+
export declare function formatFileHandle(parts: FileHandleParts): string;
|
|
4
11
|
export declare function parseTenantId(value: unknown): TenantId | null;
|
|
5
12
|
export declare const SYSTEM_TENANT_ID: TenantId;
|
|
6
13
|
export declare function isSystemTenant(tenantId: TenantId | null | undefined): boolean;
|
package/dist/identifiers.js
CHANGED
|
@@ -20,6 +20,26 @@ const TENANT_ID_REGEX = new RegExp(UUID_SHAPE_PATTERN);
|
|
|
20
20
|
export function isUuid(value) {
|
|
21
21
|
return typeof value === "string" && TENANT_ID_REGEX.test(value);
|
|
22
22
|
}
|
|
23
|
+
// Opaque reference to a pending anonymous upload: `<fileRef uuid>.<claim>`. The claim is 32 random
|
|
24
|
+
// bytes, base64url; the server stores only its hash. Field values accept this form and persist
|
|
25
|
+
// the bare uuid.
|
|
26
|
+
const FILE_HANDLE_CLAIM_PATTERN = /^[A-Za-z0-9_-]{43}$/;
|
|
27
|
+
export function parseFileHandle(value) {
|
|
28
|
+
if (typeof value !== "string")
|
|
29
|
+
return null;
|
|
30
|
+
const separator = value.indexOf(".");
|
|
31
|
+
if (separator === -1)
|
|
32
|
+
return null;
|
|
33
|
+
const id = value.slice(0, separator);
|
|
34
|
+
const claim = value.slice(separator + 1);
|
|
35
|
+
return isUuid(id) && FILE_HANDLE_CLAIM_PATTERN.test(claim) ? { id, claim } : null;
|
|
36
|
+
}
|
|
37
|
+
export function isFileHandle(value) {
|
|
38
|
+
return parseFileHandle(value) !== null;
|
|
39
|
+
}
|
|
40
|
+
export function formatFileHandle(parts) {
|
|
41
|
+
return `${parts.id}.${parts.claim}`;
|
|
42
|
+
}
|
|
23
43
|
// Validates a candidate string against the tenantId format and returns it
|
|
24
44
|
// as a TenantId, or `null` when it doesn't match. Use at every system
|
|
25
45
|
// boundary that admits untrusted input (HTTP headers, cookies, query
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// 2 decimal places (cents) — covers every currently-supported currency
|
|
2
|
+
// (EUR/USD/GBP/...). No ISO-4217 minor-unit table yet (JPY=0, BHD=3) —
|
|
3
|
+
// upgrade path once a currency needing a different scale actually lands.
|
|
4
|
+
// Client-safe on purpose: db/money.ts pulls in engine runtime values.
|
|
5
|
+
export const MINOR_UNIT_SCALE = 100;
|
|
6
|
+
export function toMajorUnits(minorAmount) {
|
|
7
|
+
return minorAmount / MINOR_UNIT_SCALE;
|
|
8
|
+
}
|
|
9
|
+
export function toMinorUnits(amount) {
|
|
10
|
+
return Math.round(amount * MINOR_UNIT_SCALE);
|
|
11
|
+
}
|
|
@@ -16,8 +16,34 @@ export type NamedListFilterDef = {
|
|
|
16
16
|
readonly access?: AccessRule;
|
|
17
17
|
readonly filters: (scope: NamedListFilterScope) => readonly NamedListFilterCondition[] | Promise<readonly NamedListFilterCondition[]>;
|
|
18
18
|
};
|
|
19
|
+
export type ListSortDir = "asc" | "desc";
|
|
20
|
+
export type ListSortSpec = {
|
|
21
|
+
readonly field: string;
|
|
22
|
+
readonly dir: ListSortDir;
|
|
23
|
+
};
|
|
24
|
+
/** One entry of a list's "Sorted by" select; `label` is an i18n key. */
|
|
25
|
+
export type ListSortOption = ListSortSpec & {
|
|
26
|
+
readonly label: string;
|
|
27
|
+
};
|
|
19
28
|
/** Screen-side reference to an entity-defined named filter; `label` is an i18n key. */
|
|
20
29
|
export type NamedListFilterRef = {
|
|
21
30
|
readonly id: string;
|
|
22
31
|
readonly label: string;
|
|
32
|
+
/** Order while this filter is active and the URL carries no sort of its own; wins over `defaultSort`. */
|
|
33
|
+
readonly sort?: ListSortSpec;
|
|
34
|
+
/** Shown instead of the generic "no matches" text when this filter yields an empty list and no search term is set; both are i18n keys. */
|
|
35
|
+
readonly emptyState?: {
|
|
36
|
+
readonly title: string;
|
|
37
|
+
readonly description?: string;
|
|
38
|
+
};
|
|
39
|
+
/** Set by the framework from the entity filter's role rule; the chip is offered only to users holding one of them. */
|
|
40
|
+
readonly roles?: readonly string[];
|
|
41
|
+
};
|
|
42
|
+
/** How the named filters appear in the toolbar: toggle chips (default) or one single-choice select. */
|
|
43
|
+
export type NamedFilterFacetOptions = {
|
|
44
|
+
readonly display?: "chips" | "select";
|
|
45
|
+
/** Ask the list query for each filter's hit count and show it next to the label. */
|
|
46
|
+
readonly showCounts?: boolean;
|
|
47
|
+
/** i18n key for the select's label, e.g. "To do". */
|
|
48
|
+
readonly label?: string;
|
|
23
49
|
};
|
|
@@ -8,8 +8,6 @@ export type SamplingConfig = {
|
|
|
8
8
|
};
|
|
9
9
|
};
|
|
10
10
|
export type SensitiveFilterConfig = {
|
|
11
|
-
readonly redactedHeaders: readonly string[];
|
|
12
|
-
readonly redactedQueryParams: readonly string[];
|
|
13
11
|
readonly redactedAttributeKeyPatterns: readonly RegExp[];
|
|
14
12
|
};
|
|
15
13
|
export type ObservabilityOptions = {
|