@lingara/api 0.0.0-reserved.0 → 0.1.0-alpha.6

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Spinning Cat Studios
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,119 @@
1
1
  # @lingara/api
2
2
 
3
- Placeholder. The first release is published under the `next` tag: `npm install @lingara/api@next`.
3
+ The official TypeScript library for the [Lingara API](https://getlingara.com),
4
+ for **Node ≥ 22, Deno 2 and Bun 1**. It has **no runtime dependencies**:
5
+ every runtime it targets already ships `fetch`, `ReadableStream`,
6
+ `TextDecoder` and `AbortSignal`, so installing it installs nothing else.
7
+
8
+ ## Not for the browser
9
+
10
+ This library is for your server. The Lingara API authenticates with a client
11
+ secret, and a secret shipped to a browser is published: anyone who opens the
12
+ page can read it and spend your allowance. The API sends no CORS headers for
13
+ third-party origins for the same reason. The
14
+ [authentication guide](https://getlingara.com/guides/authentication) explains
15
+ why credentials stay on a server.
16
+
17
+ So the `Lingara` constructor **refuses a `clientSecret` wherever a DOM
18
+ exists** (`globalThis.document` is defined), and there is no override. If
19
+ that fires in your own tests, run them in a `node` test environment rather
20
+ than `jsdom`. A client built without credentials, for the three public
21
+ operations, is not refused.
22
+
23
+ ## Install
24
+
25
+ ```sh
26
+ npm install @lingara/api
27
+ ```
28
+
29
+ Deno: `import { Lingara } from "npm:@lingara/api";`. Bun: `bun add @lingara/api`.
30
+
31
+ ## Quick start
32
+
33
+ ```ts
34
+ import { Lingara } from "@lingara/api";
35
+
36
+ const client = new Lingara({
37
+ clientId: process.env.LINGARA_CLIENT_ID!,
38
+ clientSecret: process.env.LINGARA_CLIENT_SECRET!,
39
+ // version: "2026-09-knowing-tenpounder", // optional pin
40
+ // onDeprecation: (notice) => metrics.warn(notice),
41
+ });
42
+
43
+ // A stream is an async iterable; the request starts on the first read.
44
+ const ac = new AbortController();
45
+ for await (const ev of client.generateVocabulary({ level: 2, source_lang: "en", target_lang: "zh", count: 8 }, { signal: ac.signal })) {
46
+ if (ev.event === "item") console.log(ev.data.word, ev.data.translation);
47
+ }
48
+
49
+ // A JSON call resolves with the body; `servedVersion` rides beside it.
50
+ const usage = await client.getUsage();
51
+ console.log(usage.servedVersion, usage.allowance);
52
+ ```
53
+
54
+ Nine methods, each named after its `operationId`: `generateVocabulary`,
55
+ `createLessonPlan`, `streamLessonPlan` and `sendTutorMessage` return an
56
+ `EventStream`; `getLessonPlan`, `getUsage`, `getOpenApiDocument`,
57
+ `listApiVersions` and `getApiVersion` return a promise. The last three need
58
+ no credentials. Every method takes `{ signal?: AbortSignal }` last.
59
+
60
+ - `break` out of a `for await`, or `stream.close()`, closes the connection.
61
+ - A stream's `error` event is thrown as an `ApiError` with `status: 200`;
62
+ `done` ends iteration and is not yielded; `result` and `pending` are
63
+ yielded, then iteration ends.
64
+ - `await stream.servedVersion` gives the `Lingara-Version` echo, and never
65
+ rejects.
66
+
67
+ ## Errors
68
+
69
+ Everything the library throws is a `LingaraError`: `ApiError` (a refusal from
70
+ `/v1`, or a stream's `error` event), `OAuthError` (the token endpoint),
71
+ `MaintenanceError` (a plain-text 503) or `TransportError` (no usable answer:
72
+ `kind` is `connect`, `tls`, `reset`, `timeout`, `stream_ended_early`,
73
+ `malformed_response` or `malformed_event`). Cancelling throws your signal's
74
+ own `reason`, never a `LingaraError`. `instanceof` works even when the ESM
75
+ and CommonJS builds are both loaded in one process.
76
+
77
+ The client secret and access tokens never appear in any rendering of the
78
+ client, its token source or an error; they show as `[REDACTED]`.
79
+
80
+ ## Options
81
+
82
+ | Option | Default |
83
+ |---|---|
84
+ | `clientId`, `clientSecret` | none: a credential-free client |
85
+ | `auth` | `"basic"` (`client_secret_basic`); `"post"` for `client_secret_post` |
86
+ | `scopes` | none: every scope the client is allowed |
87
+ | `tokenSource` | a `ClientCredentials` built from the options above; supply your own `TokenSource` instead of `clientSecret` |
88
+ | `baseUrl` / `tokenUrl` | `https://api.getlingara.com` / `…/oauth/token` |
89
+ | `version` | none: your client's pinned version |
90
+ | `onDeprecation` | one `console.warn` per deprecated version id |
91
+ | `maxAttempts` | `3`; `1` turns retries off |
92
+ | `retryAfterCapSeconds` | `60`: a longer `Retry-After` is thrown, with `retryAfter` set |
93
+ | `streamIdleTimeoutMs` | `120000`, counted only while a read is waiting |
94
+ | `tokenRequestTimeoutMs` | `30000` |
95
+ | `userAgentSuffix` | none: appended after the library's own token |
96
+ | `fetch` | `globalThis.fetch` |
97
+ | `clock`, `sleeper` | the real ones |
98
+
99
+ `clock` and `sleeper` are **testing seams**: they let a test control refresh
100
+ timing and `Retry-After` sleeps without waiting in real time. Leave them
101
+ alone in production.
102
+
103
+ **The `version` option and the generated-for version.** The library's types
104
+ were generated from one frozen API version, which the warning below names. Leave
105
+ `version` unset and your OAuth client's server-side pin decides what is
106
+ served; the library never sends a default. When a response is served from
107
+ another version, the library logs one `console.warn` per served id: the
108
+ response shapes may differ from the types. Pin the OAuth client (or set
109
+ `version`) to the generated-for version, or upgrade the library.
110
+
111
+ ## The contract
112
+
113
+ This library keeps the contract every official Lingara library keeps, and
114
+ runs its conformance suite on Node 22 and 24, Deno 2 and Bun 1:
115
+ [`conformance/CONTRACT.md`](../conformance/CONTRACT.md).
116
+
117
+ ## Licence
118
+
119
+ MIT.