@civitai/app-sdk 0.62.0 → 0.64.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/README.md +3 -2
- package/dist/blocks/index.d.ts +2 -2
- package/dist/blocks/messages.d.ts +55 -2
- package/dist/blocks/messages.js +1 -0
- package/dist/blocks/types.d.ts +112 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/manifest/defineBlock.js +19 -10
- package/dist/manifest/discriminatedBranch.d.ts +54 -0
- package/dist/manifest/discriminatedBranch.js +100 -0
- package/dist/manifest/index.d.ts +1 -1
- package/package.json +1 -1
- package/schemas/app-block/v1.json +98 -0
package/README.md
CHANGED
|
@@ -85,12 +85,13 @@ over `window.postMessage({ type, payload }, targetOrigin)`, discriminated by
|
|
|
85
85
|
`type`. The full union is exported:
|
|
86
86
|
|
|
87
87
|
- **parent → block**: `BLOCK_INIT`, `TOKEN_REFRESH`, `TOKEN_REFRESH_RESPONSE`,
|
|
88
|
-
`ESTIMATE_RESULT`, `WORKFLOW_SUBMITTED`, `WORKFLOW_STATUS`,
|
|
88
|
+
`ESTIMATE_RESULT`, `ESTIMATE_BATCH_RESULT`, `WORKFLOW_SUBMITTED`, `WORKFLOW_STATUS`,
|
|
89
89
|
`BUZZ_PURCHASE_RESULT`, `CHECKPOINT_PICKER_RESULT`, `USER_CHECKPOINT_SET`,
|
|
90
90
|
`APP_STORAGE_*_RESULT`, `SUSPEND`, `RESUME`, `THEME_CHANGE`, `ROUTE_CHANGED`,
|
|
91
91
|
`CONSENT_UNAVAILABLE` (`ParentToBlockMessage`).
|
|
92
92
|
- **block → parent**: `BLOCK_READY`, `BLOCK_ERROR`, `REQUEST_TOKEN`,
|
|
93
|
-
`RESIZE_IFRAME`, `SUBMIT_WORKFLOW`, `ESTIMATE_WORKFLOW`,
|
|
93
|
+
`RESIZE_IFRAME`, `SUBMIT_WORKFLOW`, `ESTIMATE_WORKFLOW`,
|
|
94
|
+
`ESTIMATE_WORKFLOW_BATCH`, `POLL_WORKFLOW`,
|
|
94
95
|
`QUERY_APP_WORKFLOWS`, `CANCEL_APP_WORKFLOW`, `OPEN_BUZZ_PURCHASE`,
|
|
95
96
|
`OPEN_CHECKPOINT_PICKER`, `SET_USER_CHECKPOINT`, `NAVIGATE`, `TRACK_EVENT`,
|
|
96
97
|
`APP_STORAGE_*` (`BlockToParentMessage`).
|
package/dist/blocks/index.d.ts
CHANGED
|
@@ -96,7 +96,7 @@ export type { AppStorageRejectionReason } from './appStorageErrors.js';
|
|
|
96
96
|
export { BLOCK_INIT_FRAGMENT_MARKER_KEY, BLOCK_INIT_FRAGMENT_VERSION, BLOCK_INIT_FRAGMENT_KEYS, encodeBlockInitFragment, parseBlockInitFragment, stripBlockInitFragment, } from './initFragment.js';
|
|
97
97
|
export type { BlockInitFragment } from './initFragment.js';
|
|
98
98
|
export { isMessage, BLOCK_TO_PARENT_MESSAGE_TYPES, OTHER_MESSAGE_TYPE_LABEL, boundBlockToParentMessageType, } from './messages.js';
|
|
99
|
-
export type { BlockInitPayload, BlockToParentMessage, BlockToParentMessageType, ConsentUnavailablePayload, ConsentUnavailableReason, ParentToBlockMessage, ParentToBlockMessageType, SharedStorageItemWire, SharedStorageValue, WrappedToken, BlockBuzzTransactionsParams, BlockDailyCompensationParams, AppWorkflowsParams, } from './messages.js';
|
|
99
|
+
export type { BlockInitPayload, BlockToParentMessage, BlockToParentMessageType, ConsentUnavailablePayload, ConsentUnavailableReason, ParentToBlockMessage, ParentToBlockMessageType, SharedStorageItemWire, SharedStorageRateLimit, SharedStorageValue, WrappedToken, BlockBuzzTransactionsParams, BlockDailyCompensationParams, AppWorkflowsParams, } from './messages.js';
|
|
100
100
|
/**
|
|
101
101
|
* Runtime narrowing for the `slotId`-discriminated {@link BlockContext} union.
|
|
102
102
|
* Values, not types — a slot context crosses a `postMessage` boundary, so its
|
|
@@ -109,7 +109,7 @@ export { isModelSlotContext, isPageSlotContext } from './types.js';
|
|
|
109
109
|
* in `types.ts` for which of the two it uses and why.
|
|
110
110
|
*/
|
|
111
111
|
export { isSignedIn } from './types.js';
|
|
112
|
-
export type { BlockContext, KnownSlotId, ModelSlotId, PageSlotContext, PageSlotId, UnknownSlotContext, BlockManifest, BlockManifestGood, BlockManifestV1, BlockSettings, BlockToken, ContentRating, ManifestBooleanField, ManifestIframe, ManifestNumberField, ManifestPage, ManifestPreview, ManifestSettingField, ManifestSettings, ManifestStringField, ManifestTarget, ModelSlotContext, SettingScope, SettingWidget, Theme, ViewerInfo, BlockCheckpointInfo, BlockResourceInfo, BlockResourcePickerType, BlockSourceImage, BlockUploadedImageInfo, BlockGenerationSourceImageInfo, BlockPendingImageInfo, BlockImageScanResult, BlockUploadPurpose, BlockNavigateScope, BlockTextToImageParams, BlockWorkflowSnapshot, BlockModelSubstitution, ModelSubstitutionReason, BlockStepToolCall, BlockTrainedEpoch, BlockPublishedModel, BuzzAccountType, ShowcaseImage, WorkflowBody, WorkflowBodyTextToImage, WorkflowBodyCustomComfy, WorkflowBodyCustomComfyRecipe, WorkflowBodyCustomComfyInline, InlineComfyNode, WorkflowBodyStep, WorkflowBodyPassThroughStep, WorkflowStatus, BlockBuzzTransaction, BlockBuzzAccount, BlockDailyCompensationResource, BlockViewer, BlockWildcardPack, BlockWildcardPackErrorCode, AppWorkflow, AppWorkflowImage, BlockGatedImage, BlockCollectionFollowErrorCode, BlockCollectionFollowResult, BlockPostSource, BlockCreatePostRequest, BlockCreatePostResult, BlockCreatePostHostError, WorkflowBodyTraining, AiToolkitTrainingParams, AiToolkitTrainingParamsBase, AiToolkitPlainEcosystem, AiToolkitLrScheduler, AiToolkitOptimizerType, BlockTrainingQuote, BlockTrainingDatasetItem, BlockTrainingDatasetResult, BlockTrainingRejectionReason, BlockPrepareTrainingDatasetHostError, BlockRunTrainingHostError, } from './types.js';
|
|
112
|
+
export type { BlockContext, KnownSlotId, ModelSlotId, PageSlotContext, PageSlotId, UnknownSlotContext, BlockAnalyticsDeclaration, BlockAnalyticsEventDeclaration, BlockAnalyticsPropertyDeclaration, BlockManifest, BlockManifestGood, BlockManifestV1, BlockSettings, BlockToken, ContentRating, ManifestBooleanField, ManifestIframe, ManifestNumberField, ManifestPage, ManifestPreview, ManifestSettingField, ManifestSettings, ManifestStringField, ManifestTarget, ModelSlotContext, SettingScope, SettingWidget, Theme, ViewerInfo, BlockCheckpointInfo, BlockResourceInfo, BlockResourcePickerType, BlockSourceImage, BlockUploadedImageInfo, BlockGenerationSourceImageInfo, BlockPendingImageInfo, BlockImageScanResult, BlockUploadPurpose, BlockNavigateScope, BlockTextToImageParams, BlockWorkflowSnapshot, BlockEstimateBatchAggregate, BlockModelSubstitution, ModelSubstitutionReason, BlockStepToolCall, BlockTrainedEpoch, BlockPublishedModel, BuzzAccountType, ShowcaseImage, WorkflowBody, WorkflowBodyTextToImage, WorkflowBodyCustomComfy, WorkflowBodyCustomComfyRecipe, WorkflowBodyCustomComfyInline, InlineComfyNode, WorkflowBodyStep, WorkflowBodyPassThroughStep, WorkflowStatus, BlockBuzzTransaction, BlockBuzzAccount, BlockDailyCompensationResource, BlockViewer, BlockWildcardPack, BlockWildcardPackErrorCode, AppWorkflow, AppWorkflowImage, BlockGatedImage, BlockCollectionFollowErrorCode, BlockCollectionFollowResult, BlockPostSource, BlockCreatePostRequest, BlockCreatePostResult, BlockCreatePostHostError, WorkflowBodyTraining, AiToolkitTrainingParams, AiToolkitTrainingParamsBase, AiToolkitPlainEcosystem, AiToolkitLrScheduler, AiToolkitOptimizerType, BlockTrainingQuote, BlockTrainingDatasetItem, BlockTrainingDatasetResult, BlockTrainingRejectionReason, BlockPrepareTrainingDatasetHostError, BlockRunTrainingHostError, } from './types.js';
|
|
113
113
|
/**
|
|
114
114
|
* Host bounds for the App Blocks `kind: 'training'` flow, mirrored from
|
|
115
115
|
* civitai/civitai's zod — see `WorkflowBodyTraining`.
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Wire format: `window.postMessage({ type, payload }, targetOrigin)`.
|
|
9
9
|
*/
|
|
10
10
|
import type { ColorDomain } from './browsingLevel.js';
|
|
11
|
-
import type { BlockCheckpointInfo, BlockResourceInfo, BlockResourcePickerType, BlockUploadedImageInfo, BlockGenerationSourceImageInfo, BlockPendingImageInfo, BlockImageScanResult, BlockUploadPurpose, BlockNavigateScope, BlockContext, BlockSettings, Theme, ViewerInfo, WorkflowBody, BlockWorkflowSnapshot, BlockBuzzTransaction, BlockBuzzAccount, BlockDailyCompensationResource, BlockViewer, BlockWildcardPack, BlockWildcardPackErrorCode, AppWorkflow, BlockGatedImage, BlockCollectionFollowErrorCode, BlockCollectionFollowResult, BlockPostSource, BlockCreatePostResult, BlockTrainingDatasetItem, BlockTrainingDatasetResult, WorkflowBodyTraining } from './types.js';
|
|
11
|
+
import type { BlockCheckpointInfo, BlockResourceInfo, BlockResourcePickerType, BlockUploadedImageInfo, BlockGenerationSourceImageInfo, BlockPendingImageInfo, BlockImageScanResult, BlockUploadPurpose, BlockNavigateScope, BlockContext, BlockSettings, Theme, ViewerInfo, WorkflowBody, BlockWorkflowSnapshot, BlockEstimateBatchAggregate, BlockBuzzTransaction, BlockBuzzAccount, BlockDailyCompensationResource, BlockViewer, BlockWildcardPack, BlockWildcardPackErrorCode, AppWorkflow, BlockGatedImage, BlockCollectionFollowErrorCode, BlockCollectionFollowResult, BlockPostSource, BlockCreatePostResult, BlockTrainingDatasetItem, BlockTrainingDatasetResult, WorkflowBodyTraining } from './types.js';
|
|
12
12
|
/**
|
|
13
13
|
* Filter params for `GET_BUZZ_TRANSACTIONS`. All optional; the host validates
|
|
14
14
|
* them server-side (they are NEVER trusted for auth — the account is self-bound
|
|
@@ -239,6 +239,27 @@ export interface SharedStorageValue {
|
|
|
239
239
|
*/
|
|
240
240
|
data?: unknown;
|
|
241
241
|
}
|
|
242
|
+
/**
|
|
243
|
+
* Retry metadata on a RATE-LIMITED shared-storage refusal. The host adds it
|
|
244
|
+
* beside `error` on the six write replies (`SHARED_APPEND_RESULT`,
|
|
245
|
+
* `SHARED_UPDATE_RESULT`, `SHARED_VOTE_RESULT`, `SHARED_UNVOTE_RESULT`,
|
|
246
|
+
* `SHARED_WITHDRAW_RESULT`, `SHARED_REPORT_RESULT`) when the server refused the
|
|
247
|
+
* write because a per-(viewer, app) budget is empty, so a block can render the
|
|
248
|
+
* wait instead of parsing it out of the prose `error`.
|
|
249
|
+
*
|
|
250
|
+
* Its PRESENCE marks the refusal as rate-limited; every other refusal omits it.
|
|
251
|
+
* ADDITIVE + OPTIONAL: a host that predates it never sends it, so a block must
|
|
252
|
+
* treat "absent" as "not known to be rate-limited", never as "retry now".
|
|
253
|
+
*
|
|
254
|
+
* Same shape as the platform's tRPC `error.data.rateLimit` and the REST 429
|
|
255
|
+
* body's `rateLimit` (which also comes with a `Retry-After` header).
|
|
256
|
+
*/
|
|
257
|
+
export interface SharedStorageRateLimit {
|
|
258
|
+
/** Whole seconds until a retry can succeed. An integer, at least 1. */
|
|
259
|
+
retryAfterSeconds: number;
|
|
260
|
+
/** ISO-8601 instant at which a retry can succeed. */
|
|
261
|
+
retryAt: string;
|
|
262
|
+
}
|
|
242
263
|
/**
|
|
243
264
|
* One SHARED-storage entry as it appears on the wire in `SHARED_LIST_RESULT`.
|
|
244
265
|
* `createdAt`/`updatedAt` are ISO-8601 strings; the block-side hook rehydrates
|
|
@@ -360,6 +381,14 @@ export type ParentToBlockMessage = {
|
|
|
360
381
|
requestId: string;
|
|
361
382
|
snapshot: BlockWorkflowSnapshot;
|
|
362
383
|
};
|
|
384
|
+
} | {
|
|
385
|
+
type: 'ESTIMATE_BATCH_RESULT';
|
|
386
|
+
payload: {
|
|
387
|
+
requestId: string;
|
|
388
|
+
snapshots?: BlockWorkflowSnapshot[];
|
|
389
|
+
aggregate?: BlockEstimateBatchAggregate;
|
|
390
|
+
error?: string;
|
|
391
|
+
};
|
|
363
392
|
} | {
|
|
364
393
|
type: 'WORKFLOW_SUBMITTED';
|
|
365
394
|
payload: {
|
|
@@ -515,6 +544,8 @@ export type ParentToBlockMessage = {
|
|
|
515
544
|
payload: {
|
|
516
545
|
requestId: string;
|
|
517
546
|
selected?: BlockResourceInfo;
|
|
547
|
+
selectedResources?: BlockResourceInfo[];
|
|
548
|
+
error?: string;
|
|
518
549
|
};
|
|
519
550
|
} | {
|
|
520
551
|
type: 'IMAGE_UPLOAD_RESULT';
|
|
@@ -610,6 +641,7 @@ export type ParentToBlockMessage = {
|
|
|
610
641
|
requestId: string;
|
|
611
642
|
key: string;
|
|
612
643
|
error?: string;
|
|
644
|
+
rateLimit?: SharedStorageRateLimit;
|
|
613
645
|
};
|
|
614
646
|
} | {
|
|
615
647
|
type: 'SHARED_VOTE_RESULT';
|
|
@@ -617,6 +649,7 @@ export type ParentToBlockMessage = {
|
|
|
617
649
|
requestId: string;
|
|
618
650
|
count: number;
|
|
619
651
|
error?: string;
|
|
652
|
+
rateLimit?: SharedStorageRateLimit;
|
|
620
653
|
};
|
|
621
654
|
} | {
|
|
622
655
|
type: 'SHARED_UNVOTE_RESULT';
|
|
@@ -624,6 +657,7 @@ export type ParentToBlockMessage = {
|
|
|
624
657
|
requestId: string;
|
|
625
658
|
count: number;
|
|
626
659
|
error?: string;
|
|
660
|
+
rateLimit?: SharedStorageRateLimit;
|
|
627
661
|
};
|
|
628
662
|
} | {
|
|
629
663
|
type: 'SHARED_WITHDRAW_RESULT';
|
|
@@ -632,6 +666,7 @@ export type ParentToBlockMessage = {
|
|
|
632
666
|
ok?: boolean;
|
|
633
667
|
deleted?: boolean;
|
|
634
668
|
error?: string;
|
|
669
|
+
rateLimit?: SharedStorageRateLimit;
|
|
635
670
|
};
|
|
636
671
|
} | {
|
|
637
672
|
type: 'SHARED_UPDATE_RESULT';
|
|
@@ -639,6 +674,7 @@ export type ParentToBlockMessage = {
|
|
|
639
674
|
requestId: string;
|
|
640
675
|
ok?: boolean;
|
|
641
676
|
error?: string;
|
|
677
|
+
rateLimit?: SharedStorageRateLimit;
|
|
642
678
|
};
|
|
643
679
|
} | {
|
|
644
680
|
type: 'SHARED_GET_RESULT';
|
|
@@ -653,6 +689,7 @@ export type ParentToBlockMessage = {
|
|
|
653
689
|
requestId: string;
|
|
654
690
|
ok?: boolean;
|
|
655
691
|
error?: string;
|
|
692
|
+
rateLimit?: SharedStorageRateLimit;
|
|
656
693
|
};
|
|
657
694
|
} | {
|
|
658
695
|
type: 'SAVE_IMAGE_RESULT';
|
|
@@ -712,6 +749,12 @@ export type BlockToParentMessage = {
|
|
|
712
749
|
requestId: string;
|
|
713
750
|
body: WorkflowBody;
|
|
714
751
|
};
|
|
752
|
+
} | {
|
|
753
|
+
type: 'ESTIMATE_WORKFLOW_BATCH';
|
|
754
|
+
payload: {
|
|
755
|
+
requestId: string;
|
|
756
|
+
bodies: WorkflowBody[];
|
|
757
|
+
};
|
|
715
758
|
} | {
|
|
716
759
|
type: 'POLL_WORKFLOW';
|
|
717
760
|
payload: {
|
|
@@ -831,6 +874,16 @@ export type BlockToParentMessage = {
|
|
|
831
874
|
resourceType: BlockResourcePickerType;
|
|
832
875
|
/** Optional base-model family hint (ecosystem key or baseModel name). */
|
|
833
876
|
baseModelGroup?: string;
|
|
877
|
+
/**
|
|
878
|
+
* Ask for SEVERAL resources in one picker session. LoRA only — the host
|
|
879
|
+
* refuses it with a Checkpoint. `max` is a whole number ≥ 1; the host
|
|
880
|
+
* clamps anything above 5 (the `additionalResources` cap) to 5. The
|
|
881
|
+
* reply is then `selectedResources`, not `selected`. OMIT the key for
|
|
882
|
+
* a single pick: its absence is what keeps that path unchanged.
|
|
883
|
+
*/
|
|
884
|
+
multiple?: {
|
|
885
|
+
max: number;
|
|
886
|
+
};
|
|
834
887
|
};
|
|
835
888
|
} | {
|
|
836
889
|
type: 'OPEN_IMAGE_UPLOAD';
|
|
@@ -1040,7 +1093,7 @@ export type BlockToParentMessageType = BlockToParentMessage['type'];
|
|
|
1040
1093
|
* it is a measurement, not an invariant, and civitai's own compile-time gate
|
|
1041
1094
|
* (published SDK union ⊆ `keyof INVENTORY`) is what holds one direction of it.
|
|
1042
1095
|
*/
|
|
1043
|
-
export declare const BLOCK_TO_PARENT_MESSAGE_TYPES: readonly ["BLOCK_HELLO", "BLOCK_READY", "BLOCK_ERROR", "BLOCK_MESSAGE_REJECTED", "REQUEST_TOKEN", "RESIZE_IFRAME", "SUBMIT_WORKFLOW", "ESTIMATE_WORKFLOW", "POLL_WORKFLOW", "CANCEL_WORKFLOW", "OPEN_BUZZ_PURCHASE", "GET_BUZZ_BALANCE", "GET_VIEWER", "GET_BUZZ_TRANSACTIONS", "GET_BUZZ_ACCOUNTS", "GET_DAILY_COMPENSATION", "GET_WILDCARD_PACK", "QUERY_APP_WORKFLOWS", "PUBLISH_GENERATION_OUTPUTS", "CREATE_POST_FROM_APP", "PREPARE_TRAINING_DATASET", "RUN_TRAINING", "GET_IMAGES_BY_IDS", "CANCEL_APP_WORKFLOW", "OPEN_CHECKPOINT_PICKER", "OPEN_RESOURCE_PICKER", "OPEN_IMAGE_UPLOAD", "SET_USER_CHECKPOINT", "NAVIGATE", "REQUEST_SIGN_IN", "REQUEST_CONSENT", "TRACK_EVENT", "APP_STORAGE_GET", "APP_STORAGE_SET", "APP_STORAGE_DELETE", "APP_STORAGE_LIST", "APP_STORAGE_QUOTA", "SHARED_LIST", "SHARED_GET_COUNT", "SHARED_GET_COUNTS", "SHARED_APPEND", "SHARED_VOTE", "SHARED_UNVOTE", "SHARED_WITHDRAW", "SHARED_UPDATE", "SHARED_GET", "SHARED_REPORT", "SAVE_IMAGE", "SET_COLLECTION_FOLLOW"];
|
|
1096
|
+
export declare const BLOCK_TO_PARENT_MESSAGE_TYPES: readonly ["BLOCK_HELLO", "BLOCK_READY", "BLOCK_ERROR", "BLOCK_MESSAGE_REJECTED", "REQUEST_TOKEN", "RESIZE_IFRAME", "SUBMIT_WORKFLOW", "ESTIMATE_WORKFLOW", "ESTIMATE_WORKFLOW_BATCH", "POLL_WORKFLOW", "CANCEL_WORKFLOW", "OPEN_BUZZ_PURCHASE", "GET_BUZZ_BALANCE", "GET_VIEWER", "GET_BUZZ_TRANSACTIONS", "GET_BUZZ_ACCOUNTS", "GET_DAILY_COMPENSATION", "GET_WILDCARD_PACK", "QUERY_APP_WORKFLOWS", "PUBLISH_GENERATION_OUTPUTS", "CREATE_POST_FROM_APP", "PREPARE_TRAINING_DATASET", "RUN_TRAINING", "GET_IMAGES_BY_IDS", "CANCEL_APP_WORKFLOW", "OPEN_CHECKPOINT_PICKER", "OPEN_RESOURCE_PICKER", "OPEN_IMAGE_UPLOAD", "SET_USER_CHECKPOINT", "NAVIGATE", "REQUEST_SIGN_IN", "REQUEST_CONSENT", "TRACK_EVENT", "APP_STORAGE_GET", "APP_STORAGE_SET", "APP_STORAGE_DELETE", "APP_STORAGE_LIST", "APP_STORAGE_QUOTA", "SHARED_LIST", "SHARED_GET_COUNT", "SHARED_GET_COUNTS", "SHARED_APPEND", "SHARED_VOTE", "SHARED_UNVOTE", "SHARED_WITHDRAW", "SHARED_UPDATE", "SHARED_GET", "SHARED_REPORT", "SAVE_IMAGE", "SET_COLLECTION_FOLLOW"];
|
|
1044
1097
|
/**
|
|
1045
1098
|
* The bucket a message type falls into when the protocol does not declare it.
|
|
1046
1099
|
*
|
package/dist/blocks/messages.js
CHANGED
package/dist/blocks/types.d.ts
CHANGED
|
@@ -1468,7 +1468,27 @@ export interface BlockWorkflowSnapshot {
|
|
|
1468
1468
|
workflowId: string;
|
|
1469
1469
|
status: 'pending' | 'processing' | 'succeeded' | 'failed' | 'expired' | 'canceled';
|
|
1470
1470
|
cost?: {
|
|
1471
|
+
/** Whole Buzz. Includes {@link authorFee} whenever that field is present. */
|
|
1471
1472
|
total: number;
|
|
1473
|
+
/**
|
|
1474
|
+
* The part of `total` that is the app author's per-generation fee, in whole
|
|
1475
|
+
* Buzz — so `total - authorFee` is the generation's own price. Display only:
|
|
1476
|
+
* it is the amount the host added into `total` for this reply.
|
|
1477
|
+
*
|
|
1478
|
+
* - **A number** wherever `total` was built from a fee quote: the
|
|
1479
|
+
* `estimate()` result for a `textToImage` or registered-`step` body, and
|
|
1480
|
+
* the refusal `submit()` resolves for those kinds when a budget, spend cap
|
|
1481
|
+
* or rate limit refuses the quoted price. **`0` means a fee was looked up
|
|
1482
|
+
* and none applies** to this request.
|
|
1483
|
+
* - **Absent** when the total is not itemised: a host that predates the
|
|
1484
|
+
* field; every snapshot of a submitted workflow (`submit` success, `poll`,
|
|
1485
|
+
* `cancel` — its total is the realized generation cost and the fee is a
|
|
1486
|
+
* separate charge); the registered-`step` `submit()` refusal for a missing
|
|
1487
|
+
* orchestrator price quote, which refuses before any fee is looked up; and
|
|
1488
|
+
* the kinds that price no fee (`customComfy`, pass-through `step`,
|
|
1489
|
+
* `training`). **Do not read absent as `0`** — show `total` on its own.
|
|
1490
|
+
*/
|
|
1491
|
+
authorFee?: number;
|
|
1472
1492
|
};
|
|
1473
1493
|
imageUrls?: string[];
|
|
1474
1494
|
error?: string;
|
|
@@ -1674,6 +1694,27 @@ export interface BlockWorkflowSnapshot {
|
|
|
1674
1694
|
*/
|
|
1675
1695
|
trainingQuote?: BlockTrainingQuote;
|
|
1676
1696
|
}
|
|
1697
|
+
/**
|
|
1698
|
+
* The run total a BATCH estimate reports beside its per-cell snapshots
|
|
1699
|
+
* (`ESTIMATE_BATCH_RESULT`, sent in reply to `ESTIMATE_WORKFLOW_BATCH`).
|
|
1700
|
+
*
|
|
1701
|
+
* `total` is the sum of `cost.total` over the cells that PRICED: a snapshot that
|
|
1702
|
+
* is not `'failed'` and carries a numeric `cost.total`, the same rule one
|
|
1703
|
+
* estimate is held to. It is the total for the whole list only when
|
|
1704
|
+
* `pricedCells === cellCount`. Otherwise it is a partial sum; show it as one.
|
|
1705
|
+
*
|
|
1706
|
+
* 🔴 IT IS A QUOTE, NOT A LOCK, exactly as each cell is. Nothing reserves it. The
|
|
1707
|
+
* host creates no workflow and no batch for the list, and each cell is priced
|
|
1708
|
+
* again when the app submits it.
|
|
1709
|
+
*/
|
|
1710
|
+
export interface BlockEstimateBatchAggregate {
|
|
1711
|
+
/** Sum of `cost.total` over the priced cells, in Buzz. `0` when none priced. */
|
|
1712
|
+
total: number;
|
|
1713
|
+
/** How many cells priced and are therefore in `total`. */
|
|
1714
|
+
pricedCells: number;
|
|
1715
|
+
/** How many cells the host answered — the length of the list it was sent. */
|
|
1716
|
+
cellCount: number;
|
|
1717
|
+
}
|
|
1677
1718
|
/** {@link BlockWorkflowSnapshot.trainingQuote}. */
|
|
1678
1719
|
export interface BlockTrainingQuote {
|
|
1679
1720
|
/** `tq_` + 32 hex characters. Opaque — pass it back verbatim. */
|
|
@@ -1859,6 +1900,57 @@ export interface ManifestPreview {
|
|
|
1859
1900
|
description: string;
|
|
1860
1901
|
screenshots?: string[];
|
|
1861
1902
|
}
|
|
1903
|
+
/**
|
|
1904
|
+
* One declared property of a custom event — see {@link BlockManifestV1.analytics}.
|
|
1905
|
+
*
|
|
1906
|
+
* Exactly three types, and deliberately NO free-text string:
|
|
1907
|
+
*
|
|
1908
|
+
* - `enum` — a string drawn from `values`: 1 to 50 distinct, non-empty strings of
|
|
1909
|
+
* at most 64 characters (Unicode code points) each. A string that is not one of
|
|
1910
|
+
* the declared values will never be recorded.
|
|
1911
|
+
* - `number` — not range-limited, so do not use one to carry an identifier.
|
|
1912
|
+
* - `boolean`.
|
|
1913
|
+
*
|
|
1914
|
+
* No other key is allowed on a declaration (`values` only on an `enum`).
|
|
1915
|
+
* Mirrors the canonical schema's `analytics.events.*.properties.*`.
|
|
1916
|
+
*/
|
|
1917
|
+
export type BlockAnalyticsPropertyDeclaration = {
|
|
1918
|
+
type: 'enum';
|
|
1919
|
+
values: string[];
|
|
1920
|
+
} | {
|
|
1921
|
+
type: 'number';
|
|
1922
|
+
} | {
|
|
1923
|
+
type: 'boolean';
|
|
1924
|
+
};
|
|
1925
|
+
/**
|
|
1926
|
+
* One declared custom event — see {@link BlockManifestV1.analytics}. Mirrors the
|
|
1927
|
+
* canonical schema's `analytics.events.*`.
|
|
1928
|
+
*/
|
|
1929
|
+
export interface BlockAnalyticsEventDeclaration {
|
|
1930
|
+
/**
|
|
1931
|
+
* Optional human-readable meaning of the event, shown to reviewers and in
|
|
1932
|
+
* your analytics. At most 200 characters (Unicode code points).
|
|
1933
|
+
*/
|
|
1934
|
+
description?: string;
|
|
1935
|
+
/**
|
|
1936
|
+
* Property name → declaration. At most 10 properties per event. A property
|
|
1937
|
+
* name follows the same rule as an event name: lowercase snake_case, a letter
|
|
1938
|
+
* then up to 63 of `a-z`, `0-9` or `_` (`^[a-z][a-z0-9_]{0,63}$`).
|
|
1939
|
+
*/
|
|
1940
|
+
properties?: Record<string, BlockAnalyticsPropertyDeclaration>;
|
|
1941
|
+
}
|
|
1942
|
+
/**
|
|
1943
|
+
* The manifest's `analytics` object — see {@link BlockManifestV1.analytics}.
|
|
1944
|
+
* Mirrors the canonical schema's `analytics`; no other key is allowed.
|
|
1945
|
+
*/
|
|
1946
|
+
export interface BlockAnalyticsDeclaration {
|
|
1947
|
+
/**
|
|
1948
|
+
* Event name → declaration. At most 50 events. An event name is lowercase
|
|
1949
|
+
* snake_case: a letter, then up to 63 of `a-z`, `0-9` or `_`
|
|
1950
|
+
* (`^[a-z][a-z0-9_]{0,63}$`).
|
|
1951
|
+
*/
|
|
1952
|
+
events?: Record<string, BlockAnalyticsEventDeclaration>;
|
|
1953
|
+
}
|
|
1862
1954
|
/**
|
|
1863
1955
|
* One entry of a manifest's `goods[]`. Mirrors the canonical schema exactly;
|
|
1864
1956
|
* `id`, `title` and `priceBuzz` are required there and so are they here.
|
|
@@ -1978,6 +2070,26 @@ export interface BlockManifestV1 {
|
|
|
1978
2070
|
* Kept in lockstep with the canonical schema's `goods`.
|
|
1979
2071
|
*/
|
|
1980
2072
|
goods?: BlockManifestGood[];
|
|
2073
|
+
/**
|
|
2074
|
+
* Optional CUSTOM EVENTS declaration. `events` names every event the app may
|
|
2075
|
+
* send and the properties each event may carry. Manifest-governed and
|
|
2076
|
+
* REVIEW-GATED: the declaration a moderator approves is the only one the
|
|
2077
|
+
* platform accepts events against, so a reviewer can see what the app tracks.
|
|
2078
|
+
*
|
|
2079
|
+
* Properties are typed `enum` (a declared list of string values), `number` or
|
|
2080
|
+
* `boolean` ONLY — free-text strings are deliberately not supported, so a
|
|
2081
|
+
* string that is not one of an enum's declared values will never be recorded.
|
|
2082
|
+
* That is what keeps prompts, emails and usernames out of string properties.
|
|
2083
|
+
* Owners see aggregates only, never individual events.
|
|
2084
|
+
*
|
|
2085
|
+
* Limits (the canonical schema's, which this type cannot express): event and
|
|
2086
|
+
* property names match `^[a-z][a-z0-9_]{0,63}$`; at most 50 events and 10
|
|
2087
|
+
* properties per event; an enum declares 1 to 50 distinct non-empty values of
|
|
2088
|
+
* at most 64 code points; a `description` is at most 200 code points. The
|
|
2089
|
+
* platform's manifest validator is authoritative. Kept in lockstep with the
|
|
2090
|
+
* canonical schema's `analytics`.
|
|
2091
|
+
*/
|
|
2092
|
+
analytics?: BlockAnalyticsDeclaration;
|
|
1981
2093
|
/** Optional; the canonical declares no required sub-field. */
|
|
1982
2094
|
iframe?: ManifestIframe;
|
|
1983
2095
|
/** Full-page surface descriptor (W10). */
|
package/dist/index.d.ts
CHANGED
|
@@ -25,11 +25,11 @@
|
|
|
25
25
|
*
|
|
26
26
|
* - **`./blocks`** — importing it RUNS `./safe-storage` for its side effect
|
|
27
27
|
* (repairing `localStorage`/`sessionStorage` at an opaque origin). Folding
|
|
28
|
-
*
|
|
28
|
+
* 159 symbols and that side effect into the root would make every OAuth app
|
|
29
29
|
* that writes `import { exchangeCode } from '@civitai/app-sdk'` pay for the
|
|
30
30
|
* Civitai-Apps contract it does not use. Blocks and OAuth apps are disjoint
|
|
31
31
|
* audiences; see this package's README.
|
|
32
|
-
* 🔴 That
|
|
32
|
+
* 🔴 That 159 is ASSERTED, not merely quoted — by
|
|
33
33
|
* `packages/civitai-app-sdk/test/export-surface.test.ts`, which pins
|
|
34
34
|
* `surface.get('./blocks').size`. It said 119 here against a real 125 until
|
|
35
35
|
* 2026-10-04, because the existing checks pin the root as an exact union and
|
package/dist/index.js
CHANGED
|
@@ -25,11 +25,11 @@
|
|
|
25
25
|
*
|
|
26
26
|
* - **`./blocks`** — importing it RUNS `./safe-storage` for its side effect
|
|
27
27
|
* (repairing `localStorage`/`sessionStorage` at an opaque origin). Folding
|
|
28
|
-
*
|
|
28
|
+
* 159 symbols and that side effect into the root would make every OAuth app
|
|
29
29
|
* that writes `import { exchangeCode } from '@civitai/app-sdk'` pay for the
|
|
30
30
|
* Civitai-Apps contract it does not use. Blocks and OAuth apps are disjoint
|
|
31
31
|
* audiences; see this package's README.
|
|
32
|
-
* 🔴 That
|
|
32
|
+
* 🔴 That 159 is ASSERTED, not merely quoted — by
|
|
33
33
|
* `packages/civitai-app-sdk/test/export-surface.test.ts`, which pins
|
|
34
34
|
* `surface.get('./blocks').size`. It said 119 here against a real 125 until
|
|
35
35
|
* 2026-10-04, because the existing checks pin the root as an exact union and
|
|
@@ -45,6 +45,7 @@ import { readFileSync } from 'node:fs';
|
|
|
45
45
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
46
46
|
import { BlockManifestError } from '../blocks/manifestError.js';
|
|
47
47
|
import { BLOCK_SCOPES } from '../blocks/scopes.js';
|
|
48
|
+
import { preferDiscriminatedBranch } from './discriminatedBranch.js';
|
|
48
49
|
/**
|
|
49
50
|
* Rules `defineBlock` applies that the canonical schema does NOT express in
|
|
50
51
|
* machine-checkable form.
|
|
@@ -173,7 +174,11 @@ function canonicalValidator() {
|
|
|
173
174
|
// scaffold. The gap is recorded in KNOWN_GAPS, not shouted at the author on
|
|
174
175
|
// every boot. NOTE this silences Ajv's own diagnostics only; validation
|
|
175
176
|
// errors are returned in `validate.errors`, never logged.
|
|
176
|
-
|
|
177
|
+
//
|
|
178
|
+
// `verbose: true` puts the failed keyword's own `schema` and the offending
|
|
179
|
+
// `data` on each error. `preferDiscriminatedBranch` reads both off a failed
|
|
180
|
+
// `oneOf` / `anyOf`; it changes which error is REPORTED, never the verdict.
|
|
181
|
+
const ajv = new Ajv2020({ allErrors: true, strict: false, logger: false, verbose: true });
|
|
177
182
|
compiled = ajv.compile(loadCanonicalSchema());
|
|
178
183
|
return compiled;
|
|
179
184
|
}
|
|
@@ -194,12 +199,14 @@ function toDotPath(instancePath) {
|
|
|
194
199
|
}
|
|
195
200
|
const PASCAL_CASE_PATTERN = /^[A-Z][A-Za-z0-9]*$/;
|
|
196
201
|
/**
|
|
197
|
-
* Turns
|
|
198
|
-
* kept (it is derived from the
|
|
199
|
-
*
|
|
202
|
+
* Turns ONE Ajv error (the one {@link preferDiscriminatedBranch} picked) into a
|
|
203
|
+
* `BlockManifestError`. Ajv's own wording is kept (it is derived from the
|
|
204
|
+
* schema, so it cannot drift); only the field path is reshaped, and two cases
|
|
205
|
+
* get extra wording: an unknown key inside a discriminated branch says which
|
|
206
|
+
* branch, and a PascalCase scope gets a hint because it is the mistake the
|
|
200
207
|
* docs see most.
|
|
201
208
|
*/
|
|
202
|
-
function toManifestError(error, manifest) {
|
|
209
|
+
function toManifestError(error, manifest, discriminator) {
|
|
203
210
|
const base = toDotPath(error.instancePath);
|
|
204
211
|
if (error.keyword === 'required') {
|
|
205
212
|
const missing = error.params.missingProperty;
|
|
@@ -216,7 +223,10 @@ function toManifestError(error, manifest) {
|
|
|
216
223
|
}
|
|
217
224
|
if (error.keyword === 'additionalProperties') {
|
|
218
225
|
const extra = error.params.additionalProperty;
|
|
219
|
-
message =
|
|
226
|
+
message = discriminator
|
|
227
|
+
? `${shown}.${extra} is not a known property when \`${discriminator.key}\` is ` +
|
|
228
|
+
JSON.stringify(discriminator.value)
|
|
229
|
+
: `${shown}.${extra} is not a known property here`;
|
|
220
230
|
return new BlockManifestError(message, field === '' ? extra : `${field}.${extra}`);
|
|
221
231
|
}
|
|
222
232
|
// The one hint worth adding: block scopes are colon-separated lowercase, and
|
|
@@ -281,12 +291,11 @@ export function defineBlock(config) {
|
|
|
281
291
|
// whatever Ajv rejects, this rejects.
|
|
282
292
|
const validate = canonicalValidator();
|
|
283
293
|
if (!validate(manifest)) {
|
|
284
|
-
const
|
|
285
|
-
const first = errors[0];
|
|
294
|
+
const picked = preferDiscriminatedBranch(validate.errors ?? []);
|
|
286
295
|
/* c8 ignore next */
|
|
287
|
-
if (!
|
|
296
|
+
if (!picked)
|
|
288
297
|
throw new BlockManifestError('manifest failed schema validation');
|
|
289
|
-
throw toManifestError(
|
|
298
|
+
throw toManifestError(picked.error, manifest, picked.discriminator);
|
|
290
299
|
}
|
|
291
300
|
// (2) The prose-only server rejections. Strictly additive, one per
|
|
292
301
|
// SCHEMA_DIVERGENCES entry, in the table's order.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which validation error to REPORT when a `oneOf` / `anyOf` fails.
|
|
3
|
+
*
|
|
4
|
+
* INTERNAL to `./defineBlock` — not re-exported from `./index`, and typed
|
|
5
|
+
* structurally (no Ajv import) so it can be exercised against small synthetic
|
|
6
|
+
* schemas in `test/manifest/discriminated-branch.test.ts`.
|
|
7
|
+
*/
|
|
8
|
+
/** The fields of an Ajv `ErrorObject` this reads. `schema` and `data` need Ajv's `verbose: true`. */
|
|
9
|
+
export interface ValidationErrorLike {
|
|
10
|
+
keyword: string;
|
|
11
|
+
instancePath: string;
|
|
12
|
+
schemaPath: string;
|
|
13
|
+
schema?: unknown;
|
|
14
|
+
data?: unknown;
|
|
15
|
+
}
|
|
16
|
+
/** The discriminator that selected the member an error is reported from. */
|
|
17
|
+
export interface Discriminator {
|
|
18
|
+
key: string;
|
|
19
|
+
value: unknown;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Picks the error to REPORT. Ajv (with `allErrors`) validates every member of
|
|
23
|
+
* a failed `oneOf` / `anyOf` and lists all their errors in member order, so
|
|
24
|
+
* the plain first error is the FIRST MEMBER's complaint whichever member the
|
|
25
|
+
* author was writing: a `{ type: 'number', min: 0 }` property declaration used
|
|
26
|
+
* to be reported as "`values` is required", the `enum` member's rule.
|
|
27
|
+
*
|
|
28
|
+
* Where the members discriminate on a `const` (see {@link discriminatorKey})
|
|
29
|
+
* and the instance's value for that key equals exactly one member's, that
|
|
30
|
+
* member is the one the author meant: errors from the OTHER members, on that
|
|
31
|
+
* instance, are skipped, and the first error left is returned. (The summary
|
|
32
|
+
* `oneOf` / `anyOf` error needs no handling: Ajv lists it after the members'
|
|
33
|
+
* own errors.) `discriminator` is set when the returned error is on the
|
|
34
|
+
* discriminated object itself, so the message can say which member it is from.
|
|
35
|
+
*
|
|
36
|
+
* Any other failed composition — no shared `const` property, or a value no
|
|
37
|
+
* member names, or more than one member naming it — is left exactly as it
|
|
38
|
+
* was: the first error wins.
|
|
39
|
+
*
|
|
40
|
+
* 🔴 SELECTION ONLY. It is given the errors of a manifest Ajv has already
|
|
41
|
+
* rejected and returns one of them (the plain first if it would otherwise skip
|
|
42
|
+
* everything; `undefined` only for an empty list), so it cannot turn a
|
|
43
|
+
* rejection into an acceptance or the reverse.
|
|
44
|
+
*
|
|
45
|
+
* Members reached through `$ref` report a `schemaPath` relative to the
|
|
46
|
+
* referenced schema, so their errors are not recognised as a member's and are
|
|
47
|
+
* never skipped — that site degrades to "first error not from a recognised
|
|
48
|
+
* other member". The canonical uses no `$ref` today.
|
|
49
|
+
*/
|
|
50
|
+
export declare function preferDiscriminatedBranch<E extends ValidationErrorLike>(errors: E[]): {
|
|
51
|
+
error: E;
|
|
52
|
+
discriminator?: Discriminator;
|
|
53
|
+
} | undefined;
|
|
54
|
+
//# sourceMappingURL=discriminatedBranch.d.ts.map
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which validation error to REPORT when a `oneOf` / `anyOf` fails.
|
|
3
|
+
*
|
|
4
|
+
* INTERNAL to `./defineBlock` — not re-exported from `./index`, and typed
|
|
5
|
+
* structurally (no Ajv import) so it can be exercised against small synthetic
|
|
6
|
+
* schemas in `test/manifest/discriminated-branch.test.ts`.
|
|
7
|
+
*/
|
|
8
|
+
const isRecord = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
9
|
+
/** `{ value }` when `member.properties[key]` pins a primitive `const`, else `null`. */
|
|
10
|
+
function constOf(member, key) {
|
|
11
|
+
const prop = isRecord(member) && isRecord(member.properties) ? member.properties[key] : undefined;
|
|
12
|
+
if (!isRecord(prop) || !('const' in prop))
|
|
13
|
+
return null;
|
|
14
|
+
return typeof prop.const === 'object' && prop.const !== null ? null : { value: prop.const };
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The property every member of a `oneOf` / `anyOf` pins to its own `const` —
|
|
18
|
+
* `type` in `[{ properties: { type: { const: 'enum' } } }, { … 'number' }, …]` —
|
|
19
|
+
* or `undefined` when the members do not discriminate that way. Read from the
|
|
20
|
+
* schema, so nothing here names a manifest field.
|
|
21
|
+
*/
|
|
22
|
+
function discriminatorKey(members) {
|
|
23
|
+
const first = members[0];
|
|
24
|
+
if (!isRecord(first) || !isRecord(first.properties))
|
|
25
|
+
return undefined;
|
|
26
|
+
return Object.keys(first.properties).find((key) => members.every((member) => constOf(member, key) !== null));
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Picks the error to REPORT. Ajv (with `allErrors`) validates every member of
|
|
30
|
+
* a failed `oneOf` / `anyOf` and lists all their errors in member order, so
|
|
31
|
+
* the plain first error is the FIRST MEMBER's complaint whichever member the
|
|
32
|
+
* author was writing: a `{ type: 'number', min: 0 }` property declaration used
|
|
33
|
+
* to be reported as "`values` is required", the `enum` member's rule.
|
|
34
|
+
*
|
|
35
|
+
* Where the members discriminate on a `const` (see {@link discriminatorKey})
|
|
36
|
+
* and the instance's value for that key equals exactly one member's, that
|
|
37
|
+
* member is the one the author meant: errors from the OTHER members, on that
|
|
38
|
+
* instance, are skipped, and the first error left is returned. (The summary
|
|
39
|
+
* `oneOf` / `anyOf` error needs no handling: Ajv lists it after the members'
|
|
40
|
+
* own errors.) `discriminator` is set when the returned error is on the
|
|
41
|
+
* discriminated object itself, so the message can say which member it is from.
|
|
42
|
+
*
|
|
43
|
+
* Any other failed composition — no shared `const` property, or a value no
|
|
44
|
+
* member names, or more than one member naming it — is left exactly as it
|
|
45
|
+
* was: the first error wins.
|
|
46
|
+
*
|
|
47
|
+
* 🔴 SELECTION ONLY. It is given the errors of a manifest Ajv has already
|
|
48
|
+
* rejected and returns one of them (the plain first if it would otherwise skip
|
|
49
|
+
* everything; `undefined` only for an empty list), so it cannot turn a
|
|
50
|
+
* rejection into an acceptance or the reverse.
|
|
51
|
+
*
|
|
52
|
+
* Members reached through `$ref` report a `schemaPath` relative to the
|
|
53
|
+
* referenced schema, so their errors are not recognised as a member's and are
|
|
54
|
+
* never skipped — that site degrades to "first error not from a recognised
|
|
55
|
+
* other member". The canonical uses no `$ref` today.
|
|
56
|
+
*/
|
|
57
|
+
export function preferDiscriminatedBranch(errors) {
|
|
58
|
+
// One entry per member of each discriminated composition that failed: where
|
|
59
|
+
// its errors live (`prefix`), on which instance, and — for the member the
|
|
60
|
+
// instance's discriminator names — the discriminator (`chosen`).
|
|
61
|
+
const scopes = [];
|
|
62
|
+
for (const error of errors) {
|
|
63
|
+
if (error.keyword !== 'oneOf' && error.keyword !== 'anyOf')
|
|
64
|
+
continue;
|
|
65
|
+
const members = error.schema;
|
|
66
|
+
if (!Array.isArray(members) || !isRecord(error.data))
|
|
67
|
+
continue;
|
|
68
|
+
const key = discriminatorKey(members);
|
|
69
|
+
if (key === undefined)
|
|
70
|
+
continue;
|
|
71
|
+
const value = error.data[key];
|
|
72
|
+
const matching = members.flatMap((member, index) => constOf(member, key)?.value === value ? [index] : []);
|
|
73
|
+
if (matching.length !== 1)
|
|
74
|
+
continue;
|
|
75
|
+
members.forEach((_member, index) => {
|
|
76
|
+
scopes.push({
|
|
77
|
+
prefix: `${error.schemaPath}/${index}/`,
|
|
78
|
+
instancePath: error.instancePath,
|
|
79
|
+
...(index === matching[0] ? { chosen: { key, value } } : {}),
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
for (const error of errors) {
|
|
84
|
+
// Compositions nest, so an error can sit inside several members at once.
|
|
85
|
+
const within = scopes.filter((s) => error.schemaPath.startsWith(s.prefix) &&
|
|
86
|
+
(error.instancePath === s.instancePath ||
|
|
87
|
+
error.instancePath.startsWith(`${s.instancePath}/`)));
|
|
88
|
+
// Inside a member, at any level, that its instance's discriminator does not name.
|
|
89
|
+
if (within.some((s) => !s.chosen))
|
|
90
|
+
continue;
|
|
91
|
+
// The member is named in the message only for an error ON the discriminated
|
|
92
|
+
// object itself (the innermost one), not for one on something nested inside it.
|
|
93
|
+
const innermost = within.reduce((a, b) => (a && a.prefix.length >= b.prefix.length ? a : b), undefined);
|
|
94
|
+
return innermost?.chosen && error.instancePath === innermost.instancePath
|
|
95
|
+
? { error, discriminator: innermost.chosen }
|
|
96
|
+
: { error };
|
|
97
|
+
}
|
|
98
|
+
return errors[0] ? { error: errors[0] } : undefined;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=discriminatedBranch.js.map
|
package/dist/manifest/index.d.ts
CHANGED
|
@@ -15,5 +15,5 @@
|
|
|
15
15
|
export { defineBlock, SCHEMA_DIVERGENCES, KNOWN_GAPS, loadCanonicalSchema } from './defineBlock.js';
|
|
16
16
|
export type { DefineBlockConfig } from './defineBlock.js';
|
|
17
17
|
export { BlockManifestError } from '../blocks/manifestError.js';
|
|
18
|
-
export type { BlockManifest, BlockManifestGood, BlockManifestV1 } from '../blocks/types.js';
|
|
18
|
+
export type { BlockAnalyticsDeclaration, BlockAnalyticsEventDeclaration, BlockAnalyticsPropertyDeclaration, BlockManifest, BlockManifestGood, BlockManifestV1, } from '../blocks/types.js';
|
|
19
19
|
//# sourceMappingURL=index.d.ts.map
|
package/package.json
CHANGED
|
@@ -275,6 +275,104 @@
|
|
|
275
275
|
}
|
|
276
276
|
}
|
|
277
277
|
},
|
|
278
|
+
"analytics": {
|
|
279
|
+
"type": "object",
|
|
280
|
+
"description": "Optional CUSTOM EVENTS declaration. `events` names every event your app may send with `useBlockAnalytics().track(name, properties)` and the properties each event may carry. Manifest-governed and REVIEW-GATED: the declaration a moderator approves is the only one the platform will accept events against, so a reviewer can see what your app tracks. Properties are typed `enum` (a declared list of string values), `number` or `boolean` ONLY — free-text strings are deliberately not supported, so a string that is not one of an enum's declared values will never be recorded. That is what keeps prompts, emails and usernames out of string properties (a `number` property is not range-limited, so do not use one to carry an identifier). Owners will see aggregates only, never individual events. The platform's manifest validator is authoritative.",
|
|
281
|
+
"additionalProperties": false,
|
|
282
|
+
"properties": {
|
|
283
|
+
"events": {
|
|
284
|
+
"type": "object",
|
|
285
|
+
"description": "Map of event name → declaration. At most 50 events. An event name is lowercase snake_case: a letter, then up to 63 of a-z, 0-9 or _.",
|
|
286
|
+
"maxProperties": 50,
|
|
287
|
+
"additionalProperties": false,
|
|
288
|
+
"patternProperties": {
|
|
289
|
+
"^[a-z][a-z0-9_]{0,63}$": {
|
|
290
|
+
"type": "object",
|
|
291
|
+
"additionalProperties": false,
|
|
292
|
+
"properties": {
|
|
293
|
+
"description": {
|
|
294
|
+
"type": "string",
|
|
295
|
+
"description": "Optional human-readable meaning of the event, shown to reviewers and in your analytics.",
|
|
296
|
+
"maxLength": 200
|
|
297
|
+
},
|
|
298
|
+
"properties": {
|
|
299
|
+
"type": "object",
|
|
300
|
+
"description": "Map of property name → declaration. At most 10 properties per event. A property name follows the same rule as an event name.",
|
|
301
|
+
"maxProperties": 10,
|
|
302
|
+
"additionalProperties": false,
|
|
303
|
+
"patternProperties": {
|
|
304
|
+
"^[a-z][a-z0-9_]{0,63}$": {
|
|
305
|
+
"type": "object",
|
|
306
|
+
"allOf": [
|
|
307
|
+
{
|
|
308
|
+
"$comment": "Checked FIRST, before the oneOf below, so a wrong type is reported as such rather than as a missing `values`.",
|
|
309
|
+
"required": ["type"],
|
|
310
|
+
"properties": {
|
|
311
|
+
"type": {
|
|
312
|
+
"description": "The property's type. There is no free-text string type: declare an enum.",
|
|
313
|
+
"enum": ["enum", "number", "boolean"]
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
"oneOf": [
|
|
319
|
+
{
|
|
320
|
+
"type": "object",
|
|
321
|
+
"description": "A string drawn from a declared list. A string outside `values` will not be recorded.",
|
|
322
|
+
"additionalProperties": false,
|
|
323
|
+
"required": ["type", "values"],
|
|
324
|
+
"properties": {
|
|
325
|
+
"type": {
|
|
326
|
+
"const": "enum"
|
|
327
|
+
},
|
|
328
|
+
"values": {
|
|
329
|
+
"type": "array",
|
|
330
|
+
"description": "The allowed values: 1 to 50 distinct non-empty strings of at most 64 characters (Unicode code points) each.",
|
|
331
|
+
"minItems": 1,
|
|
332
|
+
"maxItems": 50,
|
|
333
|
+
"uniqueItems": true,
|
|
334
|
+
"items": {
|
|
335
|
+
"type": "string",
|
|
336
|
+
"minLength": 1,
|
|
337
|
+
"maxLength": 64
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
"type": "object",
|
|
344
|
+
"description": "A number.",
|
|
345
|
+
"additionalProperties": false,
|
|
346
|
+
"required": ["type"],
|
|
347
|
+
"properties": {
|
|
348
|
+
"type": {
|
|
349
|
+
"const": "number"
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
"type": "object",
|
|
355
|
+
"description": "A boolean.",
|
|
356
|
+
"additionalProperties": false,
|
|
357
|
+
"required": ["type"],
|
|
358
|
+
"properties": {
|
|
359
|
+
"type": {
|
|
360
|
+
"const": "boolean"
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
]
|
|
365
|
+
}
|
|
366
|
+
]
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
},
|
|
278
376
|
"targets": {
|
|
279
377
|
"type": "array",
|
|
280
378
|
"description": "Model-page slot targets. Each target's slotId must be a known registered model slot (not the page slot). Optional for page-only apps.",
|