@teemtape/api-client 0.1.1 → 0.1.3
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/dist/index.d.ts +29 -2
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -31,13 +31,31 @@ interface QuotesResponse {
|
|
|
31
31
|
interface Note {
|
|
32
32
|
id: string;
|
|
33
33
|
symbol: string;
|
|
34
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* The poster's anonymous handle, e.g. "user1234". Falls back to a
|
|
36
|
+
* token-derived label ("anon-6f1ed0") or "agent-cli" when no handle was set.
|
|
37
|
+
*/
|
|
35
38
|
author: string;
|
|
36
39
|
source: NoteSource;
|
|
37
40
|
body: string;
|
|
38
41
|
/** ISO timestamp. */
|
|
39
42
|
createdAt: string;
|
|
40
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* An anonymous handle: a short, human-friendly name (e.g. "user1234") that a
|
|
46
|
+
* person or agent picks once and reuses so collaborators on a shared watchlist
|
|
47
|
+
* can tell each other apart. Still anonymous — no account, email, or password.
|
|
48
|
+
*/
|
|
49
|
+
interface Handle {
|
|
50
|
+
handle: string;
|
|
51
|
+
/** ISO timestamp the handle was first claimed. */
|
|
52
|
+
createdAt: string;
|
|
53
|
+
}
|
|
54
|
+
/** Result of checking whether a specific handle is still available. */
|
|
55
|
+
interface HandleAvailability {
|
|
56
|
+
handle: string;
|
|
57
|
+
available: boolean;
|
|
58
|
+
}
|
|
41
59
|
interface NotesResponse {
|
|
42
60
|
symbol: string;
|
|
43
61
|
notes: Note[];
|
|
@@ -52,6 +70,8 @@ interface CreateNoteInput {
|
|
|
52
70
|
symbol: string;
|
|
53
71
|
body: string;
|
|
54
72
|
source: NoteSource;
|
|
73
|
+
/** The poster's anonymous handle, e.g. "user1234". Optional. */
|
|
74
|
+
handle?: string;
|
|
55
75
|
}
|
|
56
76
|
/** A row in the SEC symbols reference catalog. */
|
|
57
77
|
interface SymbolEntry {
|
|
@@ -107,6 +127,13 @@ declare class TeemtapeClient {
|
|
|
107
127
|
}): Promise<SymbolsListResponse>;
|
|
108
128
|
/** Create a new anonymous watchlist and return its MD5 token. */
|
|
109
129
|
createWatchlist(): Promise<Watchlist>;
|
|
130
|
+
/**
|
|
131
|
+
* Claim an anonymous handle. Pass a `handle` to request a specific one (throws
|
|
132
|
+
* ApiError 409 if taken); omit it to get a fresh, unique, auto-generated one.
|
|
133
|
+
*/
|
|
134
|
+
createHandle(handle?: string): Promise<Handle>;
|
|
135
|
+
/** Check whether a specific handle is still available (not yet claimed). */
|
|
136
|
+
checkHandle(handle: string): Promise<HandleAvailability>;
|
|
110
137
|
/** Fetch a watchlist (symbols + metadata) by token. */
|
|
111
138
|
getWatchlist(token?: string): Promise<Watchlist>;
|
|
112
139
|
/** Add a symbol to a watchlist. */
|
|
@@ -119,4 +146,4 @@ declare class TeemtapeClient {
|
|
|
119
146
|
private request;
|
|
120
147
|
}
|
|
121
148
|
|
|
122
|
-
export { type ApiClientOptions, ApiError, type CreateNoteInput, type Note, type NoteSource, type NotesResponse, type Quote, type QuotesResponse, type SymbolEntry, type SymbolsListResponse, TeemtapeClient, type Watchlist };
|
|
149
|
+
export { type ApiClientOptions, ApiError, type CreateNoteInput, type Handle, type HandleAvailability, type Note, type NoteSource, type NotesResponse, type Quote, type QuotesResponse, type SymbolEntry, type SymbolsListResponse, TeemtapeClient, type Watchlist };
|
package/dist/index.js
CHANGED
|
@@ -42,6 +42,20 @@ var TeemtapeClient = class {
|
|
|
42
42
|
async createWatchlist() {
|
|
43
43
|
return this.request(`/api/watchlists`, { method: "POST" });
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Claim an anonymous handle. Pass a `handle` to request a specific one (throws
|
|
47
|
+
* ApiError 409 if taken); omit it to get a fresh, unique, auto-generated one.
|
|
48
|
+
*/
|
|
49
|
+
async createHandle(handle) {
|
|
50
|
+
return this.request(`/api/handles`, {
|
|
51
|
+
method: "POST",
|
|
52
|
+
body: JSON.stringify(handle ? { handle } : {})
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/** Check whether a specific handle is still available (not yet claimed). */
|
|
56
|
+
async checkHandle(handle) {
|
|
57
|
+
return this.request(`/api/handles/${encodeURIComponent(handle)}`);
|
|
58
|
+
}
|
|
45
59
|
/** Fetch a watchlist (symbols + metadata) by token. */
|
|
46
60
|
async getWatchlist(token = this.requireToken()) {
|
|
47
61
|
return this.request(`/api/w/${token}`);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["import type {\n CreateNoteInput,\n Note,\n NotesResponse,\n QuotesResponse,\n SymbolsListResponse,\n Watchlist,\n} from \"./types.js\";\n\nexport interface ApiClientOptions {\n /** Base URL of the Worker API, e.g. https://api.teemtape.com */\n baseUrl: string;\n /** Optional default watchlist token used by watchlist/note calls. */\n token?: string;\n /** Injectable fetch (defaults to global fetch); handy for tests. */\n fetch?: typeof fetch;\n}\n\n/** Thrown for any non-2xx API response. */\nexport class ApiError extends Error {\n readonly status: number;\n readonly body: unknown;\n\n constructor(status: number, message: string, body: unknown) {\n super(message);\n this.name = \"ApiError\";\n this.status = status;\n this.body = body;\n }\n}\n\n/**\n * Minimal typed client for the teemtape Worker API. The same contract is reused\n * by the CLI now and the web/native apps later (see docs/cli-options.md).\n */\nexport class TeemtapeClient {\n private readonly baseUrl: string;\n private readonly token?: string;\n private readonly fetchImpl: typeof fetch;\n\n constructor(options: ApiClientOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/$/, \"\");\n this.token = options.token;\n // Wrap default fetch: assigning window.fetch to a variable breaks in browsers\n // (\"Illegal invocation\") unless called with the correct receiver.\n this.fetchImpl =\n options.fetch ?? ((input, init) => globalThis.fetch(input, init));\n if (typeof this.fetchImpl !== \"function\") {\n throw new Error(\"No fetch implementation available (need Node 18+ or pass options.fetch).\");\n }\n }\n\n /** Delayed quotes for the given symbols. */\n async getQuotes(symbols: string[]): Promise<QuotesResponse> {\n const query = encodeURIComponent(symbols.join(\",\"));\n return this.request<QuotesResponse>(`/api/quotes?symbols=${query}`);\n }\n\n /** Paginated SEC symbol catalog with optional search and sort. */\n async listSymbols(params: {\n offset?: number;\n limit?: number;\n sort?: \"ticker\" | \"title\";\n q?: string;\n symbol?: string;\n name?: string;\n } = {}): Promise<SymbolsListResponse> {\n const search = new URLSearchParams();\n if (params.offset !== undefined) search.set(\"offset\", String(params.offset));\n if (params.limit !== undefined) search.set(\"limit\", String(params.limit));\n if (params.sort) search.set(\"sort\", params.sort);\n if (params.q) search.set(\"q\", params.q);\n if (params.symbol) search.set(\"symbol\", params.symbol);\n if (params.name) search.set(\"name\", params.name);\n const qs = search.toString();\n return this.request<SymbolsListResponse>(`/api/symbols${qs ? `?${qs}` : \"\"}`);\n }\n\n /** Create a new anonymous watchlist and return its MD5 token. */\n async createWatchlist(): Promise<Watchlist> {\n return this.request<Watchlist>(`/api/watchlists`, { method: \"POST\" });\n }\n\n /** Fetch a watchlist (symbols + metadata) by token. */\n async getWatchlist(token = this.requireToken()): Promise<Watchlist> {\n return this.request<Watchlist>(`/api/w/${token}`);\n }\n\n /** Add a symbol to a watchlist. */\n async addSymbol(symbol: string, token = this.requireToken()): Promise<Watchlist> {\n return this.request<Watchlist>(`/api/w/${token}/symbols`, {\n method: \"POST\",\n body: JSON.stringify({ symbol: symbol.toUpperCase() }),\n });\n }\n\n /** Notes for a symbol on a watchlist. */\n async getNotes(symbol: string, token = this.requireToken()): Promise<NotesResponse> {\n const query = encodeURIComponent(symbol.toUpperCase());\n return this.request<NotesResponse>(`/api/w/${token}/notes?symbol=${query}`);\n }\n\n /** Post an anonymous note to a symbol. */\n async addNote(input: CreateNoteInput, token = this.requireToken()): Promise<Note> {\n return this.request<Note>(`/api/w/${token}/notes`, {\n method: \"POST\",\n body: JSON.stringify({ ...input, symbol: input.symbol.toUpperCase() }),\n });\n }\n\n private requireToken(): string {\n if (!this.token) {\n throw new Error(\"A watchlist token is required. Pass --token or set TEEMTAPE_TOKEN.\");\n }\n return this.token;\n }\n\n private async request<T>(path: string, init: RequestInit = {}): Promise<T> {\n const headers = new Headers(init.headers);\n if (init.body && !headers.has(\"content-type\")) {\n headers.set(\"content-type\", \"application/json\");\n }\n headers.set(\"accept\", \"application/json\");\n\n const res = await this.fetchImpl(`${this.baseUrl}${path}`, { ...init, headers });\n const text = await res.text();\n const data = text ? safeJsonParse(text) : undefined;\n\n if (!res.ok) {\n const message =\n (isRecord(data) && typeof data.error === \"string\" && data.error) ||\n `Request to ${path} failed with ${res.status}`;\n throw new ApiError(res.status, message, data);\n }\n return data as T;\n }\n}\n\nfunction safeJsonParse(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n"],"mappings":";
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["import type {\n CreateNoteInput,\n Handle,\n HandleAvailability,\n Note,\n NotesResponse,\n QuotesResponse,\n SymbolsListResponse,\n Watchlist,\n} from \"./types.js\";\n\nexport interface ApiClientOptions {\n /** Base URL of the Worker API, e.g. https://api.teemtape.com */\n baseUrl: string;\n /** Optional default watchlist token used by watchlist/note calls. */\n token?: string;\n /** Injectable fetch (defaults to global fetch); handy for tests. */\n fetch?: typeof fetch;\n}\n\n/** Thrown for any non-2xx API response. */\nexport class ApiError extends Error {\n readonly status: number;\n readonly body: unknown;\n\n constructor(status: number, message: string, body: unknown) {\n super(message);\n this.name = \"ApiError\";\n this.status = status;\n this.body = body;\n }\n}\n\n/**\n * Minimal typed client for the teemtape Worker API. The same contract is reused\n * by the CLI now and the web/native apps later (see docs/cli-options.md).\n */\nexport class TeemtapeClient {\n private readonly baseUrl: string;\n private readonly token?: string;\n private readonly fetchImpl: typeof fetch;\n\n constructor(options: ApiClientOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/$/, \"\");\n this.token = options.token;\n // Wrap default fetch: assigning window.fetch to a variable breaks in browsers\n // (\"Illegal invocation\") unless called with the correct receiver.\n this.fetchImpl =\n options.fetch ?? ((input, init) => globalThis.fetch(input, init));\n if (typeof this.fetchImpl !== \"function\") {\n throw new Error(\"No fetch implementation available (need Node 18+ or pass options.fetch).\");\n }\n }\n\n /** Delayed quotes for the given symbols. */\n async getQuotes(symbols: string[]): Promise<QuotesResponse> {\n const query = encodeURIComponent(symbols.join(\",\"));\n return this.request<QuotesResponse>(`/api/quotes?symbols=${query}`);\n }\n\n /** Paginated SEC symbol catalog with optional search and sort. */\n async listSymbols(params: {\n offset?: number;\n limit?: number;\n sort?: \"ticker\" | \"title\";\n q?: string;\n symbol?: string;\n name?: string;\n } = {}): Promise<SymbolsListResponse> {\n const search = new URLSearchParams();\n if (params.offset !== undefined) search.set(\"offset\", String(params.offset));\n if (params.limit !== undefined) search.set(\"limit\", String(params.limit));\n if (params.sort) search.set(\"sort\", params.sort);\n if (params.q) search.set(\"q\", params.q);\n if (params.symbol) search.set(\"symbol\", params.symbol);\n if (params.name) search.set(\"name\", params.name);\n const qs = search.toString();\n return this.request<SymbolsListResponse>(`/api/symbols${qs ? `?${qs}` : \"\"}`);\n }\n\n /** Create a new anonymous watchlist and return its MD5 token. */\n async createWatchlist(): Promise<Watchlist> {\n return this.request<Watchlist>(`/api/watchlists`, { method: \"POST\" });\n }\n\n /**\n * Claim an anonymous handle. Pass a `handle` to request a specific one (throws\n * ApiError 409 if taken); omit it to get a fresh, unique, auto-generated one.\n */\n async createHandle(handle?: string): Promise<Handle> {\n return this.request<Handle>(`/api/handles`, {\n method: \"POST\",\n body: JSON.stringify(handle ? { handle } : {}),\n });\n }\n\n /** Check whether a specific handle is still available (not yet claimed). */\n async checkHandle(handle: string): Promise<HandleAvailability> {\n return this.request<HandleAvailability>(`/api/handles/${encodeURIComponent(handle)}`);\n }\n\n /** Fetch a watchlist (symbols + metadata) by token. */\n async getWatchlist(token = this.requireToken()): Promise<Watchlist> {\n return this.request<Watchlist>(`/api/w/${token}`);\n }\n\n /** Add a symbol to a watchlist. */\n async addSymbol(symbol: string, token = this.requireToken()): Promise<Watchlist> {\n return this.request<Watchlist>(`/api/w/${token}/symbols`, {\n method: \"POST\",\n body: JSON.stringify({ symbol: symbol.toUpperCase() }),\n });\n }\n\n /** Notes for a symbol on a watchlist. */\n async getNotes(symbol: string, token = this.requireToken()): Promise<NotesResponse> {\n const query = encodeURIComponent(symbol.toUpperCase());\n return this.request<NotesResponse>(`/api/w/${token}/notes?symbol=${query}`);\n }\n\n /** Post an anonymous note to a symbol. */\n async addNote(input: CreateNoteInput, token = this.requireToken()): Promise<Note> {\n return this.request<Note>(`/api/w/${token}/notes`, {\n method: \"POST\",\n body: JSON.stringify({ ...input, symbol: input.symbol.toUpperCase() }),\n });\n }\n\n private requireToken(): string {\n if (!this.token) {\n throw new Error(\"A watchlist token is required. Pass --token or set TEEMTAPE_TOKEN.\");\n }\n return this.token;\n }\n\n private async request<T>(path: string, init: RequestInit = {}): Promise<T> {\n const headers = new Headers(init.headers);\n if (init.body && !headers.has(\"content-type\")) {\n headers.set(\"content-type\", \"application/json\");\n }\n headers.set(\"accept\", \"application/json\");\n\n const res = await this.fetchImpl(`${this.baseUrl}${path}`, { ...init, headers });\n const text = await res.text();\n const data = text ? safeJsonParse(text) : undefined;\n\n if (!res.ok) {\n const message =\n (isRecord(data) && typeof data.error === \"string\" && data.error) ||\n `Request to ${path} failed with ${res.status}`;\n throw new ApiError(res.status, message, data);\n }\n return data as T;\n }\n}\n\nfunction safeJsonParse(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED