@cosmoops/connect-sdk 0.30.1 → 0.32.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/CHANGELOG.md +127 -0
- package/dist/cjs/client/index.js +10 -3
- package/dist/cjs/client/use-push.js +139 -0
- package/dist/cjs/server/client.js +8 -1
- package/dist/cjs/server/handler.js +54 -0
- package/dist/cjs/server/resources/ai.js +35 -0
- package/dist/cjs/server/resources/enrichment.js +48 -0
- package/dist/cjs/server/resources/push.js +92 -0
- package/dist/esm/client/index.js +6 -0
- package/dist/esm/client/use-push.js +136 -0
- package/dist/esm/server/client.js +8 -1
- package/dist/esm/server/handler.js +54 -0
- package/dist/esm/server/resources/ai.js +35 -0
- package/dist/esm/server/resources/enrichment.js +48 -0
- package/dist/esm/server/resources/push.js +88 -0
- package/dist/types/client/index.d.ts +7 -0
- package/dist/types/client/use-push.d.ts +56 -0
- package/dist/types/server/client.d.ts +7 -1
- package/dist/types/server/resources/ai.d.ts +28 -1
- package/dist/types/server/resources/enrichment.d.ts +46 -1
- package/dist/types/server/resources/push.d.ts +73 -0
- package/dist/types/types.d.ts +270 -0
- package/package.json +1 -1
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { type TransportOptions } from "../http.js";
|
|
2
|
+
import type { PushKeyResponse, RegisterPushDeviceInput, RegisterPushDeviceResponse, SendPushInput, SendPushResponse } from "../../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Web Push, with Connect holding everything that makes it work.
|
|
5
|
+
*
|
|
6
|
+
* Your application supplies a title, a body, a URL and the members to reach.
|
|
7
|
+
* Connect owns the VAPID keys it signs with, the devices each member has
|
|
8
|
+
* registered across every application on the platform, that member's own
|
|
9
|
+
* notification preferences, and the bookkeeping when a browser goes away.
|
|
10
|
+
*
|
|
11
|
+
* So you never hold a push credential, never store an endpoint, and cannot
|
|
12
|
+
* reach anybody who is not an active member of the workspace you name -
|
|
13
|
+
* membership is re-checked on every send rather than trusted from the ids you
|
|
14
|
+
* pass.
|
|
15
|
+
*
|
|
16
|
+
* ## Two halves, and only `send` belongs on your server
|
|
17
|
+
*
|
|
18
|
+
* Registering a device happens in the **browser**, because a subscription is
|
|
19
|
+
* produced by `pushManager.subscribe()` against the member's own browser and
|
|
20
|
+
* no server can make one for them. `registerDevice` and `revokeDevice` below
|
|
21
|
+
* therefore take a session token and are what the SDK bridge calls on the
|
|
22
|
+
* browser's behalf - see `createConnectHandler`. You will normally reach them
|
|
23
|
+
* through `useConnectPush()` in `@cosmoops/connect-sdk/client` rather than
|
|
24
|
+
* directly.
|
|
25
|
+
*/
|
|
26
|
+
export declare class PushResource {
|
|
27
|
+
private readonly transport;
|
|
28
|
+
constructor(transport: TransportOptions);
|
|
29
|
+
/**
|
|
30
|
+
* Notify members of a workspace.
|
|
31
|
+
*
|
|
32
|
+
* **`delivered: 0` is a success, not a failure.** The member may have
|
|
33
|
+
* registered no device, silenced notifications from their apps, or denied
|
|
34
|
+
* the browser prompt - all ordinary. A caller that retries on zero will
|
|
35
|
+
* retry forever against somebody who has opted out.
|
|
36
|
+
*
|
|
37
|
+
* Treat every send as a nudge toward something already true in your own
|
|
38
|
+
* data, and never let a failure here fail the thing that triggered it: the
|
|
39
|
+
* reminder is worth less than the record it is about.
|
|
40
|
+
*
|
|
41
|
+
* `url` must be absolute and on an origin this application registered as an
|
|
42
|
+
* SSO redirect URI. Connect refuses anything else, because a URL it did not
|
|
43
|
+
* check is an open redirect delivered to a lock screen.
|
|
44
|
+
*/
|
|
45
|
+
send(input: SendPushInput): Promise<SendPushResponse>;
|
|
46
|
+
/**
|
|
47
|
+
* The VAPID public key a browser needs before it can subscribe, and whether
|
|
48
|
+
* the platform can send at all.
|
|
49
|
+
*
|
|
50
|
+
* Safe to hand to any signed-in caller: it is the verification half of the
|
|
51
|
+
* pair, so holding it lets a browser *receive* what Connect signs and never
|
|
52
|
+
* send anything.
|
|
53
|
+
*/
|
|
54
|
+
key(options: {
|
|
55
|
+
asUser: string;
|
|
56
|
+
}): Promise<PushKeyResponse>;
|
|
57
|
+
/** Register one browser, from a subscription that browser just produced. */
|
|
58
|
+
registerDevice(input: RegisterPushDeviceInput, options: {
|
|
59
|
+
asUser: string;
|
|
60
|
+
}): Promise<RegisterPushDeviceResponse>;
|
|
61
|
+
/**
|
|
62
|
+
* Stop notifications on one browser.
|
|
63
|
+
*
|
|
64
|
+
* The id is the one `registerDevice` returned. Connect re-checks that it
|
|
65
|
+
* belongs to the calling member, so a guessed id is a refusal rather than
|
|
66
|
+
* somebody else's device going quiet.
|
|
67
|
+
*/
|
|
68
|
+
revokeDevice(id: string, options: {
|
|
69
|
+
asUser: string;
|
|
70
|
+
}): Promise<{
|
|
71
|
+
revoked: boolean;
|
|
72
|
+
}>;
|
|
73
|
+
}
|
package/dist/types/types.d.ts
CHANGED
|
@@ -586,6 +586,54 @@ export interface AiConversationDetail {
|
|
|
586
586
|
conversation: AiConversation;
|
|
587
587
|
turns: AiConversationTurn[];
|
|
588
588
|
}
|
|
589
|
+
/**
|
|
590
|
+
* One question in an assessment, as the platform defines it. Echoed back on a
|
|
591
|
+
* draft so the questions can be rendered without a second round trip.
|
|
592
|
+
*/
|
|
593
|
+
export interface AssessmentSection {
|
|
594
|
+
/** Stable identifier - the key answers are filed under. */
|
|
595
|
+
key: string;
|
|
596
|
+
/** The question as a person reads it. */
|
|
597
|
+
question: string;
|
|
598
|
+
/** What belongs here. Written to steer generation, but useful as helper text. */
|
|
599
|
+
hint?: string;
|
|
600
|
+
/** Fewest answers a draft offers. */
|
|
601
|
+
min?: number;
|
|
602
|
+
/** Most answers a draft offers, and the cap worth enforcing on edits. */
|
|
603
|
+
max?: number;
|
|
604
|
+
}
|
|
605
|
+
/** Answers, keyed by section key. */
|
|
606
|
+
export type AssessmentAnswers = Record<string, string[]>;
|
|
607
|
+
export interface AssessmentDraft {
|
|
608
|
+
assessmentKey: string;
|
|
609
|
+
title: string;
|
|
610
|
+
/** The definition's sections, in the order they should be asked. */
|
|
611
|
+
sections: AssessmentSection[];
|
|
612
|
+
/** Suggested answers - a strawman to edit, never a finished answer. */
|
|
613
|
+
answers: AssessmentAnswers;
|
|
614
|
+
}
|
|
615
|
+
export type AssessmentFindingSeverity = "critical" | "warning" | "note";
|
|
616
|
+
export interface AssessmentFinding {
|
|
617
|
+
severity: AssessmentFindingSeverity;
|
|
618
|
+
/** Section keys this finding concerns - usually two, since the useful findings are tensions between sections. */
|
|
619
|
+
sections: string[];
|
|
620
|
+
title: string;
|
|
621
|
+
detail: string;
|
|
622
|
+
/** The specific change to make. */
|
|
623
|
+
fix: string;
|
|
624
|
+
}
|
|
625
|
+
export interface AssessmentReview {
|
|
626
|
+
assessmentKey: string;
|
|
627
|
+
findings: AssessmentFinding[];
|
|
628
|
+
/** The one assumption everything rests on, and a cheap way to test it. Lead with this. */
|
|
629
|
+
riskiestAssumption: {
|
|
630
|
+
section: string;
|
|
631
|
+
assumption: string;
|
|
632
|
+
experiment: string;
|
|
633
|
+
};
|
|
634
|
+
/** 0-100, advisory and coarse - it shows movement between revisions. */
|
|
635
|
+
score: number;
|
|
636
|
+
}
|
|
589
637
|
export interface WorkspaceApp {
|
|
590
638
|
applicationId: string;
|
|
591
639
|
/** Stable, human-readable key - branch on this, not `applicationId`, which is per-environment. */
|
|
@@ -1698,6 +1746,169 @@ export type CompanyEnrichmentResult = {
|
|
|
1698
1746
|
status: "skipped";
|
|
1699
1747
|
reason: EnrichmentSkipReason;
|
|
1700
1748
|
};
|
|
1749
|
+
/**
|
|
1750
|
+
* Where a brand claim was read. Its own union rather than `EnrichmentSource`,
|
|
1751
|
+
* which has no notion of a stylesheet or a rendered page.
|
|
1752
|
+
*/
|
|
1753
|
+
export type BrandCitationSource = "website" | "stylesheet" | "render";
|
|
1754
|
+
export interface BrandCitation {
|
|
1755
|
+
source: BrandCitationSource;
|
|
1756
|
+
url: string;
|
|
1757
|
+
/** ISO-8601. */
|
|
1758
|
+
retrievedAt: string;
|
|
1759
|
+
}
|
|
1760
|
+
/** Whether the company sells a thing, an engagement, or both. */
|
|
1761
|
+
export type BusinessModel = "product" | "service" | "hybrid";
|
|
1762
|
+
/**
|
|
1763
|
+
* Where an official name came from.
|
|
1764
|
+
*
|
|
1765
|
+
* `website-legal` is a legal disclosure the company published itself - a
|
|
1766
|
+
* footer copyright line, or the entity named on its Terms page - matched by
|
|
1767
|
+
* code against a corporate suffix and cited. `registry` is a statutory
|
|
1768
|
+
* register and outranks it. Neither is a model's recall.
|
|
1769
|
+
*/
|
|
1770
|
+
export type OfficialNameSource = "registry" | "website-legal";
|
|
1771
|
+
export interface Offering {
|
|
1772
|
+
name: string;
|
|
1773
|
+
/** The company's own grouping when it has one. */
|
|
1774
|
+
category: string | null;
|
|
1775
|
+
description: string | null;
|
|
1776
|
+
url: string | null;
|
|
1777
|
+
}
|
|
1778
|
+
export type LogoKind = "primary" | "mark" | "wordmark" | "favicon" | "social";
|
|
1779
|
+
export interface BrandLogo {
|
|
1780
|
+
/**
|
|
1781
|
+
* A **short-lived signed URL**, minted fresh on every read.
|
|
1782
|
+
*
|
|
1783
|
+
* Do not store it and do not bake it into a cached page - it will expire
|
|
1784
|
+
* and become a broken image with a plausible-looking address. Render it
|
|
1785
|
+
* directly, or re-read the profile, which on a cache hit is one document
|
|
1786
|
+
* read. `null` when Connect holds no cached copy, in which case `sourceUrl`
|
|
1787
|
+
* is all there is.
|
|
1788
|
+
*/
|
|
1789
|
+
url: string | null;
|
|
1790
|
+
/** Where it lives on the company's own site. Stable, and the citation. */
|
|
1791
|
+
sourceUrl: string;
|
|
1792
|
+
kind: LogoKind;
|
|
1793
|
+
/** Lowercase extension without the dot: `svg`, `png`, `webp`. */
|
|
1794
|
+
format: string;
|
|
1795
|
+
width: number | null;
|
|
1796
|
+
height: number | null;
|
|
1797
|
+
}
|
|
1798
|
+
/**
|
|
1799
|
+
* How a colour was obtained, which is how much it is worth.
|
|
1800
|
+
*
|
|
1801
|
+
* `css-var` is the company naming its own colour in its own tokens - the
|
|
1802
|
+
* strongest signal short of a brand manual. `computed` comes from a rendered
|
|
1803
|
+
* page. `frequency` is a ranked count of declarations and is the weakest.
|
|
1804
|
+
*/
|
|
1805
|
+
export type PaletteSource = "css-var" | "computed" | "frequency";
|
|
1806
|
+
export type PaletteRole = "primary" | "secondary" | "accent" | "background" | "surface" | "text" | "muted";
|
|
1807
|
+
export interface PaletteEntry {
|
|
1808
|
+
/** Lowercase `#rrggbb`. */
|
|
1809
|
+
hex: string;
|
|
1810
|
+
/** Assigned by a model over measured values - never invented by one. */
|
|
1811
|
+
role: PaletteRole | null;
|
|
1812
|
+
source: PaletteSource;
|
|
1813
|
+
/** The CSS custom property it came from, when `source` is `css-var`. */
|
|
1814
|
+
variableName: string | null;
|
|
1815
|
+
occurrences: number;
|
|
1816
|
+
}
|
|
1817
|
+
export type TypefaceRole = "display" | "heading" | "body" | "mono";
|
|
1818
|
+
export interface TypefaceEntry {
|
|
1819
|
+
/** The first named family in the stack - the one the company chose, not the fallbacks. */
|
|
1820
|
+
family: string;
|
|
1821
|
+
role: TypefaceRole | null;
|
|
1822
|
+
weights: number[];
|
|
1823
|
+
webfontUrl: string | null;
|
|
1824
|
+
occurrences: number;
|
|
1825
|
+
}
|
|
1826
|
+
/** One step of a measured scale. */
|
|
1827
|
+
export interface ScaleStep {
|
|
1828
|
+
value: number;
|
|
1829
|
+
/** `px`, `rem`, `em`, `%`. */
|
|
1830
|
+
unit: string;
|
|
1831
|
+
occurrences: number;
|
|
1832
|
+
}
|
|
1833
|
+
/**
|
|
1834
|
+
* A CSS framework detected by signature.
|
|
1835
|
+
*
|
|
1836
|
+
* Worth checking first, because it tells you how to read everything else: a
|
|
1837
|
+
* Tailwind site's spacing scale is Tailwind's defaults rather than a
|
|
1838
|
+
* considered choice, and a palette full of `--bs-*` is Bootstrap's.
|
|
1839
|
+
*/
|
|
1840
|
+
export type CssFramework = "tailwind" | "bootstrap" | "material" | "chakra" | "bulma";
|
|
1841
|
+
export interface BrandKit {
|
|
1842
|
+
logos: BrandLogo[];
|
|
1843
|
+
palette: PaletteEntry[];
|
|
1844
|
+
typography: TypefaceEntry[];
|
|
1845
|
+
voice: {
|
|
1846
|
+
tagline: string | null;
|
|
1847
|
+
/** A few adjectives for the register the copy is written in. */
|
|
1848
|
+
tone: string[];
|
|
1849
|
+
};
|
|
1850
|
+
}
|
|
1851
|
+
export interface DesignSystem {
|
|
1852
|
+
radiusScale: ScaleStep[];
|
|
1853
|
+
spacingScale: ScaleStep[];
|
|
1854
|
+
/** Raw `box-shadow` values, most used first. */
|
|
1855
|
+
shadows: string[];
|
|
1856
|
+
/** `@media` breakpoint widths as written, ascending. */
|
|
1857
|
+
breakpoints: string[];
|
|
1858
|
+
/** Every `--*` declaration on `:root` - the company's own token vocabulary, verbatim. */
|
|
1859
|
+
customProperties: {
|
|
1860
|
+
name: string;
|
|
1861
|
+
value: string;
|
|
1862
|
+
}[];
|
|
1863
|
+
framework: CssFramework | null;
|
|
1864
|
+
}
|
|
1865
|
+
/**
|
|
1866
|
+
* What a company looks like, measured off its own website.
|
|
1867
|
+
*
|
|
1868
|
+
* The important property: **the numbers and the colours here were parsed, not
|
|
1869
|
+
* generated.** Every hex came out of a stylesheet and every scale step is a
|
|
1870
|
+
* count of real declarations. A model wrote only the prose (`about`,
|
|
1871
|
+
* `industry`, `businessModel`, `offerings`, `voice`) and assigned `role` to
|
|
1872
|
+
* values it was shown. Ask any model directly for a brand colour and it
|
|
1873
|
+
* answers `#3B82F6`; that is the failure this shape exists to avoid.
|
|
1874
|
+
*/
|
|
1875
|
+
export interface BrandProfile {
|
|
1876
|
+
/** Normalised - lowercase, no scheme, no `www.`. */
|
|
1877
|
+
domain: string;
|
|
1878
|
+
/** The trading name, as the company writes it. */
|
|
1879
|
+
knownAs: string | null;
|
|
1880
|
+
officialName: string | null;
|
|
1881
|
+
officialNameSource: OfficialNameSource | null;
|
|
1882
|
+
about: string | null;
|
|
1883
|
+
industry: string | null;
|
|
1884
|
+
businessModel: BusinessModel | null;
|
|
1885
|
+
offerings: Offering[];
|
|
1886
|
+
brandKit: BrandKit;
|
|
1887
|
+
designSystem: DesignSystem;
|
|
1888
|
+
/** Derived from how many aspects the sources actually filled - not a model's self-report. */
|
|
1889
|
+
confidence: "high" | "medium" | "low";
|
|
1890
|
+
citations: BrandCitation[];
|
|
1891
|
+
/** ISO-8601. Age the profile against this. */
|
|
1892
|
+
retrievedAt: string;
|
|
1893
|
+
}
|
|
1894
|
+
/**
|
|
1895
|
+
* Why there is nothing to report. A normal 200, not an error.
|
|
1896
|
+
*
|
|
1897
|
+
* `consumer_domain` and `blocked_domain` will never succeed on retry.
|
|
1898
|
+
* `unreachable` may. `no_stylesheet` means the site answered but shipped no
|
|
1899
|
+
* CSS Connect could measure - a real outcome for a page built entirely in
|
|
1900
|
+
* inline styles.
|
|
1901
|
+
*/
|
|
1902
|
+
export type BrandSkipReason = "no_domain" | "consumer_domain" | "blocked_domain" | "unreachable" | "no_signal" | "no_stylesheet";
|
|
1903
|
+
export type BrandExtractionResult = {
|
|
1904
|
+
status: "extracted";
|
|
1905
|
+
profile: BrandProfile;
|
|
1906
|
+
/** Whether Connect served this from its platform-wide cache. The profile is the same either way. */
|
|
1907
|
+
cached: boolean;
|
|
1908
|
+
} | {
|
|
1909
|
+
status: "skipped";
|
|
1910
|
+
reason: BrandSkipReason;
|
|
1911
|
+
};
|
|
1701
1912
|
/**
|
|
1702
1913
|
* What kind of conversation a channel carries. `voice` is the only kind
|
|
1703
1914
|
* Connect stocks inventory for today - `whatsapp` and `meeting` are modelled
|
|
@@ -1952,3 +2163,62 @@ export interface ConversationsPage {
|
|
|
1952
2163
|
nextCursor: string | null;
|
|
1953
2164
|
hasMore: boolean;
|
|
1954
2165
|
}
|
|
2166
|
+
/**
|
|
2167
|
+
* One notification, to members of one workspace.
|
|
2168
|
+
*
|
|
2169
|
+
* Hand-written here rather than imported from `@connect/data`, like every wire
|
|
2170
|
+
* type in this file: the published package carries none of Connect's
|
|
2171
|
+
* internals, and `conformance.test.ts` type-checks these against the server's
|
|
2172
|
+
* own DTOs so drift fails the build in Connect rather than in a partner's
|
|
2173
|
+
* integration.
|
|
2174
|
+
*/
|
|
2175
|
+
export interface SendPushInput {
|
|
2176
|
+
workspaceId: string;
|
|
2177
|
+
/**
|
|
2178
|
+
* Connect user ids. Ids naming somebody who is not an active member are
|
|
2179
|
+
* dropped rather than refused - a partner legitimately holds a stale id for
|
|
2180
|
+
* somebody who has left, and failing the whole call for one of them would
|
|
2181
|
+
* mean a reminder nobody gets.
|
|
2182
|
+
*/
|
|
2183
|
+
userIds: string[];
|
|
2184
|
+
title: string;
|
|
2185
|
+
body: string;
|
|
2186
|
+
/** Absolute, and on an origin this application registered as an SSO redirect URI. */
|
|
2187
|
+
url: string;
|
|
2188
|
+
}
|
|
2189
|
+
/**
|
|
2190
|
+
* What a fan-out actually did.
|
|
2191
|
+
*
|
|
2192
|
+
* Reported honestly rather than collapsed to a boolean, because the
|
|
2193
|
+
* interesting number is usually `silenced`: a partner seeing zero delivered
|
|
2194
|
+
* needs to know whether nobody has a device or everybody muted them, and those
|
|
2195
|
+
* call for opposite responses.
|
|
2196
|
+
*/
|
|
2197
|
+
export interface SendPushResponse {
|
|
2198
|
+
/** Members reached after preference filtering. */
|
|
2199
|
+
recipients: number;
|
|
2200
|
+
/** Members dropped because they silenced notifications from their apps. */
|
|
2201
|
+
silenced: number;
|
|
2202
|
+
/** Devices the notification actually reached, across every recipient. */
|
|
2203
|
+
delivered: number;
|
|
2204
|
+
failed: number;
|
|
2205
|
+
}
|
|
2206
|
+
export interface PushKeyResponse {
|
|
2207
|
+
/** `false` when the platform has no VAPID keys - say so rather than prompting. */
|
|
2208
|
+
configured: boolean;
|
|
2209
|
+
/** The `applicationServerKey` for `pushManager.subscribe()`. Empty when unconfigured. */
|
|
2210
|
+
publicKey: string;
|
|
2211
|
+
}
|
|
2212
|
+
/** Exactly what `PushSubscription.toJSON()` produces, plus the agent string for the device list. */
|
|
2213
|
+
export interface RegisterPushDeviceInput {
|
|
2214
|
+
endpoint: string;
|
|
2215
|
+
keys: {
|
|
2216
|
+
p256dh: string;
|
|
2217
|
+
auth: string;
|
|
2218
|
+
};
|
|
2219
|
+
userAgent: string;
|
|
2220
|
+
}
|
|
2221
|
+
export interface RegisterPushDeviceResponse {
|
|
2222
|
+
/** Hand this to `revokeDevice` to turn the same browser off again. */
|
|
2223
|
+
id: string;
|
|
2224
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmoops/connect-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.32.0",
|
|
4
4
|
"description": "Official server-side SDK for the CosmoOps Connect platform API - Login with Connect, entitlements, billing and metering.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"type": "commonjs",
|