@productcraft/rally 0.0.2 → 0.0.4
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 +180 -0
- package/dist/index.d.cts +354 -114
- package/dist/index.d.ts +354 -114
- package/package.json +3 -3
package/README.md
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# @productcraft/rally
|
|
2
|
+
|
|
3
|
+
Typed Node.js SDK for [ProductCraft Rally](https://productcraft.co) — waitlist management: public-form signups, variants for A/B/n landing pages, referrals, position + leaderboard, approval workflow with invite-to-app, signed outbound webhooks, CSV export.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install @productcraft/rally
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
The waitlist-entries endpoint can be called unauthenticated from a marketing-site form. Every other endpoint (admin, approval, analytics, exports, webhooks) requires a PlatformUser cookie or a workspace-scoped PAK (`pcft_live_…`).
|
|
10
|
+
|
|
11
|
+
## Quick start — accept a signup from a public form
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { Rally } from "@productcraft/rally";
|
|
15
|
+
|
|
16
|
+
// No auth — public surface
|
|
17
|
+
const rally = new Rally();
|
|
18
|
+
|
|
19
|
+
const { data, error } = await rally.client.POST(
|
|
20
|
+
"/v1/waitlists/{workspace_slug}/{waitlist_slug}/entries",
|
|
21
|
+
{
|
|
22
|
+
params: { path: { workspace_slug: "acme", waitlist_slug: "early-access" } },
|
|
23
|
+
body: {
|
|
24
|
+
email: "alice@example.com",
|
|
25
|
+
name: "Alice",
|
|
26
|
+
referrer: "twitter",
|
|
27
|
+
referral_code: "ada-123", // optional — bumps the referrer's position
|
|
28
|
+
metadata: { plan_interest: "pro" },
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
);
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The response includes the entry's id + assigned position. Round-trip the `id` into your "thanks" page so the visitor can share their own referral link.
|
|
35
|
+
|
|
36
|
+
If the waitlist has `settings.recaptcha_site_key` set, also pass a `recaptcha_token` from your client side.
|
|
37
|
+
|
|
38
|
+
## Quick start — workspace-admin (authenticated)
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { Rally } from "@productcraft/rally";
|
|
42
|
+
|
|
43
|
+
const rally = new Rally({
|
|
44
|
+
auth: { type: "apiKey", key: process.env.PCFT_KEY! },
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Create a waitlist
|
|
48
|
+
const { data } = await rally.client.POST(
|
|
49
|
+
"/v1/workspaces/{workspace_id}/waitlists",
|
|
50
|
+
{
|
|
51
|
+
params: { path: { workspace_id: "ws_..." } },
|
|
52
|
+
body: { name: "Early Access", slug: "early-access" },
|
|
53
|
+
},
|
|
54
|
+
);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`{workspace_id}` is the workspace UUID returned by `@productcraft/platform-auth`'s introspect endpoint.
|
|
58
|
+
|
|
59
|
+
## Configuration
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
new Rally({
|
|
63
|
+
// Optional: required for workspace-admin calls. Public submit works without auth.
|
|
64
|
+
auth: { type: "apiKey", key: "pcft_live_..." }
|
|
65
|
+
| { type: "bearer", token: "eyJ..." }
|
|
66
|
+
| { type: "cookie", value: "auth_token=..." },
|
|
67
|
+
baseUrl: "https://api.rally.example.test", // optional override
|
|
68
|
+
fetch: customFetch, // optional
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Common operations
|
|
73
|
+
|
|
74
|
+
### Public surface
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
// Read the waitlist's public metadata (name, position-window, active variant, ...)
|
|
78
|
+
await rally.client.GET(
|
|
79
|
+
"/v1/waitlists/{workspace_slug}/{waitlist_slug}",
|
|
80
|
+
{ params: { ... } },
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// Read the public leaderboard (when enabled per-waitlist)
|
|
84
|
+
await rally.client.GET(
|
|
85
|
+
"/v1/waitlists/{workspace_slug}/{waitlist_slug}/leaderboard",
|
|
86
|
+
{ params: { ... } },
|
|
87
|
+
);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Entries (workspace-admin)
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
// List entries
|
|
94
|
+
await rally.client.GET(
|
|
95
|
+
"/v1/workspaces/{workspace_id}/waitlists/{waitlist_id}/entries",
|
|
96
|
+
{ params: { path: { workspace_id, waitlist_id }, query: { limit: 50 } } },
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// Count
|
|
100
|
+
await rally.client.GET(
|
|
101
|
+
"/v1/workspaces/{workspace_id}/waitlists/{waitlist_id}/entries/count",
|
|
102
|
+
{ ... },
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
// Approve / reject in bulk
|
|
106
|
+
await rally.client.POST(
|
|
107
|
+
"/v1/workspaces/{workspace_id}/waitlists/{waitlist_id}/entries/bulk",
|
|
108
|
+
{ params: { ... }, body: { ids: [...], action: "approve" } },
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// Send an invite (e.g. into a Heimdall app)
|
|
112
|
+
await rally.client.POST(
|
|
113
|
+
"/v1/workspaces/{workspace_id}/waitlists/{waitlist_id}/entries/{entry_id}/invite-to-app",
|
|
114
|
+
{ params: { ... }, body: { /* app + role + invite_template */ } },
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
// Export to CSV
|
|
118
|
+
await rally.client.GET(
|
|
119
|
+
"/v1/workspaces/{workspace_id}/waitlists/{waitlist_id}/entries/export.csv",
|
|
120
|
+
{ ... },
|
|
121
|
+
);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Variants (A/B/n landing pages)
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
// Create a variant
|
|
128
|
+
await rally.client.POST(
|
|
129
|
+
"/v1/workspaces/{workspace_id}/waitlists/{waitlist_id}/variants",
|
|
130
|
+
{ params: { ... }, body: { slug: "headline-b", weight: 50 } },
|
|
131
|
+
);
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Front-end picks up the active variant from `GET /v1/waitlists/:workspace_slug/:waitlist_slug` and round-trips its id back in the entry submission via the `variant_id` field — Rally computes per-variant conversion without a separate impressions table.
|
|
135
|
+
|
|
136
|
+
### Analytics
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
// Conversion + per-variant counts
|
|
140
|
+
await rally.client.GET(
|
|
141
|
+
"/v1/workspaces/{workspace_id}/waitlists/{waitlist_id}/analytics",
|
|
142
|
+
{ ... },
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
// Timeseries
|
|
146
|
+
await rally.client.GET(
|
|
147
|
+
"/v1/workspaces/{workspace_id}/waitlists/{waitlist_id}/analytics/timeline",
|
|
148
|
+
{ params: { path: { ... }, query: { bucket: "1d", lookback: "30d" } } },
|
|
149
|
+
);
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Webhooks
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
// Subscribe to entry.created / entry.approved / entry.rejected
|
|
156
|
+
await rally.client.POST(
|
|
157
|
+
"/v1/workspaces/{workspace_id}/webhooks",
|
|
158
|
+
{ params: { ... }, body: { url: "https://yourapp.com/hooks/rally", events: ["entry.created"] } },
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
// Rotate the signing secret without disabling the webhook
|
|
162
|
+
await rally.client.POST(
|
|
163
|
+
"/v1/workspaces/{workspace_id}/webhooks/{id}/rotate-secret",
|
|
164
|
+
{ ... },
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
// Replay a delivery from history
|
|
168
|
+
await rally.client.GET(
|
|
169
|
+
"/v1/workspaces/{workspace_id}/webhooks/{id}/deliveries",
|
|
170
|
+
{ ... },
|
|
171
|
+
);
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## How this SDK is built
|
|
175
|
+
|
|
176
|
+
Generated from the live OpenAPI spec at `https://api.rally.productcraft.co/docs-json` via [`openapi-typescript`](https://openapi-ts.dev/) + [`openapi-fetch`](https://openapi-ts.dev/openapi-fetch/). The nightly `spec-refresh` workflow opens a PR whenever the spec changes.
|
|
177
|
+
|
|
178
|
+
## License
|
|
179
|
+
|
|
180
|
+
[MIT](https://github.com/clauderanelagh/productcraft-node/blob/main/LICENSE).
|