@wefunder/sdk 0.1.0-beta.0 → 0.1.0-beta.10
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 +190 -114
- package/dist/index.cjs +1588 -672
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7033 -1946
- package/dist/index.d.ts +7033 -1946
- package/dist/index.js +1575 -669
- package/dist/index.js.map +1 -1
- package/examples_manifest.json +67 -0
- package/package.json +14 -10
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"manifestVersion": 1,
|
|
3
|
+
"lang": "JavaScript",
|
|
4
|
+
"label": "@wefunder/sdk",
|
|
5
|
+
"sdkVersion": "0.1.0-beta.10",
|
|
6
|
+
"apiVersion": "2025-01-15",
|
|
7
|
+
"generatedBy": "scripts/build-examples-manifest.mjs",
|
|
8
|
+
"samples": {
|
|
9
|
+
"createInstallation": "const install = await wf.unwrap(\n wf.raw.createInstallation({\n body: { target_type: \"company\", target_id: \"co_abc123Example\", scopes: [\"read:offerings\", \"read:investments\"] },\n }),\n);\nconsole.log(install.data?.id, install.data?.attributes);",
|
|
10
|
+
"createInstallationToken": "const token = await wf.unwrap(\n wf.raw.createInstallationToken({ path: { external_id: \"ins_9t2xExample\" }, body: { scopes: [\"read:investments\"] } }),\n);\nconsole.log(token.data);",
|
|
11
|
+
"createIntent": "const { data: intent } = await wf.unwrap(\n wf.raw.createIntent({\n body: {\n action_name: \"syndicates.close_deal\",\n resource_type: \"Club\",\n resource_id: \"syn_example\",\n params: { fundraise_id: 99 },\n // Safe to retry: an existing pending/executed intent with this key is returned instead.\n idempotency_key: \"close-acme-series-a\",\n requested_by_agent: \"My Deal Bot\",\n },\n }),\n);\n// Hand this URL to a human — the action executes only after they approve.\nconsole.log(intent?.attributes?.review_url);",
|
|
12
|
+
"createWebhookEndpoint": "const endpoint = await wf.webhookEndpoints.create({\n url: \"https://yourapp.com/webhooks/wefunder\", // public HTTPS (localhost/private IPs rejected)\n events: [\"offering.opened\", \"investment.executed\", \"investment.changed\"],\n mode: \"live\",\n});\n// Store this now — it's needed by constructEvent() and is never shown again.\nconst signingSecret = endpoint.attributes?.secret;\nconsole.log(endpoint.id, signingSecret);",
|
|
13
|
+
"createWebhookSubscription": "const subscription = await wf.unwrap(\n wf.raw.createWebhookSubscription({\n path: { campaign_id: 1234 },\n body: {\n target_url: \"https://example.com/wefunder/webhooks\",\n events: [\"investment.confirmed\", \"investment.canceled\"],\n },\n }),\n);\nconsole.log(subscription);",
|
|
14
|
+
"deleteWebhookEndpoint": "const result = await wf.webhookEndpoints.remove(\"whe_8f3ExampleEndpoint00\");\nconsole.log(result.removed); // true",
|
|
15
|
+
"deleteWebhookSubscription": "await wf.unwrap(wf.raw.deleteWebhookSubscription({ path: { campaign_id: 1234, webhook_id: 56 } }));",
|
|
16
|
+
"followCompany": "const result = await wf.unwrap(wf.raw.followCompany({ path: { company_id: \"co_abc123Example\" } }));\nconsole.log(result);",
|
|
17
|
+
"getAttributionMe": "const me = await wf.attribution.me();\nconsole.log(me);",
|
|
18
|
+
"getAttributionStats": "const stats = await wf.unwrap(\n wf.raw.getAttributionStats({\n path: { campaign_id: 1234 },\n query: { start_date: \"2026-01-01\", end_date: \"2026-03-31\", utm_source: \"newsletter\" },\n }),\n);\nconsole.log(stats.data);",
|
|
19
|
+
"getCompany": "const { data: company } = await wf.unwrap(wf.raw.getCompany({ path: { id: \"co_abc123Example\" } }));\nconsole.log(company?.attributes?.name);",
|
|
20
|
+
"getCompanyDisclosures": "const { data: disclosures } = await wf.unwrap(\n wf.raw.getCompanyDisclosures({\n path: { id: \"co_abc123Example\" },\n query: { sections: [\"business\", \"risks\", \"use_of_funds\"] },\n }),\n);\nconsole.log(disclosures?.attributes);",
|
|
21
|
+
"getCompanyPitch": "const { data: pitch } = await wf.unwrap(\n wf.raw.getCompanyPitch({ path: { id: \"co_abc123Example\" }, query: { sections: [\"story\", \"perks\"] } }),\n);\nconsole.log(pitch?.attributes);",
|
|
22
|
+
"getCompanyUpdate": "const { data: update } = await wf.unwrap(\n wf.raw.getCompanyUpdate({ path: { id: \"co_abc123Example\", update_id: \"upd_9t2xExample\" } }),\n);\nconsole.log(update?.attributes?.title);",
|
|
23
|
+
"getCurrentUser": "const me = await wf.users.me();\nconsole.log(\"Logged in as\", me.id);",
|
|
24
|
+
"getInstallation": "const { data: install } = await wf.unwrap(wf.raw.getInstallation({ path: { external_id: \"ins_9t2xExample\" } }));\nconsole.log(install?.attributes);",
|
|
25
|
+
"getIntent": "const intent = await wf.intents.get(\"intent_abc123\");\nconsole.log(intent.attributes?.status); // pending | approved | executed | expired | rejected | failed",
|
|
26
|
+
"getInvestment": "const investment = await wf.investments.get(\"inv_8f3kQ2Example\");\nconsole.log(investment.status, investment.amounts?.committed_cents, investment.investor?.id);",
|
|
27
|
+
"getOffering": "const offering = await wf.offerings.get(\"ofr_QfmTyP8qjfYAvkEgREyL3kLf\");\nconsole.log(offering.attributes?.company_name, offering.attributes?.status);",
|
|
28
|
+
"getOfferingStats": "const { data: stats } = await wf.unwrap(\n wf.raw.getOfferingStats({ path: { offering_id: \"ofr_QfmTyP8qjfYAvkEgREyL3kLf\" } }),\n);\nconsole.log(stats);",
|
|
29
|
+
"getPortfolio": "const summary = await wf.portfolio.get();\nconsole.log(\n summary.attributes?.total_current_value_cents,\n summary.attributes?.return_multiple,\n);",
|
|
30
|
+
"getSyndicate": "const syndicate = await wf.syndicates.get(42);\nconsole.log(syndicate.attributes?.name, syndicate.attributes?.deal_count);",
|
|
31
|
+
"getSyndicatePortfolio": "const { data: summary } = await wf.unwrap(\n wf.raw.getSyndicatePortfolio({\n path: { syndicate_id: \"syn_example\" },\n }),\n);\nconsole.log(summary?.attributes?.total_current_value_cents);",
|
|
32
|
+
"getWebhookEndpoint": "const endpoint = await wf.webhookEndpoints.get(\"whe_8f3ExampleEndpoint00\");\nconsole.log(endpoint.attributes?.events, endpoint.attributes?.failing_since);",
|
|
33
|
+
"getWebhookSubscription": "const subscription = await wf.unwrap(\n wf.raw.getWebhookSubscription({ path: { campaign_id: 1234, webhook_id: 56 } }),\n);\nconsole.log(subscription.data);",
|
|
34
|
+
"guides/client-credentials": "const wf = await Wefunder.fromClientCredentials({\n clientId: process.env.WEFUNDER_CLIENT_ID!,\n clientSecret: process.env.WEFUNDER_CLIENT_SECRET!,\n scopes: [\"read:public\"],\n});\n\nconsole.log(\"mode:\", wf.mode); // \"live\" or \"test\", from the token prefix\n\n// client_credentials holds only read:public — browse public offerings.\n// (wf.users.me() would throw 403 insufficient_scope: it needs read:profile.)\nfor await (const offering of wf.offerings.all()) {\n console.log(offering.id);\n}",
|
|
35
|
+
"guides/install-target": "// 1. Which companies / syndicates may this user install on? (Only those — an investor's\n// empty list is not a failure.)\nconst targets = await wf.unwrap(\n wf.raw.listEligibleInstallTargets({ query: { target_type: \"syndicate\" } }),\n);\nfor (const t of targets.data ?? [])\n console.log(t.id, t.name, t.installed ? \"(already installed)\" : \"\");\n\n// 2. Install. The response carries the install (`data`) AND its token. If the app is\n// already installed here the API answers 409 `already_installed` — its `details.installation`\n// is the existing install's id, so mint a fresh token for that instead. Any other error\n// (revoked install, missing scope) still throws.\nlet installationToken: string | undefined;\ntry {\n const installed = await wf.unwrap(\n wf.raw.createInstallation({\n body: {\n target_type: \"syndicate\",\n target_id: syndicateId,\n scopes: [\"read:syndicates\"],\n },\n }),\n );\n installationToken = installed.token?.access_token;\n} catch (err) {\n if (!(err instanceof WefunderError) || err.type !== \"already_installed\")\n throw err;\n const existingId = (err.details as { installation?: string } | undefined)\n ?.installation;\n if (!existingId) throw err;\n const minted = await wf.unwrap(\n wf.raw.createInstallationToken({ path: { external_id: existingId } }),\n );\n installationToken = minted.token?.access_token; // shown once — store it\n}\nif (!installationToken) throw new Error(\"no installation token returned\");\n\n// 3. First request AS the installation.\nconst asSyndicate = new Wefunder({ accessToken: installationToken });\nconst deals = await asSyndicate.unwrap(\n asSyndicate.raw.listSyndicateDeals({ path: { syndicate_id: syndicateId } }),\n);\nconsole.log(deals.data?.length, \"deals\");",
|
|
36
|
+
"guides/user-token": "// 1. Send the investor to consent. Keep state + the PKCE verifier in their session.\nconst pkce = generatePkce();\nconst state = crypto.randomUUID();\nawait saveAttempt(state, pkce.codeVerifier);\nconst authorizationUrl = createAuthorizationUrl({\n clientId: process.env.WEFUNDER_CLIENT_ID!,\n redirectUri: process.env.WEFUNDER_REDIRECT_URI!,\n scopes: [\"read:investments\"],\n state,\n pkce,\n});\n// redirect(authorizationUrl)\n\n// 2. On the callback, exchange the code (after checking `state` matches the session).\nconst tokens = await exchangeCode({\n clientId: process.env.WEFUNDER_CLIENT_ID!,\n clientSecret: process.env.WEFUNDER_CLIENT_SECRET, // omit for public clients\n code: \"AUTHORIZATION_CODE\",\n redirectUri: process.env.WEFUNDER_REDIRECT_URI!,\n codeVerifier: pkce.codeVerifier,\n});\n\n// 3. Read their holdings. The access token lasts two hours; the SDK rotates the refresh\n// token for you and hands every new set to `store.save` — persist the whole thing.\nconst wf = new Wefunder({\n tokens,\n clientId: process.env.WEFUNDER_CLIENT_ID!,\n clientSecret: process.env.WEFUNDER_CLIENT_SECRET,\n store: { save: (next: TokenSet) => persistTokens(next) },\n});\nconst portfolio = await wf.portfolio.get();\nconsole.log(portfolio.attributes?.total_current_value_cents);",
|
|
37
|
+
"listActivity": "// Everything my connected apps did to syndicate members, most recent first.\nconst activity = await wf.unwrap(\n wf.raw.listActivity({\n query: { action_pattern: \"syndicates.member.*\", status: \"success\" },\n }),\n);\nfor (const event of activity.data ?? []) {\n console.log(event.attributes?.action, event.attributes?.created_at);\n}",
|
|
38
|
+
"listAttributedInvestments": "const investments = await wf.unwrap(\n wf.raw.listAttributedInvestments({\n path: { campaign_id: 1234 },\n query: { start_date: \"2026-01-01\", detail_level: \"anonymized\" },\n }),\n);\nfor (const investment of investments.data ?? []) {\n if (\"amount_tier\" in investment) {\n // Anonymized row: opaque tokens + an amount tier, never exact values or PII.\n console.log(investment.investor_token, investment.amount_tier, investment.attribution?.utm_source);\n }\n}",
|
|
39
|
+
"listAttributionCampaigns": "const campaigns = await wf.unwrap(wf.raw.listAttributionCampaigns());\nconsole.log(campaigns.data);",
|
|
40
|
+
"listCampaigns": "// Campaigns for companies where the authenticated user is a founder.\nconst page = await wf.campaigns.list();\nfor (const campaign of page.data ?? []) {\n console.log(campaign.id, campaign.attributes?.company_name, campaign.attributes?.amount_raised);\n}\n\n// Or stream every campaign lazily, one page fetched at a time:\nfor await (const campaign of wf.campaigns.all()) {\n console.log(campaign.attributes?.state);\n}",
|
|
41
|
+
"listCompanyInvestmentChanges": "// Walk every change for one company. Persist meta.next_cursor after each page — it is\n// always present, even on the last page — and pass it back next sync for only what changed.\nlet cursor: string | undefined;\ndo {\n const page = await wf.unwrap(\n wf.raw.listCompanyInvestmentChanges({ path: { company_id: companyId }, query: { cursor, per_page: 50 } }),\n );\n for (const record of page.data ?? []) console.log(record.id, record.visible);\n cursor = page.meta?.next_cursor;\n if (!page.meta?.has_more) break;\n} while (cursor);",
|
|
42
|
+
"listCompanyQuestions": "const page = await wf.unwrap(\n wf.raw.listCompanyQuestions({\n path: { id: \"co_abc123Example\" },\n query: { q: \"revenue\", unanswered_by_team: true, per_page: 20 },\n }),\n);\nfor (const question of page.data ?? []) console.log(question.id, question.attributes);",
|
|
43
|
+
"listCompanyUpdates": "const page = await wf.unwrap(wf.raw.listCompanyUpdates({ path: { id: \"co_abc123Example\" }, query: { per_page: 10 } }));\nfor (const update of page.data ?? []) console.log(update.id, update.attributes?.title);\nconsole.log(page.meta?.next_cursor);",
|
|
44
|
+
"listConnectedApps": "const apps = await wf.unwrap(wf.raw.listConnectedApps());\nfor (const app of apps.data ?? []) {\n console.log(app.id, app.attributes?.name);\n}",
|
|
45
|
+
"listEligibleInstallTargets": "const targets = await wf.unwrap(wf.raw.listEligibleInstallTargets({ query: { target_type: \"company\" } }));\nfor (const target of targets.data ?? []) console.log(target);",
|
|
46
|
+
"listFollowedCompanies": "const page = await wf.unwrap(wf.raw.listFollowedCompanies({ query: { per_page: 25 } }));\nfor (const company of page.data ?? []) console.log(company.id);",
|
|
47
|
+
"listInstallations": "const installs = await wf.unwrap(wf.raw.listInstallations());\nfor (const install of installs.data ?? []) console.log(install.id, install.attributes);",
|
|
48
|
+
"listIntents": "// Which of my proposed actions are still waiting on human approval?\nconst page = await wf.intents.list({ status: \"pending\" });\nfor (const intent of page.data ?? []) {\n console.log(intent.id, intent.attributes?.action, intent.attributes?.review_url);\n}",
|
|
49
|
+
"listInvestments": "// Auto-paginate the full history lazily:\nfor await (const investment of wf.investments.all()) {\n console.log(investment.id);\n}\n\n// Or grab one page with its meta (next_cursor, etc.):\nconst page = await wf.investments.list();\nconsole.log(page.data?.length, page.meta?.next_cursor);",
|
|
50
|
+
"listMyCompanies": "const companies = await wf.unwrap(wf.raw.listMyCompanies());\nfor (const company of companies.data ?? []) console.log(company.id, company.attributes?.name);",
|
|
51
|
+
"listOfferings": "// Browse live offerings, sorted. The cursor is opaque and handled for you.\nconst page = await wf.offerings.list({ sort: \"most_raised\" });\nfor (const offering of page.data ?? []) {\n console.log(offering.id, offering.attributes?.company_name);\n}\n\n// Or stream every offering lazily, one page fetched at a time:\nfor await (const offering of wf.offerings.all({ sort: \"newest\" })) {\n console.log(offering.id);\n}",
|
|
52
|
+
"listPortfolioPositions": "for await (const position of wf.portfolio.positions.all({ status: \"active\" })) {\n console.log(\n position.id,\n position.attributes?.company?.name,\n position.attributes?.current_value_cents,\n );\n}",
|
|
53
|
+
"listSyndicatePortfolioPositions": "const page = await wf.unwrap(\n wf.raw.listSyndicatePortfolioPositions({\n path: { syndicate_id: \"syn_example\" },\n query: { status: \"active\", per_page: 25 },\n }),\n);\nfor (const position of page.data ?? []) {\n console.log(position.id, position.attributes?.investor_count);\n}",
|
|
54
|
+
"listSyndicates": "const page = await wf.syndicates.list({ limit: 50 });\nfor (const syndicate of page.data ?? []) {\n console.log(syndicate.id, syndicate.attributes?.name, syndicate.attributes?.member_count);\n}",
|
|
55
|
+
"listWebhookEndpoints": "const { data: endpoints, meta } = await wf.webhookEndpoints.list();\nconsole.log(`${endpoints?.length} of ${meta?.quota} endpoint slots used`);\nfor (const ep of endpoints ?? []) {\n console.log(ep.id, ep.attributes?.url, ep.attributes?.enabled, ep.attributes?.last_delivery_status);\n}",
|
|
56
|
+
"listWebhookSubscriptions": "const subscriptions = await wf.unwrap(\n wf.raw.listWebhookSubscriptions({ path: { campaign_id: 1234 } }),\n);\nfor (const sub of subscriptions.data ?? []) {\n console.log(sub.id, sub.target_url, sub.events, sub.active);\n}",
|
|
57
|
+
"previewIntent": "const preview = await wf.unwrap(\n wf.raw.previewIntent({\n body: {\n action_name: \"syndicates.close_deal\",\n resource_type: \"Club\",\n resource_id: \"syn_example\",\n params: { fundraise_id: 99 },\n },\n }),\n);\nconsole.log(preview);",
|
|
58
|
+
"reenableWebhookEndpoint": "const endpoint = await wf.webhookEndpoints.reenable(\"whe_8f3ExampleEndpoint00\");\nconsole.log(endpoint.attributes?.enabled); // true",
|
|
59
|
+
"revokeInstallation": "const result = await wf.unwrap(wf.raw.revokeInstallation({ path: { external_id: \"ins_9t2xExample\" } }));\nconsole.log(result);",
|
|
60
|
+
"rotateWebhookEndpointSecret": "const endpoint = await wf.webhookEndpoints.rotateSecret(\"whe_8f3ExampleEndpoint00\");\nconst newSecret = endpoint.attributes?.secret; // shown once\nconsole.log(newSecret);",
|
|
61
|
+
"searchCompanies": "const results = await wf.unwrap(wf.raw.searchCompanies({ query: { q: \"solar\", limit: 5 } }));\nfor (const hit of results.data ?? []) console.log(hit.id, hit.attributes?.name);",
|
|
62
|
+
"testWebhookEndpoint": "const outcome = await wf.webhookEndpoints.test(\"whe_8f3ExampleEndpoint00\", \"investment.executed\");\nif (outcome.delivered) {\n console.log(`endpoint answered ${outcome.response_code} in ${outcome.duration_ms}ms`);\n} else {\n console.log(`no response: ${outcome.error}`); // timeout | blocked_url | tls_error | connection_failed\n}",
|
|
63
|
+
"testWebhookSubscription": "const result = await wf.unwrap(\n wf.raw.testWebhookSubscription({ path: { campaign_id: 1234, webhook_id: 56 } }),\n);\nconsole.log(result.data);",
|
|
64
|
+
"unfollowCompany": "const result = await wf.unwrap(wf.raw.unfollowCompany({ path: { company_id: \"co_abc123Example\" } }));\nconsole.log(result);",
|
|
65
|
+
"updateWebhookEndpoint": "const endpoint = await wf.webhookEndpoints.update(\"whe_8f3ExampleEndpoint00\", {\n events: [\"offering.opened\", \"offering.closed\", \"investment.executed\"],\n});\nconsole.log(endpoint.attributes?.events);"
|
|
66
|
+
}
|
|
67
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wefunder/sdk",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.10",
|
|
4
4
|
"description": "Official TypeScript SDK for the Wefunder API (beta)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"access": "public"
|
|
26
26
|
},
|
|
27
27
|
"engines": {
|
|
28
|
-
"node": ">=
|
|
28
|
+
"node": ">=20"
|
|
29
29
|
},
|
|
30
30
|
"main": "./dist/index.cjs",
|
|
31
31
|
"module": "./dist/index.js",
|
|
@@ -35,29 +35,33 @@
|
|
|
35
35
|
"types": "./dist/index.d.ts",
|
|
36
36
|
"import": "./dist/index.js",
|
|
37
37
|
"require": "./dist/index.cjs"
|
|
38
|
-
}
|
|
38
|
+
},
|
|
39
|
+
"./package.json": "./package.json"
|
|
39
40
|
},
|
|
40
41
|
"files": [
|
|
41
|
-
"dist"
|
|
42
|
+
"dist",
|
|
43
|
+
"examples_manifest.json"
|
|
42
44
|
],
|
|
43
45
|
"scripts": {
|
|
44
46
|
"generate": "openapi-ts",
|
|
45
47
|
"build": "tsup",
|
|
46
48
|
"typecheck": "tsc --noEmit",
|
|
49
|
+
"typecheck:examples": "tsc -p tsconfig.examples.json",
|
|
50
|
+
"build:examples": "node scripts/build-examples-manifest.mjs",
|
|
51
|
+
"build:conformance": "node scripts/build-conformance-manifest.mjs",
|
|
52
|
+
"version": "npm run build:examples && git add examples_manifest.json",
|
|
47
53
|
"test": "vitest run",
|
|
48
54
|
"test:watch": "vitest",
|
|
49
55
|
"test:e2e": "vitest run --config vitest.config.e2e.ts",
|
|
50
56
|
"sync-spec": "node scripts/sync-spec.mjs",
|
|
51
|
-
"prepublishOnly": "npm run typecheck && npm test && npm run build"
|
|
52
|
-
},
|
|
53
|
-
"dependencies": {
|
|
54
|
-
"@hey-api/client-fetch": "^0.13.1"
|
|
57
|
+
"prepublishOnly": "npm run typecheck && npm run typecheck:examples && npm test && npm run build"
|
|
55
58
|
},
|
|
56
59
|
"devDependencies": {
|
|
57
|
-
"@hey-api/openapi-ts": "^0.
|
|
60
|
+
"@hey-api/openapi-ts": "^0.99.0",
|
|
58
61
|
"@types/node": "^22.0.0",
|
|
59
62
|
"tsup": "^8.3.0",
|
|
60
63
|
"typescript": "^5.6.0",
|
|
61
|
-
"vitest": "^
|
|
64
|
+
"vitest": "^4.1.9",
|
|
65
|
+
"prettier": "^3.3.0"
|
|
62
66
|
}
|
|
63
67
|
}
|