@phosphorco/bb-cross-references 0.0.0-stage → 0.1.1
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/CONTRACT.md +109 -0
- package/LICENSE +21 -0
- package/README.md +41 -2
- package/dist/bb.js +665 -0
- package/dist/index.js +649 -0
- package/dist/react.js +1308 -0
- package/dist/references.css +66 -0
- package/dist/testing.js +48 -0
- package/dist/types/bb.d.ts +29 -0
- package/dist/types/canonical.d.ts +65 -0
- package/dist/types/index.d.ts +534 -0
- package/dist/types/react.d.ts +32 -0
- package/dist/types/testing.d.ts +14 -0
- package/package.json +84 -4
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
|
|
2
|
+
.bb-reference-links__references { display: grid; gap: 10px; margin-top: 10px; padding: 12px; border: 1px solid var(--border); border-radius: 9px; background: var(--card); color: var(--card-foreground); }
|
|
3
|
+
|
|
4
|
+
.bb-reference-links__references > header { display: flex; align-items: start; justify-content: space-between; gap: 12px; }
|
|
5
|
+
|
|
6
|
+
.bb-reference-links__references h2 { font-size: 12px; font-weight: 650; }
|
|
7
|
+
|
|
8
|
+
.bb-reference-links__references header p, .bb-reference-links__reference-help, .bb-reference-links__reference-status, .bb-reference-links__reference-empty, .bb-reference-links__reference-error { color: var(--muted-foreground); font-size: 10px; }
|
|
9
|
+
|
|
10
|
+
.bb-reference-links__references header p { margin-top: 3px; max-width: 620px; }
|
|
11
|
+
|
|
12
|
+
.bb-reference-links__reference-count { min-width: 1.75rem; padding: 3px 6px; border-radius: 999px; color: var(--primary-foreground); background: var(--primary); font-size: 10px; font-variant-numeric: tabular-nums; text-align: center; }
|
|
13
|
+
|
|
14
|
+
.bb-reference-links__reference-status { margin: 0; }
|
|
15
|
+
|
|
16
|
+
.bb-reference-links__reference-status--pending { color: var(--primary); }
|
|
17
|
+
|
|
18
|
+
.bb-reference-links__reference-status--degraded, .bb-reference-links__reference-status--blocked, .bb-reference-links__reference-error { color: var(--destructive); }
|
|
19
|
+
|
|
20
|
+
.bb-reference-links__reference-empty { margin: 0; padding: 8px; border-radius: 6px; background: color-mix(in srgb, var(--muted) 36%, transparent); }
|
|
21
|
+
|
|
22
|
+
.bb-reference-links__reference-list, .bb-reference-links__reference-search-results { display: grid; gap: 6px; margin: 0; padding: 0; list-style: none; }
|
|
23
|
+
|
|
24
|
+
.bb-reference-links__reference-list li, .bb-reference-links__reference-search-results li { display: flex; align-items: center; justify-content: space-between; gap: 10px; min-width: 0; padding: 7px 8px; border-radius: 6px; background: color-mix(in srgb, var(--muted) 36%, transparent); }
|
|
25
|
+
|
|
26
|
+
.bb-reference-links__reference-list li > span, .bb-reference-links__reference-search-results li > span { display: grid; min-width: 0; gap: 2px; }
|
|
27
|
+
|
|
28
|
+
.bb-reference-links__reference-list small, .bb-reference-links__reference-search-results small { overflow: hidden; color: var(--muted-foreground); font-size: 10px; text-overflow: ellipsis; white-space: nowrap; }
|
|
29
|
+
|
|
30
|
+
.bb-reference-links__reference-link { min-width: 0; overflow: hidden; color: var(--primary); font-size: 12px; font-weight: 600; text-overflow: ellipsis; white-space: nowrap; }
|
|
31
|
+
|
|
32
|
+
.bb-reference-links__reference-picker { display: grid; gap: 4px; padding-top: 2px; }
|
|
33
|
+
|
|
34
|
+
.bb-reference-links__reference-picker > label { color: var(--foreground); font-size: 11px; }
|
|
35
|
+
|
|
36
|
+
.bb-reference-links__reference-picker input { width: 100%; min-height: 32px; padding: 0 9px; border: 1px solid var(--input); border-radius: 7px; color: var(--foreground); background: var(--background); font: inherit; }
|
|
37
|
+
|
|
38
|
+
.bb-reference-links__reference-picker input:focus-visible, .bb-reference-links__reference-list button:focus-visible, .bb-reference-links__reference-search-results button:focus-visible, .bb-reference-links__reference-external button:focus-visible, .bb-reference-links__reference-retry:focus-visible, .bb-reference-links__reference-link:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
|
|
39
|
+
|
|
40
|
+
.bb-reference-links__reference-help { margin: 0; }
|
|
41
|
+
|
|
42
|
+
.bb-reference-links__reference-error { margin: 0; overflow-wrap: anywhere; }
|
|
43
|
+
|
|
44
|
+
.bb-reference-links__reference-search-results { max-height: 230px; overflow-y: auto; overscroll-behavior: contain; }
|
|
45
|
+
|
|
46
|
+
.bb-reference-links__reference-search-results strong { min-width: 0; overflow: hidden; font-size: 11px; text-overflow: ellipsis; white-space: nowrap; }
|
|
47
|
+
|
|
48
|
+
.bb-reference-links__reference-list button, .bb-reference-links__reference-search-results button, .bb-reference-links__reference-external button, .bb-reference-links__reference-retry { flex: 0 0 auto; min-height: 28px; padding: 4px 8px; border: 1px solid var(--border); border-radius: 6px; color: var(--foreground); background: var(--background); font: inherit; font-size: 10px; cursor: pointer; }
|
|
49
|
+
|
|
50
|
+
.bb-reference-links__reference-search-results button:not(:disabled):hover, .bb-reference-links__reference-list button:not(:disabled):hover, .bb-reference-links__reference-external button:not(:disabled):hover, .bb-reference-links__reference-retry:not(:disabled):hover { background: var(--accent); color: var(--accent-foreground); }
|
|
51
|
+
|
|
52
|
+
.bb-reference-links__reference-list button:disabled, .bb-reference-links__reference-search-results button:disabled, .bb-reference-links__reference-external button:disabled, .bb-reference-links__reference-retry:disabled { cursor: default; opacity: .58; }
|
|
53
|
+
|
|
54
|
+
.bb-reference-links__reference-add { color: var(--primary); }
|
|
55
|
+
|
|
56
|
+
.bb-reference-links__reference-remove { color: var(--destructive) !important; }
|
|
57
|
+
|
|
58
|
+
.bb-reference-links__reference-external { display: grid; gap: 6px; padding: 8px; border-radius: 6px; background: color-mix(in srgb, var(--muted) 36%, transparent); }
|
|
59
|
+
|
|
60
|
+
.bb-reference-links__reference-external > span { display: grid; min-width: 0; gap: 2px; }
|
|
61
|
+
|
|
62
|
+
.bb-reference-links__reference-external strong, .bb-reference-links__reference-external > label { font-size: 11px; }
|
|
63
|
+
|
|
64
|
+
.bb-reference-links__reference-external small { overflow: hidden; color: var(--muted-foreground); font-size: 10px; text-overflow: ellipsis; white-space: nowrap; }
|
|
65
|
+
|
|
66
|
+
.bb-reference-links__reference-external > div { display: flex; align-items: center; gap: 6px; }
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// src/testing.ts
|
|
2
|
+
import { canonicalizeIdentity, normalizeProjectionCommand, resourceIdentityOf } from "./index.js";
|
|
3
|
+
import { createCrossReferencesClient } from "./bb.js";
|
|
4
|
+
async function runCrossReferencesConformance(options) {
|
|
5
|
+
const command = normalizeProjectionCommand(options.command);
|
|
6
|
+
if (command.tombstone || command.targets.length === 0)
|
|
7
|
+
throw new Error("Conformance requires an active projection with targets.");
|
|
8
|
+
const client = createCrossReferencesClient(options.sdk, options.pluginId);
|
|
9
|
+
const checks = [];
|
|
10
|
+
await client.describe();
|
|
11
|
+
checks.push("describe-or-legacy-v1");
|
|
12
|
+
const applied = await client.applyProjection(options.command);
|
|
13
|
+
if (!["applied", "duplicate", "equal"].includes(applied.outcome) || applied.currentRevision !== command.revision || applied.currentDigest !== command.payloadDigest) {
|
|
14
|
+
throw new Error("applyProjection did not accept the fixture revision and digest.");
|
|
15
|
+
}
|
|
16
|
+
checks.push("apply-acceptance");
|
|
17
|
+
const duplicate = await client.applyProjection(options.command);
|
|
18
|
+
if (duplicate.outcome !== "duplicate" || duplicate.currentRevision !== command.revision || duplicate.currentDigest !== command.payloadDigest)
|
|
19
|
+
throw new Error("Identical mutation was not an exact duplicate.");
|
|
20
|
+
checks.push("duplicate-acceptance");
|
|
21
|
+
const source = resourceIdentityOf(command.source);
|
|
22
|
+
const { projection } = await client.getProjection({ producerPluginId: command.producerPluginId, source });
|
|
23
|
+
if (projection === null || projection.revision !== command.revision || projection.mutationId !== command.mutationId || projection.payloadDigest !== command.payloadDigest || projection.tombstone || projection.producerPluginId !== command.producerPluginId) {
|
|
24
|
+
throw new Error("getProjection did not preserve the accepted fixture.");
|
|
25
|
+
}
|
|
26
|
+
const readCommand = normalizeProjectionCommand({ ...projection, protocolVersion: 1, expectedRevision: command.expectedRevision });
|
|
27
|
+
if (readCommand.payloadJson !== command.payloadJson)
|
|
28
|
+
throw new Error("getProjection changed the accepted source, targets or presentation.");
|
|
29
|
+
checks.push("get-acceptance");
|
|
30
|
+
for (const target of command.targets) {
|
|
31
|
+
let cursor;
|
|
32
|
+
let found = false;
|
|
33
|
+
for (let page = 0;page < 10; page += 1) {
|
|
34
|
+
const backlinks = await client.listBacklinks({ target: resourceIdentityOf(target), pageSize: 100, ...cursor === undefined ? {} : { cursor } });
|
|
35
|
+
found = backlinks.rows.some((row) => row.producerPluginId === command.producerPluginId && row.revision === command.revision && canonicalizeIdentity({ provider: row.source.provider, keys: row.source.keys }).canonicalIdentityJson === command.source.canonicalIdentityJson);
|
|
36
|
+
if (found || backlinks.nextCursor === null)
|
|
37
|
+
break;
|
|
38
|
+
cursor = backlinks.nextCursor;
|
|
39
|
+
}
|
|
40
|
+
if (!found)
|
|
41
|
+
throw new Error("listBacklinks did not include the accepted source and producer.");
|
|
42
|
+
}
|
|
43
|
+
checks.push("backlinks-acceptance");
|
|
44
|
+
return { level: "source-conformance", pluginId: client.pluginId, checks };
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
runCrossReferencesConformance
|
|
48
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { BbPluginApi } from "@get-bb/plugin-sdk";
|
|
2
|
+
import type { PluginBrowserBbSdk } from "@get-bb/plugin-sdk/app";
|
|
3
|
+
import { CROSS_REFERENCES_PROTOCOL, type ApplyProjectionInput, type ApplyProjectionResponse, type GetProjectionInput, type GetProjectionResponse, type ListBacklinksInput, type ListBacklinksResponse, type ListForwardReferencesInput, type ListForwardReferencesResponse, type CheckForwardReferencesInput, type ForwardReferenceStatus } from "./index.js";
|
|
4
|
+
export type CrossReferencesSdk = Pick<BbPluginApi["sdk"], "plugins"> | Pick<PluginBrowserBbSdk, "plugins">;
|
|
5
|
+
export interface CrossReferencesDescription {
|
|
6
|
+
protocol: typeof CROSS_REFERENCES_PROTOCOL;
|
|
7
|
+
version: 1;
|
|
8
|
+
legacy: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface CrossReferencesClient {
|
|
11
|
+
readonly pluginId: string;
|
|
12
|
+
describe(signal?: AbortSignal): Promise<CrossReferencesDescription>;
|
|
13
|
+
applyProjection(input: ApplyProjectionInput, signal?: AbortSignal): Promise<ApplyProjectionResponse>;
|
|
14
|
+
getProjection(input: GetProjectionInput, signal?: AbortSignal): Promise<GetProjectionResponse>;
|
|
15
|
+
listBacklinks(input: ListBacklinksInput, signal?: AbortSignal): Promise<ListBacklinksResponse>;
|
|
16
|
+
listForwardReferences(input: ListForwardReferencesInput, signal?: AbortSignal): Promise<ListForwardReferencesResponse>;
|
|
17
|
+
checkForwardReferences(input: CheckForwardReferencesInput, signal?: AbortSignal): Promise<ForwardReferenceStatus[]>;
|
|
18
|
+
}
|
|
19
|
+
export declare class CrossReferencesProtocolError extends Error {
|
|
20
|
+
readonly code = "incompatible_protocol";
|
|
21
|
+
constructor(message: string);
|
|
22
|
+
}
|
|
23
|
+
export interface CrossReferencesClientOptions {
|
|
24
|
+
/** Budgets may only lower the package's published upper bounds. */
|
|
25
|
+
callMs?: number;
|
|
26
|
+
describeMs?: number;
|
|
27
|
+
}
|
|
28
|
+
/** Explicit target, no hooks, storage, scheduling, retry or singleton. */
|
|
29
|
+
export declare function createCrossReferencesClient(sdk: CrossReferencesSdk, targetPluginId?: string, options?: CrossReferencesClientOptions): CrossReferencesClient;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export declare const PROTOCOL_VERSION: 1;
|
|
2
|
+
export declare const MAX_RESOURCE_KEYS = 32;
|
|
3
|
+
export declare const MAX_KEY_VALUE_BYTES = 512;
|
|
4
|
+
export declare const MAX_KEY_VALUE_MATERIAL_BYTES = 8192;
|
|
5
|
+
export declare const MAX_CANONICAL_IDENTITY_BYTES: number;
|
|
6
|
+
export declare const MAX_PRESENTATION_BYTES: number;
|
|
7
|
+
export declare const MAX_PRESENTATION_LABEL_BYTES = 256;
|
|
8
|
+
export declare const MAX_PRESENTATION_DETAIL_BYTES = 1024;
|
|
9
|
+
export declare const MAX_PRESENTATION_URL_BYTES = 2048;
|
|
10
|
+
export declare const MAX_TARGETS = 256;
|
|
11
|
+
export declare const MAX_PROJECTION_PAYLOAD_BYTES: number;
|
|
12
|
+
export declare const MAX_MUTATION_ID_BYTES = 128;
|
|
13
|
+
export declare const MACHINE_MONITOR_ROUTE = "/plugins/machine-monitor/machine-monitor";
|
|
14
|
+
export type Presentation = {
|
|
15
|
+
label: string;
|
|
16
|
+
detail?: string;
|
|
17
|
+
url?: string;
|
|
18
|
+
};
|
|
19
|
+
export type ResourceIdentity = {
|
|
20
|
+
provider: string;
|
|
21
|
+
keys: Record<string, string>;
|
|
22
|
+
};
|
|
23
|
+
export type Resource = ResourceIdentity & {
|
|
24
|
+
presentation: Presentation;
|
|
25
|
+
};
|
|
26
|
+
export interface CanonicalIdentity extends ResourceIdentity {
|
|
27
|
+
canonicalKeysJson: string;
|
|
28
|
+
canonicalIdentityJson: string;
|
|
29
|
+
identityDigest: string;
|
|
30
|
+
}
|
|
31
|
+
export interface CanonicalResource extends Resource, CanonicalIdentity {
|
|
32
|
+
presentationJson: string;
|
|
33
|
+
}
|
|
34
|
+
export declare class CrossReferenceValidationError extends Error {
|
|
35
|
+
constructor(message: string);
|
|
36
|
+
}
|
|
37
|
+
export declare function utf8ByteLength(value: string): number;
|
|
38
|
+
export declare function validateBbId(value: unknown, label: string): string;
|
|
39
|
+
export declare function validateProducerPluginId(value: unknown): string;
|
|
40
|
+
export declare function validateMutationId(value: unknown): string;
|
|
41
|
+
export declare function validateRevision(value: unknown, label: string, minimum: number): number;
|
|
42
|
+
export declare function validateDigest(value: unknown, label?: string): string;
|
|
43
|
+
export declare function canonicalizeIdentity(value: ResourceIdentity): CanonicalIdentity;
|
|
44
|
+
export declare function canonicalizeResource(value: Resource): CanonicalResource;
|
|
45
|
+
export declare function resourceIdentityOf(resource: CanonicalResource): ResourceIdentity;
|
|
46
|
+
/** The installation-local BB identity conventions used by the first slice. */
|
|
47
|
+
export declare function projectIdentity(projectId: string): ResourceIdentity;
|
|
48
|
+
export declare function threadIdentity(projectId: string, threadId: string): ResourceIdentity;
|
|
49
|
+
export declare function machineMonitorIdentity(): ResourceIdentity;
|
|
50
|
+
export declare function projectResource(projectId: string, presentation: Presentation): Resource;
|
|
51
|
+
export declare function threadResource(projectId: string, threadId: string, presentation: Presentation): Resource;
|
|
52
|
+
export declare function machineMonitorResource(presentation?: Presentation): Resource;
|
|
53
|
+
export declare function serializeResource(resource: CanonicalResource): Resource;
|
|
54
|
+
export declare function sha256Hex(value: string): string;
|
|
55
|
+
export declare function projectionPayloadJson(producerPluginId: string, source: CanonicalResource, tombstone: boolean, targets: readonly CanonicalResource[]): string;
|
|
56
|
+
export declare function projectionPayloadDigest(producerPluginId: string, source: CanonicalResource, tombstone: boolean, targets: readonly CanonicalResource[]): string;
|
|
57
|
+
/** Exact v1 thread shape, after canonicalization has sorted its keys. */
|
|
58
|
+
export declare function isExactBbThreadResource(resource: CanonicalResource): boolean;
|
|
59
|
+
export declare function isUrlResource(resource: CanonicalResource): boolean;
|
|
60
|
+
export declare function normalizeThreadOrUrlReference(target: Resource): CanonicalResource;
|
|
61
|
+
export declare function urlResource(href: string, presentation: Omit<Presentation, "url">): Resource;
|
|
62
|
+
export declare const MAX_REFERENCE_URL_BYTES = 512;
|
|
63
|
+
export declare const MAX_REFERENCE_LABEL_BYTES = 256;
|
|
64
|
+
export declare function hasSensitiveUrlParameter(url: URL): boolean;
|
|
65
|
+
export declare function referenceLabelError(value: string): string | null;
|