@kedataindo/docflow-core 0.0.95 → 0.0.97
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/ai/index.cjs +49 -10
- package/dist/ai/index.d.cts +1 -1
- package/dist/ai/index.d.ts +1 -1
- package/dist/ai/index.js +5 -1
- package/dist/{chunk-6RIBBYIP.js → chunk-ADA2T4CE.js} +47 -10
- package/dist/{index-DRhAaVy1.d.cts → index-Br6IhSDL.d.cts} +62 -8
- package/dist/{index-DRhAaVy1.d.ts → index-Br6IhSDL.d.ts} +62 -8
- package/dist/index.cjs +49 -10
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +5 -1
- package/package.json +1 -1
package/dist/ai/index.cjs
CHANGED
|
@@ -22,12 +22,14 @@ var ai_exports = {};
|
|
|
22
22
|
__export(ai_exports, {
|
|
23
23
|
CONTEXT_CHAR_CAP: () => CONTEXT_CHAR_CAP,
|
|
24
24
|
LOCAL_STORAGE_KEY: () => LOCAL_STORAGE_KEY,
|
|
25
|
+
SESSION_STORAGE_KEY: () => SESSION_STORAGE_KEY,
|
|
25
26
|
buildAIPrompt: () => buildAIPrompt,
|
|
26
27
|
claudeProvider: () => claudeProvider,
|
|
27
28
|
httpKeyStorage: () => httpKeyStorage,
|
|
28
29
|
localStorageKeyStorage: () => localStorageKeyStorage,
|
|
29
30
|
memoryKeyStorage: () => memoryKeyStorage,
|
|
30
31
|
openaiCompatibleProvider: () => openaiCompatibleProvider,
|
|
32
|
+
sessionStorageKeyStorage: () => sessionStorageKeyStorage,
|
|
31
33
|
toAIStreamFn: () => toAIStreamFn,
|
|
32
34
|
trimContextAfter: () => trimContextAfter,
|
|
33
35
|
trimContextBefore: () => trimContextBefore
|
|
@@ -144,30 +146,65 @@ function memoryKeyStorage(initial = null) {
|
|
|
144
146
|
};
|
|
145
147
|
}
|
|
146
148
|
var LOCAL_STORAGE_KEY = "docflow:ai-config";
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
149
|
+
var SESSION_STORAGE_KEY = "docflow:ai-config";
|
|
150
|
+
function isStoredAIConfig(value) {
|
|
151
|
+
if (typeof value !== "object" || value === null) return false;
|
|
152
|
+
const candidate = value;
|
|
153
|
+
return candidate.__docflowKeyStorage === 1 && typeof candidate.config === "object" && candidate.config !== null;
|
|
154
|
+
}
|
|
155
|
+
function webStorageKeyStorage(resolve, key = LOCAL_STORAGE_KEY, options = {}) {
|
|
156
|
+
const { ttlMs } = options;
|
|
157
|
+
if (ttlMs !== void 0 && (!Number.isFinite(ttlMs) || ttlMs <= 0)) {
|
|
158
|
+
throw new Error(
|
|
159
|
+
`webStorageKeyStorage: ttlMs must be a positive number of milliseconds, got ${String(ttlMs)}`
|
|
160
|
+
);
|
|
161
|
+
}
|
|
153
162
|
return {
|
|
154
163
|
async get() {
|
|
155
|
-
const raw =
|
|
164
|
+
const raw = resolve().getItem(key);
|
|
156
165
|
if (!raw) return null;
|
|
166
|
+
let parsed;
|
|
157
167
|
try {
|
|
158
|
-
|
|
168
|
+
parsed = JSON.parse(raw);
|
|
159
169
|
} catch {
|
|
160
170
|
return null;
|
|
161
171
|
}
|
|
172
|
+
if (!isStoredAIConfig(parsed)) {
|
|
173
|
+
return parsed;
|
|
174
|
+
}
|
|
175
|
+
if (parsed.expiresAt !== void 0 && Date.now() >= parsed.expiresAt) {
|
|
176
|
+
resolve().removeItem(key);
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
return parsed.config;
|
|
162
180
|
},
|
|
163
181
|
async set(value) {
|
|
164
|
-
|
|
182
|
+
const stored = {
|
|
183
|
+
__docflowKeyStorage: 1,
|
|
184
|
+
config: value,
|
|
185
|
+
...ttlMs === void 0 ? {} : { expiresAt: Date.now() + ttlMs }
|
|
186
|
+
};
|
|
187
|
+
resolve().setItem(key, JSON.stringify(stored));
|
|
165
188
|
},
|
|
166
189
|
async clear() {
|
|
167
|
-
|
|
190
|
+
resolve().removeItem(key);
|
|
168
191
|
}
|
|
169
192
|
};
|
|
170
193
|
}
|
|
194
|
+
function localStorageKeyStorage(key = LOCAL_STORAGE_KEY, storage, options = {}) {
|
|
195
|
+
return webStorageKeyStorage(() => {
|
|
196
|
+
const s = storage ?? globalThis.localStorage;
|
|
197
|
+
if (!s) throw new Error("localStorageKeyStorage: no localStorage available (SSR?)");
|
|
198
|
+
return s;
|
|
199
|
+
}, key, options);
|
|
200
|
+
}
|
|
201
|
+
function sessionStorageKeyStorage(key = SESSION_STORAGE_KEY, storage, options = {}) {
|
|
202
|
+
return webStorageKeyStorage(() => {
|
|
203
|
+
const s = storage ?? globalThis.sessionStorage;
|
|
204
|
+
if (!s) throw new Error("sessionStorageKeyStorage: no sessionStorage available (SSR?)");
|
|
205
|
+
return s;
|
|
206
|
+
}, key, options);
|
|
207
|
+
}
|
|
171
208
|
function httpKeyStorage(urls) {
|
|
172
209
|
const doFetch = urls.fetchImpl ?? ((...args) => fetch(...args));
|
|
173
210
|
return {
|
|
@@ -444,12 +481,14 @@ function claudeProvider(config) {
|
|
|
444
481
|
0 && (module.exports = {
|
|
445
482
|
CONTEXT_CHAR_CAP,
|
|
446
483
|
LOCAL_STORAGE_KEY,
|
|
484
|
+
SESSION_STORAGE_KEY,
|
|
447
485
|
buildAIPrompt,
|
|
448
486
|
claudeProvider,
|
|
449
487
|
httpKeyStorage,
|
|
450
488
|
localStorageKeyStorage,
|
|
451
489
|
memoryKeyStorage,
|
|
452
490
|
openaiCompatibleProvider,
|
|
491
|
+
sessionStorageKeyStorage,
|
|
453
492
|
toAIStreamFn,
|
|
454
493
|
trimContextAfter,
|
|
455
494
|
trimContextBefore
|
package/dist/ai/index.d.cts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { b as AIAction, c as AIActionRequest, d as AICompleteRequest, e as AIConfig, f as AIDraftCitation, g as AIDraftEvent, a as AIDraftFn, h as AIProvider, A as AIStreamFn, j as Auth, k as CONTEXT_CHAR_CAP, l as ClaudeConfig, m as CslDate, n as CslItemData, o as CslName, H as HttpKeyStorageUrls, K as KeyStorage, L as LOCAL_STORAGE_KEY, O as OpenAICompatibleConfig, S as StreamEvent,
|
|
1
|
+
export { b as AIAction, c as AIActionRequest, d as AICompleteRequest, e as AIConfig, f as AIDraftCitation, g as AIDraftEvent, a as AIDraftFn, h as AIProvider, A as AIStreamFn, j as Auth, B as BrowserKeyStorageOptions, k as CONTEXT_CHAR_CAP, l as ClaudeConfig, m as CslDate, n as CslItemData, o as CslName, H as HttpKeyStorageUrls, K as KeyStorage, L as LOCAL_STORAGE_KEY, O as OpenAICompatibleConfig, S as SESSION_STORAGE_KEY, r as StreamEvent, s as buildAIPrompt, t as claudeProvider, u as httpKeyStorage, v as localStorageKeyStorage, w as memoryKeyStorage, x as openaiCompatibleProvider, y as sessionStorageKeyStorage, z as toAIStreamFn, D as trimContextAfter, E as trimContextBefore } from '../index-Br6IhSDL.cjs';
|
package/dist/ai/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { b as AIAction, c as AIActionRequest, d as AICompleteRequest, e as AIConfig, f as AIDraftCitation, g as AIDraftEvent, a as AIDraftFn, h as AIProvider, A as AIStreamFn, j as Auth, k as CONTEXT_CHAR_CAP, l as ClaudeConfig, m as CslDate, n as CslItemData, o as CslName, H as HttpKeyStorageUrls, K as KeyStorage, L as LOCAL_STORAGE_KEY, O as OpenAICompatibleConfig, S as StreamEvent,
|
|
1
|
+
export { b as AIAction, c as AIActionRequest, d as AICompleteRequest, e as AIConfig, f as AIDraftCitation, g as AIDraftEvent, a as AIDraftFn, h as AIProvider, A as AIStreamFn, j as Auth, B as BrowserKeyStorageOptions, k as CONTEXT_CHAR_CAP, l as ClaudeConfig, m as CslDate, n as CslItemData, o as CslName, H as HttpKeyStorageUrls, K as KeyStorage, L as LOCAL_STORAGE_KEY, O as OpenAICompatibleConfig, S as SESSION_STORAGE_KEY, r as StreamEvent, s as buildAIPrompt, t as claudeProvider, u as httpKeyStorage, v as localStorageKeyStorage, w as memoryKeyStorage, x as openaiCompatibleProvider, y as sessionStorageKeyStorage, z as toAIStreamFn, D as trimContextAfter, E as trimContextBefore } from '../index-Br6IhSDL.js';
|
package/dist/ai/index.js
CHANGED
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CONTEXT_CHAR_CAP,
|
|
3
3
|
LOCAL_STORAGE_KEY,
|
|
4
|
+
SESSION_STORAGE_KEY,
|
|
4
5
|
buildAIPrompt,
|
|
5
6
|
claudeProvider,
|
|
6
7
|
httpKeyStorage,
|
|
7
8
|
localStorageKeyStorage,
|
|
8
9
|
memoryKeyStorage,
|
|
9
10
|
openaiCompatibleProvider,
|
|
11
|
+
sessionStorageKeyStorage,
|
|
10
12
|
toAIStreamFn,
|
|
11
13
|
trimContextAfter,
|
|
12
14
|
trimContextBefore
|
|
13
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-ADA2T4CE.js";
|
|
14
16
|
export {
|
|
15
17
|
CONTEXT_CHAR_CAP,
|
|
16
18
|
LOCAL_STORAGE_KEY,
|
|
19
|
+
SESSION_STORAGE_KEY,
|
|
17
20
|
buildAIPrompt,
|
|
18
21
|
claudeProvider,
|
|
19
22
|
httpKeyStorage,
|
|
20
23
|
localStorageKeyStorage,
|
|
21
24
|
memoryKeyStorage,
|
|
22
25
|
openaiCompatibleProvider,
|
|
26
|
+
sessionStorageKeyStorage,
|
|
23
27
|
toAIStreamFn,
|
|
24
28
|
trimContextAfter,
|
|
25
29
|
trimContextBefore
|
|
@@ -108,30 +108,65 @@ function memoryKeyStorage(initial = null) {
|
|
|
108
108
|
};
|
|
109
109
|
}
|
|
110
110
|
var LOCAL_STORAGE_KEY = "docflow:ai-config";
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
111
|
+
var SESSION_STORAGE_KEY = "docflow:ai-config";
|
|
112
|
+
function isStoredAIConfig(value) {
|
|
113
|
+
if (typeof value !== "object" || value === null) return false;
|
|
114
|
+
const candidate = value;
|
|
115
|
+
return candidate.__docflowKeyStorage === 1 && typeof candidate.config === "object" && candidate.config !== null;
|
|
116
|
+
}
|
|
117
|
+
function webStorageKeyStorage(resolve, key = LOCAL_STORAGE_KEY, options = {}) {
|
|
118
|
+
const { ttlMs } = options;
|
|
119
|
+
if (ttlMs !== void 0 && (!Number.isFinite(ttlMs) || ttlMs <= 0)) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
`webStorageKeyStorage: ttlMs must be a positive number of milliseconds, got ${String(ttlMs)}`
|
|
122
|
+
);
|
|
123
|
+
}
|
|
117
124
|
return {
|
|
118
125
|
async get() {
|
|
119
|
-
const raw =
|
|
126
|
+
const raw = resolve().getItem(key);
|
|
120
127
|
if (!raw) return null;
|
|
128
|
+
let parsed;
|
|
121
129
|
try {
|
|
122
|
-
|
|
130
|
+
parsed = JSON.parse(raw);
|
|
123
131
|
} catch {
|
|
124
132
|
return null;
|
|
125
133
|
}
|
|
134
|
+
if (!isStoredAIConfig(parsed)) {
|
|
135
|
+
return parsed;
|
|
136
|
+
}
|
|
137
|
+
if (parsed.expiresAt !== void 0 && Date.now() >= parsed.expiresAt) {
|
|
138
|
+
resolve().removeItem(key);
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
return parsed.config;
|
|
126
142
|
},
|
|
127
143
|
async set(value) {
|
|
128
|
-
|
|
144
|
+
const stored = {
|
|
145
|
+
__docflowKeyStorage: 1,
|
|
146
|
+
config: value,
|
|
147
|
+
...ttlMs === void 0 ? {} : { expiresAt: Date.now() + ttlMs }
|
|
148
|
+
};
|
|
149
|
+
resolve().setItem(key, JSON.stringify(stored));
|
|
129
150
|
},
|
|
130
151
|
async clear() {
|
|
131
|
-
|
|
152
|
+
resolve().removeItem(key);
|
|
132
153
|
}
|
|
133
154
|
};
|
|
134
155
|
}
|
|
156
|
+
function localStorageKeyStorage(key = LOCAL_STORAGE_KEY, storage, options = {}) {
|
|
157
|
+
return webStorageKeyStorage(() => {
|
|
158
|
+
const s = storage ?? globalThis.localStorage;
|
|
159
|
+
if (!s) throw new Error("localStorageKeyStorage: no localStorage available (SSR?)");
|
|
160
|
+
return s;
|
|
161
|
+
}, key, options);
|
|
162
|
+
}
|
|
163
|
+
function sessionStorageKeyStorage(key = SESSION_STORAGE_KEY, storage, options = {}) {
|
|
164
|
+
return webStorageKeyStorage(() => {
|
|
165
|
+
const s = storage ?? globalThis.sessionStorage;
|
|
166
|
+
if (!s) throw new Error("sessionStorageKeyStorage: no sessionStorage available (SSR?)");
|
|
167
|
+
return s;
|
|
168
|
+
}, key, options);
|
|
169
|
+
}
|
|
135
170
|
function httpKeyStorage(urls) {
|
|
136
171
|
const doFetch = urls.fetchImpl ?? ((...args) => fetch(...args));
|
|
137
172
|
return {
|
|
@@ -413,7 +448,9 @@ export {
|
|
|
413
448
|
toAIStreamFn,
|
|
414
449
|
memoryKeyStorage,
|
|
415
450
|
LOCAL_STORAGE_KEY,
|
|
451
|
+
SESSION_STORAGE_KEY,
|
|
416
452
|
localStorageKeyStorage,
|
|
453
|
+
sessionStorageKeyStorage,
|
|
417
454
|
httpKeyStorage,
|
|
418
455
|
openaiCompatibleProvider,
|
|
419
456
|
claudeProvider
|
|
@@ -156,6 +156,19 @@ interface CslItemData {
|
|
|
156
156
|
abstract?: string;
|
|
157
157
|
[k: string]: unknown;
|
|
158
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* A CSL style the citation engine can render with (issue #180). The library
|
|
161
|
+
* bundles four; a host may inject more (an institutional or Harvard/IEEE style)
|
|
162
|
+
* through `CitationPort.styles` — without modifying the library.
|
|
163
|
+
*/
|
|
164
|
+
interface CslStyleInfo {
|
|
165
|
+
id: string;
|
|
166
|
+
name: string;
|
|
167
|
+
/** 'note' styles render citations as footnotes; 'in-text' render inline. */
|
|
168
|
+
category: 'note' | 'in-text';
|
|
169
|
+
/** The CSL XML itself (the `<style>` document, as a string). */
|
|
170
|
+
xml: string;
|
|
171
|
+
}
|
|
159
172
|
/**
|
|
160
173
|
* Host-supplied citation port. The library never fetches sources itself — the
|
|
161
174
|
* host owns the reference library and injects CSL-JSON through this port,
|
|
@@ -166,6 +179,13 @@ interface CitationPort {
|
|
|
166
179
|
sources: CslItemData[] | (() => CslItemData[]);
|
|
167
180
|
/** Active CSL style id (e.g. 'chicago-notes-bibliography'). */
|
|
168
181
|
style?: string;
|
|
182
|
+
/**
|
|
183
|
+
* Extra CSL styles for this document's engine (issue #180). A style whose
|
|
184
|
+
* `id` matches a bundled one overrides it; every other id is added alongside
|
|
185
|
+
* the four defaults. Omit it and the engine behaves exactly as before — the
|
|
186
|
+
* bundled styles keep the editor offline-capable.
|
|
187
|
+
*/
|
|
188
|
+
styles?: CslStyleInfo[];
|
|
169
189
|
/**
|
|
170
190
|
* Called when the user inserts a citation: the host opens its source
|
|
171
191
|
* picker and resolves with the chosen source id (null = cancelled).
|
|
@@ -236,11 +256,16 @@ declare function buildAIPrompt(req: AIActionRequest): {
|
|
|
236
256
|
* is the overridable `DEFAULT_BASE_URL` default in `claudeProvider.ts`
|
|
237
257
|
* (contract rule 4 exception; see §5 of docs/LIBRARY_CONTRACT.md).
|
|
238
258
|
*
|
|
239
|
-
* ⚠️ Secrets note: `localStorageKeyStorage`
|
|
240
|
-
* `
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
259
|
+
* ⚠️ Secrets note (issue #179): `localStorageKeyStorage` **and**
|
|
260
|
+
* `sessionStorageKeyStorage` keep an API key in the browser in plaintext — any
|
|
261
|
+
* script running on the same origin can read either one. `sessionStorage` only
|
|
262
|
+
* shortens the *lifetime* (closing the tab drops it); it is **not** an XSS
|
|
263
|
+
* mitigation. The only option that keeps the key out of the browser entirely is
|
|
264
|
+
* `httpKeyStorage` — with `AIConfig.visibility: 'admin-only'` plus the host's
|
|
265
|
+
* completion proxy, the key never reaches the client. That is the recommended
|
|
266
|
+
* production path. `ttlMs` (both browser storages) bounds how long a stored key
|
|
267
|
+
* stays readable, and hosts should call `clear()` on sign-out.
|
|
268
|
+
* See docs/AI_PROVIDERS.md §5.
|
|
244
269
|
*/
|
|
245
270
|
type Auth = {
|
|
246
271
|
type: 'bearer';
|
|
@@ -274,12 +299,41 @@ interface KeyStorage {
|
|
|
274
299
|
/** In-memory storage — for tests and SSR (no persistence). */
|
|
275
300
|
declare function memoryKeyStorage(initial?: AIConfig | null): KeyStorage;
|
|
276
301
|
declare const LOCAL_STORAGE_KEY = "docflow:ai-config";
|
|
302
|
+
/** Same logical key, kept in `sessionStorage` instead (issue #179). */
|
|
303
|
+
declare const SESSION_STORAGE_KEY = "docflow:ai-config";
|
|
304
|
+
interface BrowserKeyStorageOptions {
|
|
305
|
+
/**
|
|
306
|
+
* Drop the credentials once the entry is older than this many milliseconds:
|
|
307
|
+
* `get()` then returns null and removes the entry. Rotating a key is the
|
|
308
|
+
* host's job, but a TTL bounds how long a key the user forgot about stays
|
|
309
|
+
* readable.
|
|
310
|
+
*/
|
|
311
|
+
ttlMs?: number;
|
|
312
|
+
}
|
|
277
313
|
/**
|
|
278
314
|
* Browser `localStorage` persistence — for embedded apps that want the config
|
|
279
315
|
* kept client-side. Note: this stores AI *credentials*, not document data, so
|
|
280
316
|
* it is the host's deliberate choice, not a library persistence leak.
|
|
317
|
+
*
|
|
318
|
+
* The key survives browser restarts and is readable by any script on the
|
|
319
|
+
* origin — see the module secrets note, and prefer `sessionStorageKeyStorage`
|
|
320
|
+
* (shorter lifetime) or `httpKeyStorage` (key never reaches the client) when the
|
|
321
|
+
* embedding app is not fully trusted.
|
|
322
|
+
*/
|
|
323
|
+
declare function localStorageKeyStorage(key?: string, storage?: Storage, options?: BrowserKeyStorageOptions): KeyStorage;
|
|
324
|
+
/**
|
|
325
|
+
* Browser `sessionStorage` persistence (issue #179) — the same key/value pair
|
|
326
|
+
* as `localStorageKeyStorage`, scoped to the tab: closing it (or the browser)
|
|
327
|
+
* drops the credentials, and a reload keeps them.
|
|
328
|
+
*
|
|
329
|
+
* What this does and does not do: it **reduces the lifetime** of a key the user
|
|
330
|
+
* typed into an embedded app (no "I logged out but the key is still there"
|
|
331
|
+
* surprise after the tab closes). It is **not** a defence against XSS — a
|
|
332
|
+
* script running on the origin reads `sessionStorage` as easily as
|
|
333
|
+
* `localStorage`. Only `httpKeyStorage` (plus `visibility: 'admin-only'` and the
|
|
334
|
+
* host's completion proxy) keeps the key out of the browser.
|
|
281
335
|
*/
|
|
282
|
-
declare function
|
|
336
|
+
declare function sessionStorageKeyStorage(key?: string, storage?: Storage, options?: BrowserKeyStorageOptions): KeyStorage;
|
|
283
337
|
interface HttpKeyStorageUrls {
|
|
284
338
|
/** GET → 200 with the AIConfig JSON body, or 404 when unset. */
|
|
285
339
|
getUrl: string;
|
|
@@ -349,5 +403,5 @@ interface ClaudeConfig {
|
|
|
349
403
|
}
|
|
350
404
|
declare function claudeProvider(config: ClaudeConfig): AIProvider;
|
|
351
405
|
|
|
352
|
-
export {
|
|
353
|
-
export type { AIStreamFn as A, CitationPort as C, HttpKeyStorageUrls as H, ImageUploadHandler as I, KeyStorage as K, OpenAICompatibleConfig as O,
|
|
406
|
+
export { trimContextAfter as D, trimContextBefore as E, LOCAL_STORAGE_KEY as L, SESSION_STORAGE_KEY as S, CONTEXT_CHAR_CAP as k, buildAIPrompt as s, claudeProvider as t, httpKeyStorage as u, localStorageKeyStorage as v, memoryKeyStorage as w, openaiCompatibleProvider as x, sessionStorageKeyStorage as y, toAIStreamFn as z };
|
|
407
|
+
export type { AIStreamFn as A, BrowserKeyStorageOptions as B, CitationPort as C, HttpKeyStorageUrls as H, ImageUploadHandler as I, KeyStorage as K, OpenAICompatibleConfig as O, AIDraftFn as a, AIAction as b, AIActionRequest as c, AICompleteRequest as d, AIConfig as e, AIDraftCitation as f, AIDraftEvent as g, AIProvider as h, AIProviderFactory as i, Auth as j, ClaudeConfig as l, CslDate as m, CslItemData as n, CslName as o, CslStyleInfo as p, ImageUploadResult as q, StreamEvent as r };
|
|
@@ -156,6 +156,19 @@ interface CslItemData {
|
|
|
156
156
|
abstract?: string;
|
|
157
157
|
[k: string]: unknown;
|
|
158
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* A CSL style the citation engine can render with (issue #180). The library
|
|
161
|
+
* bundles four; a host may inject more (an institutional or Harvard/IEEE style)
|
|
162
|
+
* through `CitationPort.styles` — without modifying the library.
|
|
163
|
+
*/
|
|
164
|
+
interface CslStyleInfo {
|
|
165
|
+
id: string;
|
|
166
|
+
name: string;
|
|
167
|
+
/** 'note' styles render citations as footnotes; 'in-text' render inline. */
|
|
168
|
+
category: 'note' | 'in-text';
|
|
169
|
+
/** The CSL XML itself (the `<style>` document, as a string). */
|
|
170
|
+
xml: string;
|
|
171
|
+
}
|
|
159
172
|
/**
|
|
160
173
|
* Host-supplied citation port. The library never fetches sources itself — the
|
|
161
174
|
* host owns the reference library and injects CSL-JSON through this port,
|
|
@@ -166,6 +179,13 @@ interface CitationPort {
|
|
|
166
179
|
sources: CslItemData[] | (() => CslItemData[]);
|
|
167
180
|
/** Active CSL style id (e.g. 'chicago-notes-bibliography'). */
|
|
168
181
|
style?: string;
|
|
182
|
+
/**
|
|
183
|
+
* Extra CSL styles for this document's engine (issue #180). A style whose
|
|
184
|
+
* `id` matches a bundled one overrides it; every other id is added alongside
|
|
185
|
+
* the four defaults. Omit it and the engine behaves exactly as before — the
|
|
186
|
+
* bundled styles keep the editor offline-capable.
|
|
187
|
+
*/
|
|
188
|
+
styles?: CslStyleInfo[];
|
|
169
189
|
/**
|
|
170
190
|
* Called when the user inserts a citation: the host opens its source
|
|
171
191
|
* picker and resolves with the chosen source id (null = cancelled).
|
|
@@ -236,11 +256,16 @@ declare function buildAIPrompt(req: AIActionRequest): {
|
|
|
236
256
|
* is the overridable `DEFAULT_BASE_URL` default in `claudeProvider.ts`
|
|
237
257
|
* (contract rule 4 exception; see §5 of docs/LIBRARY_CONTRACT.md).
|
|
238
258
|
*
|
|
239
|
-
* ⚠️ Secrets note: `localStorageKeyStorage`
|
|
240
|
-
* `
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
259
|
+
* ⚠️ Secrets note (issue #179): `localStorageKeyStorage` **and**
|
|
260
|
+
* `sessionStorageKeyStorage` keep an API key in the browser in plaintext — any
|
|
261
|
+
* script running on the same origin can read either one. `sessionStorage` only
|
|
262
|
+
* shortens the *lifetime* (closing the tab drops it); it is **not** an XSS
|
|
263
|
+
* mitigation. The only option that keeps the key out of the browser entirely is
|
|
264
|
+
* `httpKeyStorage` — with `AIConfig.visibility: 'admin-only'` plus the host's
|
|
265
|
+
* completion proxy, the key never reaches the client. That is the recommended
|
|
266
|
+
* production path. `ttlMs` (both browser storages) bounds how long a stored key
|
|
267
|
+
* stays readable, and hosts should call `clear()` on sign-out.
|
|
268
|
+
* See docs/AI_PROVIDERS.md §5.
|
|
244
269
|
*/
|
|
245
270
|
type Auth = {
|
|
246
271
|
type: 'bearer';
|
|
@@ -274,12 +299,41 @@ interface KeyStorage {
|
|
|
274
299
|
/** In-memory storage — for tests and SSR (no persistence). */
|
|
275
300
|
declare function memoryKeyStorage(initial?: AIConfig | null): KeyStorage;
|
|
276
301
|
declare const LOCAL_STORAGE_KEY = "docflow:ai-config";
|
|
302
|
+
/** Same logical key, kept in `sessionStorage` instead (issue #179). */
|
|
303
|
+
declare const SESSION_STORAGE_KEY = "docflow:ai-config";
|
|
304
|
+
interface BrowserKeyStorageOptions {
|
|
305
|
+
/**
|
|
306
|
+
* Drop the credentials once the entry is older than this many milliseconds:
|
|
307
|
+
* `get()` then returns null and removes the entry. Rotating a key is the
|
|
308
|
+
* host's job, but a TTL bounds how long a key the user forgot about stays
|
|
309
|
+
* readable.
|
|
310
|
+
*/
|
|
311
|
+
ttlMs?: number;
|
|
312
|
+
}
|
|
277
313
|
/**
|
|
278
314
|
* Browser `localStorage` persistence — for embedded apps that want the config
|
|
279
315
|
* kept client-side. Note: this stores AI *credentials*, not document data, so
|
|
280
316
|
* it is the host's deliberate choice, not a library persistence leak.
|
|
317
|
+
*
|
|
318
|
+
* The key survives browser restarts and is readable by any script on the
|
|
319
|
+
* origin — see the module secrets note, and prefer `sessionStorageKeyStorage`
|
|
320
|
+
* (shorter lifetime) or `httpKeyStorage` (key never reaches the client) when the
|
|
321
|
+
* embedding app is not fully trusted.
|
|
322
|
+
*/
|
|
323
|
+
declare function localStorageKeyStorage(key?: string, storage?: Storage, options?: BrowserKeyStorageOptions): KeyStorage;
|
|
324
|
+
/**
|
|
325
|
+
* Browser `sessionStorage` persistence (issue #179) — the same key/value pair
|
|
326
|
+
* as `localStorageKeyStorage`, scoped to the tab: closing it (or the browser)
|
|
327
|
+
* drops the credentials, and a reload keeps them.
|
|
328
|
+
*
|
|
329
|
+
* What this does and does not do: it **reduces the lifetime** of a key the user
|
|
330
|
+
* typed into an embedded app (no "I logged out but the key is still there"
|
|
331
|
+
* surprise after the tab closes). It is **not** a defence against XSS — a
|
|
332
|
+
* script running on the origin reads `sessionStorage` as easily as
|
|
333
|
+
* `localStorage`. Only `httpKeyStorage` (plus `visibility: 'admin-only'` and the
|
|
334
|
+
* host's completion proxy) keeps the key out of the browser.
|
|
281
335
|
*/
|
|
282
|
-
declare function
|
|
336
|
+
declare function sessionStorageKeyStorage(key?: string, storage?: Storage, options?: BrowserKeyStorageOptions): KeyStorage;
|
|
283
337
|
interface HttpKeyStorageUrls {
|
|
284
338
|
/** GET → 200 with the AIConfig JSON body, or 404 when unset. */
|
|
285
339
|
getUrl: string;
|
|
@@ -349,5 +403,5 @@ interface ClaudeConfig {
|
|
|
349
403
|
}
|
|
350
404
|
declare function claudeProvider(config: ClaudeConfig): AIProvider;
|
|
351
405
|
|
|
352
|
-
export {
|
|
353
|
-
export type { AIStreamFn as A, CitationPort as C, HttpKeyStorageUrls as H, ImageUploadHandler as I, KeyStorage as K, OpenAICompatibleConfig as O,
|
|
406
|
+
export { trimContextAfter as D, trimContextBefore as E, LOCAL_STORAGE_KEY as L, SESSION_STORAGE_KEY as S, CONTEXT_CHAR_CAP as k, buildAIPrompt as s, claudeProvider as t, httpKeyStorage as u, localStorageKeyStorage as v, memoryKeyStorage as w, openaiCompatibleProvider as x, sessionStorageKeyStorage as y, toAIStreamFn as z };
|
|
407
|
+
export type { AIStreamFn as A, BrowserKeyStorageOptions as B, CitationPort as C, HttpKeyStorageUrls as H, ImageUploadHandler as I, KeyStorage as K, OpenAICompatibleConfig as O, AIDraftFn as a, AIAction as b, AIActionRequest as c, AICompleteRequest as d, AIConfig as e, AIDraftCitation as f, AIDraftEvent as g, AIProvider as h, AIProviderFactory as i, Auth as j, ClaudeConfig as l, CslDate as m, CslItemData as n, CslName as o, CslStyleInfo as p, ImageUploadResult as q, StreamEvent as r };
|
package/dist/index.cjs
CHANGED
|
@@ -45,6 +45,7 @@ __export(index_exports, {
|
|
|
45
45
|
PAGE_SIZES: () => PAGE_SIZES,
|
|
46
46
|
PaginationPlus: () => PaginationPlus,
|
|
47
47
|
PerformanceMonitor: () => PerformanceMonitor,
|
|
48
|
+
SESSION_STORAGE_KEY: () => SESSION_STORAGE_KEY,
|
|
48
49
|
SearchAndReplaceExtension: () => SearchAndReplaceExtension,
|
|
49
50
|
SubdocumentProvider: () => SubdocumentProvider,
|
|
50
51
|
buildAIPrompt: () => buildAIPrompt,
|
|
@@ -75,6 +76,7 @@ __export(index_exports, {
|
|
|
75
76
|
searchAndReplaceKey: () => searchAndReplaceKey,
|
|
76
77
|
searchNext: () => searchNext,
|
|
77
78
|
searchPrev: () => searchPrev,
|
|
79
|
+
sessionStorageKeyStorage: () => sessionStorageKeyStorage,
|
|
78
80
|
setSearchQuery: () => setSearchQuery,
|
|
79
81
|
toAIStreamFn: () => toAIStreamFn,
|
|
80
82
|
trimContextAfter: () => trimContextAfter,
|
|
@@ -2801,30 +2803,65 @@ function memoryKeyStorage(initial = null) {
|
|
|
2801
2803
|
};
|
|
2802
2804
|
}
|
|
2803
2805
|
var LOCAL_STORAGE_KEY = "docflow:ai-config";
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2806
|
+
var SESSION_STORAGE_KEY = "docflow:ai-config";
|
|
2807
|
+
function isStoredAIConfig(value) {
|
|
2808
|
+
if (typeof value !== "object" || value === null) return false;
|
|
2809
|
+
const candidate = value;
|
|
2810
|
+
return candidate.__docflowKeyStorage === 1 && typeof candidate.config === "object" && candidate.config !== null;
|
|
2811
|
+
}
|
|
2812
|
+
function webStorageKeyStorage(resolve, key2 = LOCAL_STORAGE_KEY, options = {}) {
|
|
2813
|
+
const { ttlMs } = options;
|
|
2814
|
+
if (ttlMs !== void 0 && (!Number.isFinite(ttlMs) || ttlMs <= 0)) {
|
|
2815
|
+
throw new Error(
|
|
2816
|
+
`webStorageKeyStorage: ttlMs must be a positive number of milliseconds, got ${String(ttlMs)}`
|
|
2817
|
+
);
|
|
2818
|
+
}
|
|
2810
2819
|
return {
|
|
2811
2820
|
async get() {
|
|
2812
|
-
const raw =
|
|
2821
|
+
const raw = resolve().getItem(key2);
|
|
2813
2822
|
if (!raw) return null;
|
|
2823
|
+
let parsed;
|
|
2814
2824
|
try {
|
|
2815
|
-
|
|
2825
|
+
parsed = JSON.parse(raw);
|
|
2816
2826
|
} catch {
|
|
2817
2827
|
return null;
|
|
2818
2828
|
}
|
|
2829
|
+
if (!isStoredAIConfig(parsed)) {
|
|
2830
|
+
return parsed;
|
|
2831
|
+
}
|
|
2832
|
+
if (parsed.expiresAt !== void 0 && Date.now() >= parsed.expiresAt) {
|
|
2833
|
+
resolve().removeItem(key2);
|
|
2834
|
+
return null;
|
|
2835
|
+
}
|
|
2836
|
+
return parsed.config;
|
|
2819
2837
|
},
|
|
2820
2838
|
async set(value) {
|
|
2821
|
-
|
|
2839
|
+
const stored = {
|
|
2840
|
+
__docflowKeyStorage: 1,
|
|
2841
|
+
config: value,
|
|
2842
|
+
...ttlMs === void 0 ? {} : { expiresAt: Date.now() + ttlMs }
|
|
2843
|
+
};
|
|
2844
|
+
resolve().setItem(key2, JSON.stringify(stored));
|
|
2822
2845
|
},
|
|
2823
2846
|
async clear() {
|
|
2824
|
-
|
|
2847
|
+
resolve().removeItem(key2);
|
|
2825
2848
|
}
|
|
2826
2849
|
};
|
|
2827
2850
|
}
|
|
2851
|
+
function localStorageKeyStorage(key2 = LOCAL_STORAGE_KEY, storage, options = {}) {
|
|
2852
|
+
return webStorageKeyStorage(() => {
|
|
2853
|
+
const s = storage ?? globalThis.localStorage;
|
|
2854
|
+
if (!s) throw new Error("localStorageKeyStorage: no localStorage available (SSR?)");
|
|
2855
|
+
return s;
|
|
2856
|
+
}, key2, options);
|
|
2857
|
+
}
|
|
2858
|
+
function sessionStorageKeyStorage(key2 = SESSION_STORAGE_KEY, storage, options = {}) {
|
|
2859
|
+
return webStorageKeyStorage(() => {
|
|
2860
|
+
const s = storage ?? globalThis.sessionStorage;
|
|
2861
|
+
if (!s) throw new Error("sessionStorageKeyStorage: no sessionStorage available (SSR?)");
|
|
2862
|
+
return s;
|
|
2863
|
+
}, key2, options);
|
|
2864
|
+
}
|
|
2828
2865
|
function httpKeyStorage(urls) {
|
|
2829
2866
|
const doFetch = urls.fetchImpl ?? ((...args) => fetch(...args));
|
|
2830
2867
|
return {
|
|
@@ -3114,6 +3151,7 @@ function claudeProvider(config) {
|
|
|
3114
3151
|
PAGE_SIZES,
|
|
3115
3152
|
PaginationPlus,
|
|
3116
3153
|
PerformanceMonitor,
|
|
3154
|
+
SESSION_STORAGE_KEY,
|
|
3117
3155
|
SearchAndReplaceExtension,
|
|
3118
3156
|
SubdocumentProvider,
|
|
3119
3157
|
buildAIPrompt,
|
|
@@ -3144,6 +3182,7 @@ function claudeProvider(config) {
|
|
|
3144
3182
|
searchAndReplaceKey,
|
|
3145
3183
|
searchNext,
|
|
3146
3184
|
searchPrev,
|
|
3185
|
+
sessionStorageKeyStorage,
|
|
3147
3186
|
setSearchQuery,
|
|
3148
3187
|
toAIStreamFn,
|
|
3149
3188
|
trimContextAfter,
|
package/dist/index.d.cts
CHANGED
|
@@ -4,8 +4,8 @@ import { IndexeddbPersistence } from 'y-indexeddb';
|
|
|
4
4
|
import { WebrtcProvider } from 'y-webrtc';
|
|
5
5
|
import { WebsocketProvider } from 'y-websocket';
|
|
6
6
|
import * as Y from 'yjs';
|
|
7
|
-
import { I as ImageUploadHandler, C as CitationPort, A as AIStreamFn, a as AIDraftFn } from './index-
|
|
8
|
-
export { b as AIAction, c as AIActionRequest, d as AICompleteRequest, e as AIConfig, f as AIDraftCitation, g as AIDraftEvent, h as AIProvider, i as AIProviderFactory, j as Auth, k as CONTEXT_CHAR_CAP, l as ClaudeConfig, m as CslDate, n as CslItemData, o as CslName, H as HttpKeyStorageUrls,
|
|
7
|
+
import { I as ImageUploadHandler, C as CitationPort, A as AIStreamFn, a as AIDraftFn } from './index-Br6IhSDL.cjs';
|
|
8
|
+
export { b as AIAction, c as AIActionRequest, d as AICompleteRequest, e as AIConfig, f as AIDraftCitation, g as AIDraftEvent, h as AIProvider, i as AIProviderFactory, j as Auth, B as BrowserKeyStorageOptions, k as CONTEXT_CHAR_CAP, l as ClaudeConfig, m as CslDate, n as CslItemData, o as CslName, p as CslStyleInfo, H as HttpKeyStorageUrls, q as ImageUploadResult, K as KeyStorage, L as LOCAL_STORAGE_KEY, O as OpenAICompatibleConfig, S as SESSION_STORAGE_KEY, r as StreamEvent, s as buildAIPrompt, t as claudeProvider, u as httpKeyStorage, v as localStorageKeyStorage, w as memoryKeyStorage, x as openaiCompatibleProvider, y as sessionStorageKeyStorage, z as toAIStreamFn, D as trimContextAfter, E as trimContextBefore } from './index-Br6IhSDL.cjs';
|
|
9
9
|
import { PaginationPlusOptions } from 'tiptap-pagination-plus';
|
|
10
10
|
export { PAGE_SIZES, PageSize, PaginationPlus, PaginationPlusOptions } from 'tiptap-pagination-plus';
|
|
11
11
|
import { Node } from '@tiptap/pm/model';
|
package/dist/index.d.ts
CHANGED
|
@@ -4,8 +4,8 @@ import { IndexeddbPersistence } from 'y-indexeddb';
|
|
|
4
4
|
import { WebrtcProvider } from 'y-webrtc';
|
|
5
5
|
import { WebsocketProvider } from 'y-websocket';
|
|
6
6
|
import * as Y from 'yjs';
|
|
7
|
-
import { I as ImageUploadHandler, C as CitationPort, A as AIStreamFn, a as AIDraftFn } from './index-
|
|
8
|
-
export { b as AIAction, c as AIActionRequest, d as AICompleteRequest, e as AIConfig, f as AIDraftCitation, g as AIDraftEvent, h as AIProvider, i as AIProviderFactory, j as Auth, k as CONTEXT_CHAR_CAP, l as ClaudeConfig, m as CslDate, n as CslItemData, o as CslName, H as HttpKeyStorageUrls,
|
|
7
|
+
import { I as ImageUploadHandler, C as CitationPort, A as AIStreamFn, a as AIDraftFn } from './index-Br6IhSDL.js';
|
|
8
|
+
export { b as AIAction, c as AIActionRequest, d as AICompleteRequest, e as AIConfig, f as AIDraftCitation, g as AIDraftEvent, h as AIProvider, i as AIProviderFactory, j as Auth, B as BrowserKeyStorageOptions, k as CONTEXT_CHAR_CAP, l as ClaudeConfig, m as CslDate, n as CslItemData, o as CslName, p as CslStyleInfo, H as HttpKeyStorageUrls, q as ImageUploadResult, K as KeyStorage, L as LOCAL_STORAGE_KEY, O as OpenAICompatibleConfig, S as SESSION_STORAGE_KEY, r as StreamEvent, s as buildAIPrompt, t as claudeProvider, u as httpKeyStorage, v as localStorageKeyStorage, w as memoryKeyStorage, x as openaiCompatibleProvider, y as sessionStorageKeyStorage, z as toAIStreamFn, D as trimContextAfter, E as trimContextBefore } from './index-Br6IhSDL.js';
|
|
9
9
|
import { PaginationPlusOptions } from 'tiptap-pagination-plus';
|
|
10
10
|
export { PAGE_SIZES, PageSize, PaginationPlus, PaginationPlusOptions } from 'tiptap-pagination-plus';
|
|
11
11
|
import { Node } from '@tiptap/pm/model';
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CONTEXT_CHAR_CAP,
|
|
3
3
|
LOCAL_STORAGE_KEY,
|
|
4
|
+
SESSION_STORAGE_KEY,
|
|
4
5
|
buildAIPrompt,
|
|
5
6
|
claudeProvider,
|
|
6
7
|
httpKeyStorage,
|
|
7
8
|
localStorageKeyStorage,
|
|
8
9
|
memoryKeyStorage,
|
|
9
10
|
openaiCompatibleProvider,
|
|
11
|
+
sessionStorageKeyStorage,
|
|
10
12
|
toAIStreamFn,
|
|
11
13
|
trimContextAfter,
|
|
12
14
|
trimContextBefore
|
|
13
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-ADA2T4CE.js";
|
|
14
16
|
|
|
15
17
|
// src/Editor.ts
|
|
16
18
|
import { Editor as TiptapEditor } from "@tiptap/core";
|
|
@@ -2636,6 +2638,7 @@ export {
|
|
|
2636
2638
|
PAGE_SIZES,
|
|
2637
2639
|
PaginationPlus,
|
|
2638
2640
|
PerformanceMonitor,
|
|
2641
|
+
SESSION_STORAGE_KEY,
|
|
2639
2642
|
SearchAndReplaceExtension,
|
|
2640
2643
|
SubdocumentProvider,
|
|
2641
2644
|
buildAIPrompt,
|
|
@@ -2666,6 +2669,7 @@ export {
|
|
|
2666
2669
|
searchAndReplaceKey,
|
|
2667
2670
|
searchNext,
|
|
2668
2671
|
searchPrev,
|
|
2672
|
+
sessionStorageKeyStorage,
|
|
2669
2673
|
setSearchQuery,
|
|
2670
2674
|
toAIStreamFn,
|
|
2671
2675
|
trimContextAfter,
|