@recursiv/sdk 0.7.82 → 0.7.83
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 +44 -1
- package/dist/assistant/fixedCardData.d.ts +91 -0
- package/dist/assistant/fixedCardData.js +372 -0
- package/dist/assistant/fixedCards.d.ts +35 -0
- package/dist/assistant/fixedCards.js +904 -0
- package/dist/assistant/hiddenText.d.ts +24 -0
- package/dist/assistant/hiddenText.js +263 -0
- package/dist/assistant/index.d.ts +22 -0
- package/dist/assistant/index.js +22 -0
- package/dist/assistant/render.d.ts +8 -0
- package/dist/assistant/render.js +253 -0
- package/dist/assistant/subjectBundle.d.ts +30 -0
- package/dist/assistant/subjectBundle.js +518 -0
- package/dist/assistant/types.d.ts +353 -0
- package/dist/assistant/types.js +11 -0
- package/dist/assistant-surfaces.d.ts +42 -0
- package/dist/assistant-surfaces.js +113 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +1 -0
- package/dist/realtime.d.ts +10 -3
- package/dist/realtime.js +11 -2
- package/dist/resources/agents.d.ts +6 -1
- package/dist/resources/agents.js +12 -2
- package/dist/resources/e2ee.d.ts +56 -0
- package/dist/resources/e2ee.js +21 -0
- package/dist/resources/link-preview.d.ts +42 -1
- package/dist/resources/link-preview.js +36 -2
- package/dist/resources/network.d.ts +6 -0
- package/dist/resources/project-brain.d.ts +8 -0
- package/dist/resources/users.d.ts +11 -2
- package/dist/resources/users.js +1 -1
- package/dist/types.d.ts +34 -0
- package/llm.md +32 -2
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -75,7 +75,7 @@ for await (const chunk of r.agents.chatStream(agent.id, { message: 'Hello!' }))
|
|
|
75
75
|
| Resource | Key methods |
|
|
76
76
|
|----------|------------|
|
|
77
77
|
| `r.agents` | `create`, `update`, `chat`, `chatStream`, `retryChatStream`, `grantProjectAccess`, `conversations`, `discover` (the app's agent directory: server search and sort, keyless-readable) |
|
|
78
|
-
| `r.e2ee` | `status`, `registerDevice`, `devices`, `revokeDevice` (`{ cascade }` to also revoke devices approved through it; 409 `has_dependents` otherwise), `requestResetCode` and `resetAccount` (reset encrypted chat with an emailed code where `status().accountReset`), `publishKeyPackages`, `keyPackageStatus`, `claimKeyPackage`, `appendEvent
|
|
78
|
+
| `r.e2ee` | `status`, `registerDevice`, `devices`, `revokeDevice` (`{ cascade }` to also revoke devices approved through it; 409 `has_dependents` otherwise), `requestResetCode` and `resetAccount` (reset encrypted chat with an emailed code where `status().accountReset`), `peerChanges` (contacts whose main device a reset replaced since a time), `publishKeyPackages`, `keyPackageStatus`, `claimKeyPackage`, `appendEvent` (`trust_change: true`: after a contact's reset, 409 `peer_not_joined` until this account added their new device), `events`, `acknowledge`, `conversations`, `requestStart` and `cancelStartRequest` (ask the other person to encrypt a chat when they have no device yet: a message with `metadata.kind` `e2ee_start_request`; the live ones are `ConversationDetail.encryption_requests`); device linking by scan: `createLink`, `link`, `scannedLink`, `approveLink`, `completeLink`, `linkProgress`, `cancelLink` (flag-gated E2EE v2 transport; its doorbell is `r.realtime.onE2eeEvent`, which carries a small message's stored ciphertext to the owner's own sockets; link and device changes arrive on `r.realtime.onE2eeLink` and `onE2eeDevices`, a contact's reset on `onE2eePeer`) |
|
|
79
79
|
| `r.brain` | `sendMessage` |
|
|
80
80
|
| `r.commands` | `execute`, `gatePrompt` |
|
|
81
81
|
| `r.projectBrain` | `tasks`, `decisions`, `milestones`, `completeTask`, `usage` |
|
|
@@ -121,6 +121,7 @@ for await (const chunk of r.agents.chatStream(agent.id, { message: 'Hello!' }))
|
|
|
121
121
|
| `r.moderation` | Moderation actions: `publicLog`, `mine`, `appeal`; staff `apply`, `resolveAppeal`, `list({ target_type?, target_id? })` (one target's recorded actions, newest first, at most 100; none named = the network's latest; 0.7.74) and `reverse(actionId)` (sent once: a repeat answers 404 `action_not_found`; 0.7.74); agent grants `listAgentGrants`, `grantAgent`, `revokeAgent` |
|
|
122
122
|
| `r.dispatcher` | Task queue, claims, signals, outcomes, webhooks |
|
|
123
123
|
| `r.goals` | Org goals with task rollups (group dispatcher tasks) |
|
|
124
|
+
| `r.linkPreview` | `get(url)` — a public link's Open Graph card (`data: null` when it has none). For a chat link pass `{ messageId }` or `{ conversationId }` on a signed-in key with `chat:read`: the link is fetched only when it is in the named message and a person wrote that message, or the caller wrote it in that conversation, else `refused: 'ai_link'`. A source with no id rejects (`ValidationError`). Not in encrypted chats: the server cannot read them, so leave `source` out there and apply the app's own rule |
|
|
124
125
|
| `r.protocols` | Private Minds safety: `sourceControls` (current enablement and saved choices; filter with `control`, each item may carry a `display` label), `setSourceControl`, `onSourceControlChange`, `reportSource`. Delegated accounts: `nativeSocialAccounts`, `startNativeSocialConnection`, `finishNativeSocialConnection`, `disconnectNativeSocialAccount`. Native follows: `nativeFollowStates` (every connected account's state for one source, one read), `nativeFollowState`, `prepareNativeFollow`, `submitNativeFollow`, `nativeFollowStatus` (native following or pending approval, distinct from a Minds feed subscription; preserve the complete signed Nostr list). Native reposts and undo: `nativeRepostState`, `prepareNativeRepost`, `submitNativeRepost`, `nativeRepostStatus` (public posts, explicit approval, connected account, no uncertain retries). Native likes and unlikes: `nativeLikeState`, `prepareNativeLike`, `submitNativeLike`, `nativeLikeStatus` (connected authority, app enablement and explicit approval; resume pending actions instead of duplicating them). Reviewed replies and optional linked posts: `prepareNativeReply`, `submitNativeReply`, `nativeReplyStatus`, `nativeReplyHistory`, `prepareNativeReplyRemoval`, `removeNativeReply` (require deployed API support and app enablement). Keyless shared post links: `sharedNetworks(projectId)`, `getSharedConversation(projectId, { protocol, post, readRef?, replies?: 'none' })` (`replies: 'none'`, 0.7.78: the post alone for a card, with `repliesStatus: 'not_requested'`; `getPublicConversation` and `getConnectedSocialConversation` take it too). Member public reading: `searchPublicPosts` (selected account required for Bluesky/ActivityPub; public Nostr search), `getConnectedSocialConversation` (preserve result readerAccountId/readRef), `publicNetworks`, `searchPublicProfiles`, `getPublicProfile`, `getPublicAuthorFeed`, `getFollowingFeed`, `getPublicConversation`, `getPublicTrends`, `getPublicTrendFeed`; radar stories: `getPublicRadarStory` (one story by id with its posts; one that left the radar within its minimum lifetime, or within the last half hour, still answers live with `liveUntil` and the story that continues it; one that has left for longer comes with its archived copy and the live story it continues as; the id may be a World Today edition story's, and the answer then names it in `editionStory` under the story's radar id; a story a World Today story seeded carries `volume`, the first page of the posts that story counts across Minds and the open networks, whose `cursor` goes on with `curator.storyPosts` or `curator.memberStoryPosts`), keyless `getSharedRadarStory(projectId, { cluster })` and `getSharedRadarPosts(projectId, { cluster })` (keyless reads carry images and videos only from outlets and members marked `visitorMedia`; signed-in reads keep all media, so a share card uses only those); the Viral read (fastest-moving posts across Bluesky, the Fediverse, Nostr and Minds, each against its own network's pace; 0.7.59): `getPublicViral({ network?, category?, media?: 'video', limit?: 5 | 10 | 20 | 40, cursor?, compass? })` under the member's key and keyless `getSharedViral(projectId, params)` (visitors: `compass` `all` or `hide` only; a visitor's `media: 'video'` read can carry items whose video the server withheld, so filter with the exported `viralItemHasVideo` before a video player; `item.post` narrows on `item.protocol`). `status: 'off'` resolves with no items until the app enables the read, and `getSharedViral` also resolves an older API's 401 `missing_api_key` as off; `getPublicViral` throws every 401 and 403, so a session failure is never read as off; private app subscriptions: `listSubscriptions`, `subscribeToProfile`, `unsubscribeFromProfile`, `onSubscriptionChange`. `list` returns bounded adapter `maturity`, `limitations`, and exhaustive `supported`/`planned`/`unsupported` implementation capabilities (not provider health or enablement); settings, candidate refresh/status, search, and read-only quarantined candidate review via `listCandidates` / `getCandidate` |
|
|
125
126
|
|
|
126
127
|
Full reference: see [llm.md](./llm.md) (ships with this package) or the type definitions in `dist/`.
|
|
@@ -360,6 +361,48 @@ export async function POST(req: NextRequest) {
|
|
|
360
361
|
|
|
361
362
|
Server-side only — the `server-only` guard fails the build if it's ever imported into a client component. Keep it out of `middleware.ts` as well (edge bundle size); gate routes with a plain cookie-presence check on a shared constant.
|
|
362
363
|
|
|
364
|
+
## Assistant subjects (`@recursiv/sdk/assistant`)
|
|
365
|
+
|
|
366
|
+
Two pure functions for asking an assistant about a post: no network, clock, randomness or Node-only API, so a server, a React Native phone and a web app build the same model input from the same data (unreleased).
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
import { buildSubjectBundle, renderBundleForModel } from '@recursiv/sdk/assistant'
|
|
370
|
+
|
|
371
|
+
const { bundle, refused, excluded } = buildSubjectBundle({
|
|
372
|
+
mode: 'standard', // or 'sealed'
|
|
373
|
+
asker: { id: me.id, blocked_ids, muted_ids }, // a server also passes blocked_by_ids
|
|
374
|
+
post, parent, quoted, replies, // data you already hold; nothing is fetched
|
|
375
|
+
})
|
|
376
|
+
if (!bundle) return showUnavailable() // make no model call; never show the asker `audit`
|
|
377
|
+
// boundary: fresh per call, e.g. 32 hex digits from crypto.getRandomValues
|
|
378
|
+
const { system, user, references } = renderBundleForModel(bundle, 'explain', { boundary })
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
- `buildSubjectBundle` leaves out posts and replies whose authors turned asks off (`ai_ask_allowed`: `'all'`, `'sealed_only'` read only in Sealed, `'off'`), authors the asker blocked or muted and authors who blocked the asker, known-minor authors (unless the asker is the author), posts under a child-safety hold and posts a moderator suppressed. Pictures go as alt text and the stored caption only, never bytes or addresses. Hidden text (zero-width, bidi and tag characters, HTML comments and hidden elements, invisible markdown) is stripped, and the top 20 replies and every field are capped. Every safety flag fails closed, and an id list it cannot read throws.
|
|
382
|
+
- `known_minor`, `child_safety_hold`, `assistant_suppressed`, media `held` and `blocked_by_ids` are server-side inputs: never send them to another member's client. A client builder passes each post's `assist_readable` (`'all'`, `'sealed_only'`, `'none'`), the server's verdict for that viewer, which does not say why.
|
|
383
|
+
- `refused` and `excluded` are what the asker may be told: blocks, minors, holds and suppression all read `'unavailable'`, so they never say who blocked the asker, whose account is a minor's or that a post is held. `audit` holds the detail for the server's log only.
|
|
384
|
+
- `renderBundleForModel(bundle, intent, { boundary, language? })` spotlights the bundle for `explain`, `summarise`, `fact_context`, `translate` or `describe`: the content sits between two lines carrying your per-call random boundary, every member-written line starts with `| `, each item is labelled with its id and handle, and `references` lists the only ids, handles and addresses an answer may use (never a picture address; write them only as plain links, never render an image the model wrote). Render only bundles built from your own reads.
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
### Fixed election and crisis cards
|
|
388
|
+
|
|
389
|
+
Before a model answers, check the member's own words for a fixed card. Pure, no model call, the same card on a server and a phone (unreleased):
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
import { fixedCardAnswer, matchFixedCard } from '@recursiv/sdk/assistant'
|
|
393
|
+
|
|
394
|
+
const match = matchFixedCard(question, { country: 'US', locale: 'en-US' }) // country: ISO 3166-1 alpha-2, if known
|
|
395
|
+
if (match) {
|
|
396
|
+
show(match.card) // { kind, id, title, lines, phones, links, as_of }
|
|
397
|
+
if (fixedCardAnswer(match.kind, Date.now()) === 'card_only') return // election before 2026-11-04T12:00Z: no model call
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
- `crisis`: self-harm or suicide risk (first person, plans, means, worry about someone else) or immediate danger. The card gives the country's crisis line and emergency number, or the generic card (the US line, labelled, and findahelpline.com). Danger from someone else, or a medical emergency, leads with the emergency number (the police first where that is a separate number, as in Brazil, Chile and Peru: id `crisis.XX.danger`); worry about someone else gets a line addressed to the helper (`crisis.XX.other`). It always shows first; the turn may go on.
|
|
402
|
+
- Matching runs in time linear in the message. Past 20,000 characters (after invisible ones are dropped), the start and the end are read.
|
|
403
|
+
- `election`: only clear logistics (how, where or when to vote, registering, deadlines, polling places, mail or absentee ballots, voter ID, checking registration or a ballot). Never candidates, results, polls, fraud claims or votes on a post or poll. The US card points to vote.gov and CanIVote.org and the state election office; elsewhere the country's election authority, or a generic line.
|
|
404
|
+
- English and Spanish. Every number and address has a cited source and an `as_of` date (`CRISIS_LINES`, `ELECTION_SITES`); a country with no checked entry gets the generic card, never a guessed number. `fixedCardFor(kind, options)` gives a card without a message.
|
|
405
|
+
|
|
363
406
|
## Error handling
|
|
364
407
|
|
|
365
408
|
```typescript
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The numbers and addresses the fixed election and crisis cards show (George plan G6b, risks 3 and 22). Every one was
|
|
3
|
+
* checked against the source cited beside it on FIXED_CARD_DATA_AS_OF; nothing here is filled in from memory, and a
|
|
4
|
+
* country with no checked entry gets the generic card, never a guessed number or address.
|
|
5
|
+
*
|
|
6
|
+
* Crisis lines come from findahelpline.com (ThroughLine's maintained directory, the per-country source the plan
|
|
7
|
+
* names) and, where the service publishes its own page, from that page too. Emergency numbers are findahelpline.com's
|
|
8
|
+
* per-country `emergencyNumber`; where that reaches an ambulance alone, the police number is added from a government
|
|
9
|
+
* page. Election sites are each country's own election authority, or the government's page that points to it.
|
|
10
|
+
*
|
|
11
|
+
* To change an entry: check it at its source, update `sources`, the comment above the entry and
|
|
12
|
+
* FIXED_CARD_DATA_AS_OF, and keep the contract test passing (it holds every number and address to the comment above
|
|
13
|
+
* its entry). Plain, frozen data only, so a phone and the server show the same card.
|
|
14
|
+
*/
|
|
15
|
+
/** The day every number and address below was last checked against its sources. */
|
|
16
|
+
export declare const FIXED_CARD_DATA_AS_OF = "2026-10-08";
|
|
17
|
+
/** Words in each card language. */
|
|
18
|
+
export interface LocalizedText {
|
|
19
|
+
readonly en: string;
|
|
20
|
+
readonly es: string;
|
|
21
|
+
}
|
|
22
|
+
export interface CrisisLineData {
|
|
23
|
+
/** The service's own name, shown as written. */
|
|
24
|
+
readonly name: string;
|
|
25
|
+
/** The number as the service writes it. */
|
|
26
|
+
readonly number: string;
|
|
27
|
+
/** What a phone dials: digits, keeping a leading `*` (Chile's *4141). */
|
|
28
|
+
readonly dial: string;
|
|
29
|
+
/** The number to text, when the service takes texts; null when it is calls only. */
|
|
30
|
+
readonly text: string | null;
|
|
31
|
+
/** Open every hour of every day. Every line chosen below is. */
|
|
32
|
+
readonly always_open: boolean;
|
|
33
|
+
/** What to do after dialling, in each card language, when the service needs it (Peru's option 5). */
|
|
34
|
+
readonly after_dialling: LocalizedText | null;
|
|
35
|
+
/** One more line for a card in that language only (the 988 Lifeline's Spanish service). */
|
|
36
|
+
readonly extra_line: Partial<LocalizedText> | null;
|
|
37
|
+
/** Where `number`, `text` and `always_open` were checked. */
|
|
38
|
+
readonly sources: readonly string[];
|
|
39
|
+
}
|
|
40
|
+
/** One emergency number and where it was checked. */
|
|
41
|
+
export interface EmergencyNumberData {
|
|
42
|
+
readonly number: string;
|
|
43
|
+
readonly sources: readonly string[];
|
|
44
|
+
}
|
|
45
|
+
export interface CrisisCountryData {
|
|
46
|
+
readonly name: LocalizedText;
|
|
47
|
+
/** The first line is the one the card leads with. */
|
|
48
|
+
readonly lines: readonly CrisisLineData[];
|
|
49
|
+
/**
|
|
50
|
+
* findahelpline.com's emergency number for the country: one that reaches every service, or, where `police` is set,
|
|
51
|
+
* the ambulance alone.
|
|
52
|
+
*/
|
|
53
|
+
readonly emergency: string;
|
|
54
|
+
readonly emergency_sources: readonly string[];
|
|
55
|
+
/**
|
|
56
|
+
* Set only where `emergency` reaches an ambulance alone (Brazil's SAMU, Chile's 131, Peru's SAMU): the police
|
|
57
|
+
* number, which a card about danger from someone else leads with. A card always shows both.
|
|
58
|
+
*/
|
|
59
|
+
readonly police?: EmergencyNumberData;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Per-country crisis lines, keyed by ISO 3166-1 alpha-2 code. Each line is free and open all day, every day, by its
|
|
63
|
+
* source. Kept to countries where that could be checked. The comment above each entry names every number in it and
|
|
64
|
+
* where it was checked; the contract test holds each number to its comment.
|
|
65
|
+
*/
|
|
66
|
+
export declare const CRISIS_LINES: Readonly<Record<string, CrisisCountryData>>;
|
|
67
|
+
/** The directory the generic crisis card points to when the member's country is unknown or has no entry above. */
|
|
68
|
+
export declare const CRISIS_DIRECTORY: {
|
|
69
|
+
readonly name: "findahelpline.com";
|
|
70
|
+
readonly url: "https://findahelpline.com";
|
|
71
|
+
readonly sources: readonly ["https://findahelpline.com"];
|
|
72
|
+
};
|
|
73
|
+
export interface ElectionSiteData {
|
|
74
|
+
/** Who runs it, as they name themselves. */
|
|
75
|
+
readonly name: string;
|
|
76
|
+
readonly url: string;
|
|
77
|
+
/** Where the address was checked. */
|
|
78
|
+
readonly sources: readonly string[];
|
|
79
|
+
}
|
|
80
|
+
export interface ElectionCountryData {
|
|
81
|
+
readonly name: LocalizedText;
|
|
82
|
+
readonly sites: readonly ElectionSiteData[];
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Official election sites, keyed by ISO 3166-1 alpha-2 code: each country's national election authority, or the
|
|
86
|
+
* government page for voters. A country not listed gets the generic line, never a guessed address. Some of these
|
|
87
|
+
* hosts refuse scripted requests (403), so they were checked by hand on the date above.
|
|
88
|
+
*/
|
|
89
|
+
export declare const ELECTION_SITES: Readonly<Record<string, ElectionCountryData>>;
|
|
90
|
+
/** Common names for a country that are not its ISO 3166-1 alpha-2 code. */
|
|
91
|
+
export declare const COUNTRY_ALIASES: Readonly<Record<string, string>>;
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The numbers and addresses the fixed election and crisis cards show (George plan G6b, risks 3 and 22). Every one was
|
|
3
|
+
* checked against the source cited beside it on FIXED_CARD_DATA_AS_OF; nothing here is filled in from memory, and a
|
|
4
|
+
* country with no checked entry gets the generic card, never a guessed number or address.
|
|
5
|
+
*
|
|
6
|
+
* Crisis lines come from findahelpline.com (ThroughLine's maintained directory, the per-country source the plan
|
|
7
|
+
* names) and, where the service publishes its own page, from that page too. Emergency numbers are findahelpline.com's
|
|
8
|
+
* per-country `emergencyNumber`; where that reaches an ambulance alone, the police number is added from a government
|
|
9
|
+
* page. Election sites are each country's own election authority, or the government's page that points to it.
|
|
10
|
+
*
|
|
11
|
+
* To change an entry: check it at its source, update `sources`, the comment above the entry and
|
|
12
|
+
* FIXED_CARD_DATA_AS_OF, and keep the contract test passing (it holds every number and address to the comment above
|
|
13
|
+
* its entry). Plain, frozen data only, so a phone and the server show the same card.
|
|
14
|
+
*/
|
|
15
|
+
/** The day every number and address below was last checked against its sources. */
|
|
16
|
+
export const FIXED_CARD_DATA_AS_OF = '2026-10-08';
|
|
17
|
+
/** Freezes plain data all the way down, so no importer can change what a later card shows. Hermes-safe. */
|
|
18
|
+
function deepFreeze(value) {
|
|
19
|
+
if (value !== null && typeof value === 'object' && !Object.isFrozen(value)) {
|
|
20
|
+
Object.freeze(value);
|
|
21
|
+
for (const key of Object.keys(value))
|
|
22
|
+
deepFreeze(value[key]);
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
const FIND_A_HELPLINE = (country) => `https://findahelpline.com/countries/${country}`;
|
|
27
|
+
/**
|
|
28
|
+
* Per-country crisis lines, keyed by ISO 3166-1 alpha-2 code. Each line is free and open all day, every day, by its
|
|
29
|
+
* source. Kept to countries where that could be checked. The comment above each entry names every number in it and
|
|
30
|
+
* where it was checked; the contract test holds each number to its comment.
|
|
31
|
+
*/
|
|
32
|
+
export const CRISIS_LINES = deepFreeze({
|
|
33
|
+
// US: 988 Suicide & Crisis Lifeline, call or text 988. Spanish: press 2 on a call, or text AYUDA to 988.
|
|
34
|
+
// Source: https://988lifeline.org, its FAQ on other languages,
|
|
35
|
+
// https://988lifeline.org/faq/calling-the-988-lifeline/faq-is-the-988-lifeline-available-in-other-languages-for-non-english-speakers/
|
|
36
|
+
// and findahelpline.com/countries/us. Emergency: 911, per findahelpline.com/countries/us.
|
|
37
|
+
US: {
|
|
38
|
+
name: { en: 'the US', es: 'EE. UU.' },
|
|
39
|
+
lines: [{
|
|
40
|
+
name: '988 Suicide & Crisis Lifeline', number: '988', dial: '988', text: '988', always_open: true,
|
|
41
|
+
after_dialling: null,
|
|
42
|
+
extra_line: { es: 'Para hablar en español, llama al 988 y oprime 2, o envía AYUDA por mensaje de texto al 988.' },
|
|
43
|
+
sources: [
|
|
44
|
+
'https://988lifeline.org',
|
|
45
|
+
'https://988lifeline.org/faq/calling-the-988-lifeline/faq-is-the-988-lifeline-available-in-other-languages-for-non-english-speakers/',
|
|
46
|
+
FIND_A_HELPLINE('us'),
|
|
47
|
+
],
|
|
48
|
+
}],
|
|
49
|
+
emergency: '911',
|
|
50
|
+
emergency_sources: [FIND_A_HELPLINE('us')],
|
|
51
|
+
},
|
|
52
|
+
// Canada: 9-8-8 Suicide Crisis Helpline, call or text 988, 24/7. Source: https://988.ca; findahelpline.com/countries/ca.
|
|
53
|
+
// Emergency: 911, per findahelpline.com/countries/ca.
|
|
54
|
+
CA: {
|
|
55
|
+
name: { en: 'Canada', es: 'Canadá' },
|
|
56
|
+
lines: [{
|
|
57
|
+
name: '9-8-8 Suicide Crisis Helpline', number: '9-8-8', dial: '988', text: '988', always_open: true,
|
|
58
|
+
after_dialling: null, extra_line: null,
|
|
59
|
+
sources: ['https://988.ca', FIND_A_HELPLINE('ca')],
|
|
60
|
+
}],
|
|
61
|
+
emergency: '911',
|
|
62
|
+
emergency_sources: [FIND_A_HELPLINE('ca')],
|
|
63
|
+
},
|
|
64
|
+
// UK: Samaritans, 116 123, free, 24/7. Source: https://www.samaritans.org/how-we-can-help/contact-samaritan/;
|
|
65
|
+
// findahelpline.com/countries/gb. Emergency: 999, per findahelpline.com/countries/gb.
|
|
66
|
+
GB: {
|
|
67
|
+
name: { en: 'the UK', es: 'el Reino Unido' },
|
|
68
|
+
lines: [{
|
|
69
|
+
name: 'Samaritans', number: '116 123', dial: '116123', text: null, always_open: true, after_dialling: null, extra_line: null,
|
|
70
|
+
sources: ['https://www.samaritans.org/how-we-can-help/contact-samaritan/', FIND_A_HELPLINE('gb')],
|
|
71
|
+
}],
|
|
72
|
+
emergency: '999',
|
|
73
|
+
emergency_sources: [FIND_A_HELPLINE('gb')],
|
|
74
|
+
},
|
|
75
|
+
// Ireland: Samaritans Ireland, 116 123, 24/7. Source: https://www.samaritans.org/samaritans-ireland/;
|
|
76
|
+
// findahelpline.com/countries/ie. Emergency: 112, per findahelpline.com/countries/ie.
|
|
77
|
+
IE: {
|
|
78
|
+
name: { en: 'Ireland', es: 'Irlanda' },
|
|
79
|
+
lines: [{
|
|
80
|
+
name: 'Samaritans', number: '116 123', dial: '116123', text: null, always_open: true, after_dialling: null, extra_line: null,
|
|
81
|
+
sources: ['https://www.samaritans.org/samaritans-ireland/', FIND_A_HELPLINE('ie')],
|
|
82
|
+
}],
|
|
83
|
+
emergency: '112',
|
|
84
|
+
emergency_sources: [FIND_A_HELPLINE('ie')],
|
|
85
|
+
},
|
|
86
|
+
// Australia: Lifeline, call 13 11 14 or text 0477 13 11 14, 24/7. Source: https://www.lifeline.org.au;
|
|
87
|
+
// findahelpline.com/countries/au. Emergency: 000, per findahelpline.com/countries/au.
|
|
88
|
+
AU: {
|
|
89
|
+
name: { en: 'Australia', es: 'Australia' },
|
|
90
|
+
lines: [{
|
|
91
|
+
name: 'Lifeline', number: '13 11 14', dial: '131114', text: '0477 13 11 14', always_open: true,
|
|
92
|
+
after_dialling: null, extra_line: null,
|
|
93
|
+
sources: ['https://www.lifeline.org.au', FIND_A_HELPLINE('au')],
|
|
94
|
+
}],
|
|
95
|
+
emergency: '000',
|
|
96
|
+
emergency_sources: [FIND_A_HELPLINE('au')],
|
|
97
|
+
},
|
|
98
|
+
// New Zealand: 1737, call or text 1737, 24/7. Source: https://1737.org.nz; findahelpline.com/countries/nz.
|
|
99
|
+
// Emergency: 111, per findahelpline.com/countries/nz.
|
|
100
|
+
NZ: {
|
|
101
|
+
name: { en: 'New Zealand', es: 'Nueva Zelanda' },
|
|
102
|
+
lines: [{
|
|
103
|
+
name: '1737', number: '1737', dial: '1737', text: '1737', always_open: true, after_dialling: null, extra_line: null,
|
|
104
|
+
sources: ['https://1737.org.nz', FIND_A_HELPLINE('nz')],
|
|
105
|
+
}],
|
|
106
|
+
emergency: '111',
|
|
107
|
+
emergency_sources: [FIND_A_HELPLINE('nz')],
|
|
108
|
+
},
|
|
109
|
+
// Mexico: Línea de la Vida (CONASAMA, Secretaría de Salud), 800 911 2000, 24/7.
|
|
110
|
+
// Source: https://www.gob.mx/conasama/articulos/linea-de-la-vida-800-911-2000; findahelpline.com/countries/mx.
|
|
111
|
+
// Emergency: 911, per findahelpline.com/countries/mx.
|
|
112
|
+
MX: {
|
|
113
|
+
name: { en: 'Mexico', es: 'México' },
|
|
114
|
+
lines: [{
|
|
115
|
+
name: 'Línea de la Vida', number: '800 911 2000', dial: '8009112000', text: null, always_open: true,
|
|
116
|
+
after_dialling: null, extra_line: null,
|
|
117
|
+
sources: ['https://www.gob.mx/conasama/articulos/linea-de-la-vida-800-911-2000', FIND_A_HELPLINE('mx')],
|
|
118
|
+
}],
|
|
119
|
+
emergency: '911',
|
|
120
|
+
emergency_sources: [FIND_A_HELPLINE('mx')],
|
|
121
|
+
},
|
|
122
|
+
// Spain: Línea 024 de atención a la conducta suicida (Ministerio de Sanidad), 024, 24/7.
|
|
123
|
+
// Source: https://www.sanidad.gob.es/linea024/home.htm; findahelpline.com/countries/es.
|
|
124
|
+
// Emergency: 112, per the Línea 024 page and findahelpline.com/countries/es.
|
|
125
|
+
ES: {
|
|
126
|
+
name: { en: 'Spain', es: 'España' },
|
|
127
|
+
lines: [{
|
|
128
|
+
name: 'Línea 024', number: '024', dial: '024', text: null, always_open: true, after_dialling: null, extra_line: null,
|
|
129
|
+
sources: ['https://www.sanidad.gob.es/linea024/home.htm', FIND_A_HELPLINE('es')],
|
|
130
|
+
}],
|
|
131
|
+
emergency: '112',
|
|
132
|
+
emergency_sources: ['https://www.sanidad.gob.es/linea024/home.htm', FIND_A_HELPLINE('es')],
|
|
133
|
+
},
|
|
134
|
+
// Argentina: Línea Nacional de Orientación y Apoyo en la Urgencia de Salud Mental, 0800-999-0091, 24/7, 365 days.
|
|
135
|
+
// Source: https://www.argentina.gob.ar/dispositivo-0800; findahelpline.com/countries/ar. (The Centro de Asistencia
|
|
136
|
+
// al Suicida, 0800 345 1435, is open 08:00 to midnight, so it is not the line the card leads with.)
|
|
137
|
+
// Emergency: 911, per findahelpline.com/countries/ar. No police number is added: 911 is the police line where it
|
|
138
|
+
// runs, so a danger card already leads with the police.
|
|
139
|
+
AR: {
|
|
140
|
+
name: { en: 'Argentina', es: 'Argentina' },
|
|
141
|
+
lines: [{
|
|
142
|
+
name: 'Línea de Urgencia en Salud Mental', number: '0800-999-0091', dial: '08009990091', text: null, always_open: true,
|
|
143
|
+
after_dialling: null, extra_line: null,
|
|
144
|
+
sources: ['https://www.argentina.gob.ar/dispositivo-0800', FIND_A_HELPLINE('ar')],
|
|
145
|
+
}],
|
|
146
|
+
emergency: '911',
|
|
147
|
+
emergency_sources: [FIND_A_HELPLINE('ar')],
|
|
148
|
+
},
|
|
149
|
+
// Colombia: Línea 106 Nacional de Salud Mental (Ministerio de Salud), 106, 24/7. Source: findahelpline.com/countries/co;
|
|
150
|
+
// MinSalud, Directorio Nacional de Líneas de Atención en Salud Mental (August 2025):
|
|
151
|
+
// https://www.minsalud.gov.co/sites/rid/Lists/BibliotecaDigital/RIDE/VS/PP/ET/directorio-salud-mental-prevencion-suicidio-minsalud.pdf
|
|
152
|
+
// Emergency: 123, per findahelpline.com/countries/co.
|
|
153
|
+
CO: {
|
|
154
|
+
name: { en: 'Colombia', es: 'Colombia' },
|
|
155
|
+
lines: [{
|
|
156
|
+
name: 'Línea 106', number: '106', dial: '106', text: null, always_open: true, after_dialling: null, extra_line: null,
|
|
157
|
+
sources: [
|
|
158
|
+
FIND_A_HELPLINE('co'),
|
|
159
|
+
'https://www.minsalud.gov.co/sites/rid/Lists/BibliotecaDigital/RIDE/VS/PP/ET/directorio-salud-mental-prevencion-suicidio-minsalud.pdf',
|
|
160
|
+
],
|
|
161
|
+
}],
|
|
162
|
+
emergency: '123',
|
|
163
|
+
emergency_sources: [FIND_A_HELPLINE('co')],
|
|
164
|
+
},
|
|
165
|
+
// Chile: *4141 Línea de Prevención del Suicidio (MINSAL), 24/7.
|
|
166
|
+
// Source: https://www.gob.cl/noticias/linea-prevencion-suicidio-4141-24-horas-atencion-balance-cifras/;
|
|
167
|
+
// findahelpline.com/countries/cl. Emergency: 131 (ambulance, SAMU), per findahelpline.com/countries/cl.
|
|
168
|
+
// Police: 133 (Carabineros), free, 24 hours, per https://www.chileatiende.gob.cl/fichas/139663-fono-emergencias-133.
|
|
169
|
+
CL: {
|
|
170
|
+
name: { en: 'Chile', es: 'Chile' },
|
|
171
|
+
lines: [{
|
|
172
|
+
name: 'Línea de Prevención del Suicidio', number: '*4141', dial: '*4141', text: null, always_open: true,
|
|
173
|
+
after_dialling: null, extra_line: null,
|
|
174
|
+
sources: ['https://www.gob.cl/noticias/linea-prevencion-suicidio-4141-24-horas-atencion-balance-cifras/', FIND_A_HELPLINE('cl')],
|
|
175
|
+
}],
|
|
176
|
+
emergency: '131',
|
|
177
|
+
emergency_sources: [FIND_A_HELPLINE('cl')],
|
|
178
|
+
police: { number: '133', sources: ['https://www.chileatiende.gob.cl/fichas/139663-fono-emergencias-133'] },
|
|
179
|
+
},
|
|
180
|
+
// Peru: Línea 113 Salud (Minsa), 113, option 5 for mental health, 24/7. Source: https://www.gob.pe/555;
|
|
181
|
+
// findahelpline.com/countries/pe ("Al marcar, selecciona la opción 5"). Emergency: 106 (ambulance, SAMU), per
|
|
182
|
+
// findahelpline.com/countries/pe. Police: 105 (Policía Nacional del Perú), per its page of emergency numbers,
|
|
183
|
+
// https://www.gob.pe/institucion/pnp/contacto-y-numeros-de-emergencias.
|
|
184
|
+
PE: {
|
|
185
|
+
name: { en: 'Peru', es: 'Perú' },
|
|
186
|
+
lines: [{
|
|
187
|
+
name: 'Línea 113 Salud', number: '113', dial: '113', text: null, always_open: true,
|
|
188
|
+
after_dialling: { en: 'choose option 5', es: 'elige la opción 5' }, extra_line: null,
|
|
189
|
+
sources: ['https://www.gob.pe/555', FIND_A_HELPLINE('pe')],
|
|
190
|
+
}],
|
|
191
|
+
emergency: '106',
|
|
192
|
+
emergency_sources: [FIND_A_HELPLINE('pe')],
|
|
193
|
+
police: { number: '105', sources: ['https://www.gob.pe/institucion/pnp/contacto-y-numeros-de-emergencias'] },
|
|
194
|
+
},
|
|
195
|
+
// Brazil: CVV (Centro de Valorização da Vida), 188, 24 hours, free. Source: https://cvv.org.br;
|
|
196
|
+
// findahelpline.com/countries/br. Emergency: 192 (ambulance, SAMU), per findahelpline.com/countries/br and the
|
|
197
|
+
// Ministério das Comunicações. Police: 190 (Polícia Militar), per the Ministério das Comunicações,
|
|
198
|
+
// https://www.gov.br/mcom/pt-br/noticias/noticias_alt/2026/setembro/emergencia-voce-sabe-para-quem-ligar-quando-precisa-de-ajuda
|
|
199
|
+
BR: {
|
|
200
|
+
name: { en: 'Brazil', es: 'Brasil' },
|
|
201
|
+
lines: [{
|
|
202
|
+
name: 'CVV', number: '188', dial: '188', text: null, always_open: true, after_dialling: null, extra_line: null,
|
|
203
|
+
sources: ['https://cvv.org.br', FIND_A_HELPLINE('br')],
|
|
204
|
+
}],
|
|
205
|
+
emergency: '192',
|
|
206
|
+
emergency_sources: [
|
|
207
|
+
FIND_A_HELPLINE('br'),
|
|
208
|
+
'https://www.gov.br/mcom/pt-br/noticias/noticias_alt/2026/setembro/emergencia-voce-sabe-para-quem-ligar-quando-precisa-de-ajuda',
|
|
209
|
+
],
|
|
210
|
+
police: {
|
|
211
|
+
number: '190',
|
|
212
|
+
sources: ['https://www.gov.br/mcom/pt-br/noticias/noticias_alt/2026/setembro/emergencia-voce-sabe-para-quem-ligar-quando-precisa-de-ajuda'],
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
// France: 3114, numéro national de prévention du suicide, 24/7. Source: https://3114.fr;
|
|
216
|
+
// findahelpline.com/countries/fr. Emergency: 112, per findahelpline.com/countries/fr.
|
|
217
|
+
FR: {
|
|
218
|
+
name: { en: 'France', es: 'Francia' },
|
|
219
|
+
lines: [{
|
|
220
|
+
name: '3114', number: '3114', dial: '3114', text: null, always_open: true, after_dialling: null, extra_line: null,
|
|
221
|
+
sources: ['https://3114.fr', FIND_A_HELPLINE('fr')],
|
|
222
|
+
}],
|
|
223
|
+
emergency: '112',
|
|
224
|
+
emergency_sources: [FIND_A_HELPLINE('fr')],
|
|
225
|
+
},
|
|
226
|
+
// Germany: TelefonSeelsorge, 0800 111 0 111 (also 0800 111 0 222 and 116 123), 24/7.
|
|
227
|
+
// Source: https://www.telefonseelsorge.de; findahelpline.com/countries/de. Emergency: 112, per
|
|
228
|
+
// findahelpline.com/countries/de.
|
|
229
|
+
DE: {
|
|
230
|
+
name: { en: 'Germany', es: 'Alemania' },
|
|
231
|
+
lines: [{
|
|
232
|
+
name: 'TelefonSeelsorge', number: '0800 111 0 111', dial: '08001110111', text: null, always_open: true,
|
|
233
|
+
after_dialling: null, extra_line: null,
|
|
234
|
+
sources: ['https://www.telefonseelsorge.de', FIND_A_HELPLINE('de')],
|
|
235
|
+
}],
|
|
236
|
+
emergency: '112',
|
|
237
|
+
emergency_sources: [FIND_A_HELPLINE('de')],
|
|
238
|
+
},
|
|
239
|
+
// India: Tele MANAS (Ministry of Health and Family Welfare), 14416, 24/7. Source:
|
|
240
|
+
// https://telemanas.mohfw.gov.in/home; findahelpline.com/countries/in. Emergency: 112, per
|
|
241
|
+
// findahelpline.com/countries/in.
|
|
242
|
+
IN: {
|
|
243
|
+
name: { en: 'India', es: 'la India' },
|
|
244
|
+
lines: [{
|
|
245
|
+
name: 'Tele MANAS', number: '14416', dial: '14416', text: null, always_open: true, after_dialling: null, extra_line: null,
|
|
246
|
+
sources: ['https://telemanas.mohfw.gov.in/home', FIND_A_HELPLINE('in')],
|
|
247
|
+
}],
|
|
248
|
+
emergency: '112',
|
|
249
|
+
emergency_sources: [FIND_A_HELPLINE('in')],
|
|
250
|
+
},
|
|
251
|
+
// Netherlands: 113 Zelfmoordpreventie, 113, 24/7. Source: https://www.113.nl; findahelpline.com/countries/nl.
|
|
252
|
+
// Emergency: 112, per findahelpline.com/countries/nl.
|
|
253
|
+
NL: {
|
|
254
|
+
name: { en: 'the Netherlands', es: 'los Países Bajos' },
|
|
255
|
+
lines: [{
|
|
256
|
+
name: '113 Zelfmoordpreventie', number: '113', dial: '113', text: null, always_open: true, after_dialling: null, extra_line: null,
|
|
257
|
+
sources: ['https://www.113.nl', FIND_A_HELPLINE('nl')],
|
|
258
|
+
}],
|
|
259
|
+
emergency: '112',
|
|
260
|
+
emergency_sources: [FIND_A_HELPLINE('nl')],
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
/** The directory the generic crisis card points to when the member's country is unknown or has no entry above. */
|
|
264
|
+
export const CRISIS_DIRECTORY = deepFreeze({
|
|
265
|
+
name: 'findahelpline.com',
|
|
266
|
+
url: 'https://findahelpline.com',
|
|
267
|
+
// Source: https://findahelpline.com (ThroughLine's international helpline directory), checked 2026-10-08.
|
|
268
|
+
sources: ['https://findahelpline.com'],
|
|
269
|
+
});
|
|
270
|
+
/**
|
|
271
|
+
* Official election sites, keyed by ISO 3166-1 alpha-2 code: each country's national election authority, or the
|
|
272
|
+
* government page for voters. A country not listed gets the generic line, never a guessed address. Some of these
|
|
273
|
+
* hosts refuse scripted requests (403), so they were checked by hand on the date above.
|
|
274
|
+
*/
|
|
275
|
+
export const ELECTION_SITES = deepFreeze({
|
|
276
|
+
// US: vote.gov (U.S. government, GSA) links to every state's election office; CanIVote.org (the National
|
|
277
|
+
// Association of Secretaries of State) finds a state's site and polling place. The plan's "Find my state's site"
|
|
278
|
+
// button. Source: https://vote.gov, https://www.canivote.org (redirects to https://www.nass.org/can-I-vote).
|
|
279
|
+
US: {
|
|
280
|
+
name: { en: 'the US', es: 'EE. UU.' },
|
|
281
|
+
sites: [
|
|
282
|
+
{ name: 'vote.gov', url: 'https://vote.gov', sources: ['https://vote.gov'] },
|
|
283
|
+
{ name: 'CanIVote.org', url: 'https://www.canivote.org', sources: ['https://www.nass.org/can-I-vote'] },
|
|
284
|
+
],
|
|
285
|
+
},
|
|
286
|
+
// Canada: Elections Canada (federal). Source: https://www.elections.ca.
|
|
287
|
+
CA: {
|
|
288
|
+
name: { en: 'Canada', es: 'Canadá' },
|
|
289
|
+
sites: [{ name: 'Elections Canada', url: 'https://www.elections.ca', sources: ['https://www.elections.ca'] }],
|
|
290
|
+
},
|
|
291
|
+
// UK: the Electoral Commission. Source: https://www.electoralcommission.org.uk.
|
|
292
|
+
GB: {
|
|
293
|
+
name: { en: 'the UK', es: 'el Reino Unido' },
|
|
294
|
+
sites: [{
|
|
295
|
+
name: 'Electoral Commission', url: 'https://www.electoralcommission.org.uk',
|
|
296
|
+
sources: ['https://www.electoralcommission.org.uk'],
|
|
297
|
+
}],
|
|
298
|
+
},
|
|
299
|
+
// Ireland: An Coimisiún Toghcháin, the Electoral Commission. Source: https://www.electoralcommission.ie.
|
|
300
|
+
IE: {
|
|
301
|
+
name: { en: 'Ireland', es: 'Irlanda' },
|
|
302
|
+
sites: [{ name: 'Electoral Commission', url: 'https://www.electoralcommission.ie', sources: ['https://www.electoralcommission.ie'] }],
|
|
303
|
+
},
|
|
304
|
+
// Australia: the Australian Electoral Commission. Source: https://www.aec.gov.au.
|
|
305
|
+
AU: {
|
|
306
|
+
name: { en: 'Australia', es: 'Australia' },
|
|
307
|
+
sites: [{ name: 'Australian Electoral Commission', url: 'https://www.aec.gov.au', sources: ['https://www.aec.gov.au'] }],
|
|
308
|
+
},
|
|
309
|
+
// New Zealand: the Electoral Commission (elections.org.nz redirects here). Source: https://vote.nz.
|
|
310
|
+
NZ: {
|
|
311
|
+
name: { en: 'New Zealand', es: 'Nueva Zelanda' },
|
|
312
|
+
sites: [{ name: 'Electoral Commission', url: 'https://vote.nz', sources: ['https://vote.nz', 'https://www.elections.org.nz'] }],
|
|
313
|
+
},
|
|
314
|
+
// India: the Election Commission of India. Source: https://www.eci.gov.in.
|
|
315
|
+
IN: {
|
|
316
|
+
name: { en: 'India', es: 'la India' },
|
|
317
|
+
sites: [{ name: 'Election Commission of India', url: 'https://www.eci.gov.in', sources: ['https://www.eci.gov.in'] }],
|
|
318
|
+
},
|
|
319
|
+
// Mexico: the Instituto Nacional Electoral. Source: https://www.ine.mx.
|
|
320
|
+
MX: {
|
|
321
|
+
name: { en: 'Mexico', es: 'México' },
|
|
322
|
+
sites: [{ name: 'Instituto Nacional Electoral', url: 'https://www.ine.mx', sources: ['https://www.ine.mx'] }],
|
|
323
|
+
},
|
|
324
|
+
// Brazil: the Tribunal Superior Eleitoral. Source: https://www.tse.jus.br.
|
|
325
|
+
BR: {
|
|
326
|
+
name: { en: 'Brazil', es: 'Brasil' },
|
|
327
|
+
sites: [{ name: 'Tribunal Superior Eleitoral', url: 'https://www.tse.jus.br', sources: ['https://www.tse.jus.br'] }],
|
|
328
|
+
},
|
|
329
|
+
// Spain: the INE's Oficina del Censo Electoral (the electoral roll and where to vote). Source:
|
|
330
|
+
// https://www.ine.es/oficina_censo/.
|
|
331
|
+
ES: {
|
|
332
|
+
name: { en: 'Spain', es: 'España' },
|
|
333
|
+
sites: [{
|
|
334
|
+
name: 'Oficina del Censo Electoral del INE', url: 'https://www.ine.es/oficina_censo/',
|
|
335
|
+
sources: ['https://www.ine.es/oficina_censo/'],
|
|
336
|
+
}],
|
|
337
|
+
},
|
|
338
|
+
// Argentina: the Justicia Nacional Electoral's padrón lookup (where you vote). Source: https://www.padron.gob.ar.
|
|
339
|
+
AR: {
|
|
340
|
+
name: { en: 'Argentina', es: 'Argentina' },
|
|
341
|
+
sites: [{ name: 'Justicia Nacional Electoral', url: 'https://www.padron.gob.ar', sources: ['https://www.padron.gob.ar'] }],
|
|
342
|
+
},
|
|
343
|
+
// Colombia: the Registraduría Nacional del Estado Civil. Source: https://www.registraduria.gov.co.
|
|
344
|
+
CO: {
|
|
345
|
+
name: { en: 'Colombia', es: 'Colombia' },
|
|
346
|
+
sites: [{
|
|
347
|
+
name: 'Registraduría Nacional del Estado Civil', url: 'https://www.registraduria.gov.co',
|
|
348
|
+
sources: ['https://www.registraduria.gov.co'],
|
|
349
|
+
}],
|
|
350
|
+
},
|
|
351
|
+
// Chile: the Servicio Electoral de Chile (Servel). Source: https://www.servel.cl.
|
|
352
|
+
CL: {
|
|
353
|
+
name: { en: 'Chile', es: 'Chile' },
|
|
354
|
+
sites: [{ name: 'Servicio Electoral de Chile', url: 'https://www.servel.cl', sources: ['https://www.servel.cl'] }],
|
|
355
|
+
},
|
|
356
|
+
// Germany: Die Bundeswahlleiterin (the Federal Returning Officer). Source: https://www.bundeswahlleiterin.de.
|
|
357
|
+
DE: {
|
|
358
|
+
name: { en: 'Germany', es: 'Alemania' },
|
|
359
|
+
sites: [{ name: 'Die Bundeswahlleiterin', url: 'https://www.bundeswahlleiterin.de', sources: ['https://www.bundeswahlleiterin.de'] }],
|
|
360
|
+
},
|
|
361
|
+
// France: Service-Public.fr's elections pages (the government's site for voters). Source:
|
|
362
|
+
// https://www.service-public.gouv.fr/particuliers/vosdroits/N47.
|
|
363
|
+
FR: {
|
|
364
|
+
name: { en: 'France', es: 'Francia' },
|
|
365
|
+
sites: [{
|
|
366
|
+
name: 'Service-Public.fr', url: 'https://www.service-public.gouv.fr/particuliers/vosdroits/N47',
|
|
367
|
+
sources: ['https://www.service-public.gouv.fr/particuliers/vosdroits/N47'],
|
|
368
|
+
}],
|
|
369
|
+
},
|
|
370
|
+
});
|
|
371
|
+
/** Common names for a country that are not its ISO 3166-1 alpha-2 code. */
|
|
372
|
+
export const COUNTRY_ALIASES = deepFreeze({ UK: 'GB' });
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { FixedCard, FixedCardAnswer, FixedCardKind, FixedCardMatch, FixedCardOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The longest message read in full, counted after invisible characters are dropped. Past it, the first and the last
|
|
4
|
+
* FIXED_CARD_SCAN_LIMIT / 2 characters are read, each on its own, so a long paste that ends with a crisis statement
|
|
5
|
+
* still matches.
|
|
6
|
+
*/
|
|
7
|
+
export declare const FIXED_CARD_SCAN_LIMIT = 20000;
|
|
8
|
+
/**
|
|
9
|
+
* Until this moment an election card replaces the model's answer; after it, the card shows first and the turn may go
|
|
10
|
+
* on. Noon UTC on 2026-11-04: the day after the US midterms (2026-11-03), after the last US polls close (Hawaii and
|
|
11
|
+
* the Aleutians, 05:00 to 06:00 UTC on 11-04).
|
|
12
|
+
*/
|
|
13
|
+
export declare const ELECTION_CARD_ONLY_UNTIL = "2026-11-04T12:00:00Z";
|
|
14
|
+
/**
|
|
15
|
+
* Lower case, no accents, no apostrophes ("I'm" reads "im"), look-alike and "fancy font" letters as plain ones, digits
|
|
16
|
+
* standing for letters read as letters, and every other run of non-letters one space: what the phrase lists are
|
|
17
|
+
* written against. Past FIXED_CARD_SCAN_LIMIT characters, the first and last halves joined by a space.
|
|
18
|
+
*/
|
|
19
|
+
export declare function normaliseForMatching(text: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* The fixed card a member's message calls for, or null. Crisis wins over election when a message shows both. Pass
|
|
22
|
+
* the member's own words (their question to George), not the post or chat they are asking about.
|
|
23
|
+
*/
|
|
24
|
+
export declare function matchFixedCard(text: string, options?: FixedCardOptions): FixedCardMatch | null;
|
|
25
|
+
/**
|
|
26
|
+
* A fixed card without reading a message: for a caller that already knows it must show one (the crisis card for a
|
|
27
|
+
* known under-18 member, D10). The language comes from `locale`, else English.
|
|
28
|
+
*/
|
|
29
|
+
export declare function fixedCardFor(kind: FixedCardKind, options?: FixedCardOptions): FixedCard;
|
|
30
|
+
/**
|
|
31
|
+
* What the turn does after a card: a crisis card always shows first and the turn may go on to a supportive answer;
|
|
32
|
+
* an election card replaces the model's answer until ELECTION_CARD_ONLY_UNTIL. `now` is epoch milliseconds or an ISO
|
|
33
|
+
* time from the caller's clock; one that cannot be read gives 'card_only'.
|
|
34
|
+
*/
|
|
35
|
+
export declare function fixedCardAnswer(kind: FixedCardKind, now: number | string): FixedCardAnswer;
|