@llmnative/react 1.3.0 → 1.5.0

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.
@@ -2,3 +2,5 @@
2
2
  var e = "x-llmnative-log-id";
3
3
  //#endregion
4
4
  export { e as t };
5
+
6
+ //# sourceMappingURL=logHeader-Busbc8OO.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logHeader-Busbc8OO.mjs","names":[],"sources":["../src/providers/proxy/logHeader.ts"],"sourcesContent":["/** Nome dell'header che trasporta l'id di conversazione dal client (provider AI, via\n * `AICompleteRequest.logId`) al proxy dev di Vite (`vite.ts`), che lo usa per accodare\n * request/response allo stesso file di log per l'intera conversazione. File separato e\n * privo di import Node-specific — i provider AI lato browser (anthropic.ts/gemini.ts/\n * openaiCompatible.ts) lo importano per impostare l'header, senza portarsi dietro le\n * dipendenze Node di vite.ts nel proprio bundle. */\nexport const LLM_LOG_ID_HEADER = 'x-llmnative-log-id';\n"],"mappings":";AAMA,IAAa,IAAoB"}
@@ -1 +1,2 @@
1
- var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},s=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},c=(n,r,a)=>(a=n==null?{}:e(i(n)),s(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n)),l=`x-llmnative-log-id`;Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return l}});
1
+ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},s=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},c=(n,r,a)=>(a=n==null?{}:e(i(n)),s(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n)),l=`x-llmnative-log-id`;Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return l}});
2
+ //# sourceMappingURL=logHeader-CbzOhx-u.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logHeader-CbzOhx-u.js","names":[],"sources":["../src/providers/proxy/logHeader.ts"],"sourcesContent":["/** Nome dell'header che trasporta l'id di conversazione dal client (provider AI, via\n * `AICompleteRequest.logId`) al proxy dev di Vite (`vite.ts`), che lo usa per accodare\n * request/response allo stesso file di log per l'intera conversazione. File separato e\n * privo di import Node-specific — i provider AI lato browser (anthropic.ts/gemini.ts/\n * openaiCompatible.ts) lo importano per impostare l'header, senza portarsi dietro le\n * dipendenze Node di vite.ts nel proprio bundle. */\nexport const LLM_LOG_ID_HEADER = 'x-llmnative-log-id';\n"],"mappings":"slBAMa,EAAoB"}
@@ -38,6 +38,7 @@ export { default as Tab } from './ui/Tab';
38
38
  export * from './ui/Tab';
39
39
  export { default as Table } from './ui/Table';
40
40
  export type { TableProps, TableHeaderProp, TableReorderHandler, TableReorderMeta, TableSelectionChangeHandler, TableSelectionState } from './ui/Table';
41
+ export type { RecordKeyResolver } from './ui/useStableRecordKey';
41
42
  export { default as Icon } from './ui/Icon';
42
43
  export { LayoutBuilder } from './ui/LayoutBuilder';
43
44
  export * from './ui/fields/Input';
@@ -4,6 +4,7 @@ import { UIProps } from '../';
4
4
  import { PaginationParams } from './Pagination';
5
5
  import { type OrderConfig } from '../../libs/order';
6
6
  import { RecordSelectionState } from './useRecordSelection';
7
+ import { type RecordKeyResolver } from './useStableRecordKey';
7
8
  export type TableHeaderProp = {
8
9
  key: string;
9
10
  label: string;
@@ -34,6 +35,12 @@ export interface TableProps extends UIProps {
34
35
  activeKey?: string | null;
35
36
  selectedKeys?: string[];
36
37
  groupBy?: string | string[];
38
+ /** Field name (or resolver function) for a stable per-record key — see
39
+ * useStableRecordKey.ts for why this matters specifically for a Table whose `records` are
40
+ * re-supplied with new object identities on every edit (e.g. wrapped in a `Form`, one clone
41
+ * per keystroke): without it, a record with no `_key` falls back to an identity-keyed cache
42
+ * that treats every clone as a brand-new row, remounting it. Absent = unchanged behavior. */
43
+ recordId?: RecordKeyResolver<RecordProps>;
37
44
  headerClassName?: string;
38
45
  bodyClassName?: string;
39
46
  footerClassName?: string;
@@ -43,5 +50,5 @@ export interface TableProps extends UIProps {
43
50
  /** Custom cell renderer. Receives the record, column key, and absolute row index. */
44
51
  renderCell?: (record: RecordProps, key: string, absoluteIndex: number) => React.ReactNode;
45
52
  }
46
- declare function Table({ columns, records, footer, onRowClick, onReorder, onSelectionChange, selection, sortable, pagination, activeKey, selectedKeys, groupBy, before, after, wrapperClassName, className, headerClassName, bodyClassName, footerClassName, heightClassName, scrollClassName, selectedClassName, renderCell }: TableProps): React.JSX.Element;
53
+ declare function Table({ columns, records, footer, onRowClick, onReorder, onSelectionChange, selection, sortable, pagination, activeKey, selectedKeys, groupBy, recordId, before, after, wrapperClassName, className, headerClassName, bodyClassName, footerClassName, heightClassName, scrollClassName, selectedClassName, renderCell }: TableProps): React.JSX.Element;
47
54
  export default Table;
@@ -1,5 +1,18 @@
1
1
  type RecordWithOptionalKey = {
2
2
  _key?: string;
3
3
  };
4
- export declare function useStableRecordKey<TRecord extends RecordWithOptionalKey>(prefix: string): (record: TRecord, _index?: number) => string;
4
+ /** A field name (read via bracket access) or a function computing the key directly. */
5
+ export type RecordKeyResolver<TRecord> = string | ((record: TRecord) => string);
6
+ export declare function useStableRecordKey<TRecord extends RecordWithOptionalKey>(prefix: string,
7
+ /** Explicit resolver, checked BEFORE the RECORD_KEY/WeakMap fallback below — trusted even
8
+ * across the new object identities a caller's own state management may produce on every
9
+ * edit (e.g. Form.tsx's `cloneContainer`, which clones the touched record on each keystroke
10
+ * but preserves this field's VALUE unchanged). Without an explicit resolver, a record with no
11
+ * `RECORD_KEY` gets a key from a WeakMap keyed by object identity — a fresh clone is a cache
12
+ * miss and gets a brand-new key, which remounts that row (and any interactive element inside
13
+ * it, e.g. a Form-bound `<Input>`, losing focus) on every single keystroke. Passing a
14
+ * resolver for a stable id field already present on the record (e.g. `"id"`) sidesteps this
15
+ * entirely, since the resolved value never changes across a clone. Absent = unchanged
16
+ * behavior (RECORD_KEY/WeakMap fallback only). */
17
+ recordId?: RecordKeyResolver<TRecord>): (record: TRecord, _index?: number) => string;
5
18
  export {};
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
2
  import { type RecordProps } from "../../../providers/data/DataProvider";
3
3
  import { type GridCoreProps } from "./types";
4
- declare function GridCore<TRecord extends RecordProps>({ records, recordId, sourcePath, columns, actions, form, editDeepLink, header, footer, view, views, sticky, wrapperClassName, loading, title, before, after, sortable, pagination, selection, onRowClick, reorderable, onReorder, groupBy, onSave, onDelete, onComplete, audit, onLoad, }: GridCoreProps<TRecord>): React.JSX.Element;
4
+ declare function GridCore<TRecord extends RecordProps>({ records, recordId, sourcePath, columns, actions, form, editDeepLink, header, footer, view, views, sticky, wrapperClassName, cardClassName, bodyClassName, loading, title, before, after, sortable, pagination, selection, onRowClick, reorderable, onReorder, groupBy, searchable, onSave, onDelete, onComplete, audit, onLoad, }: GridCoreProps<TRecord>): React.JSX.Element;
5
5
  export default GridCore;
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
2
  import { type RecordProps } from "../../../providers/data/DataProvider";
3
3
  import { type GridTableViewProps } from "./types";
4
- declare function GridTableView<TRecord extends RecordProps>({ records, recordId, columns, runAction, sortable, pagination, selection, selectedKeys, onSelectionChange, onRowClick, reorderable, onReorder, activeKey, groupBy, wrapperClassName, before, after, }: GridTableViewProps<TRecord>): React.JSX.Element;
4
+ declare function GridTableView<TRecord extends RecordProps>({ records, recordId, columns, runAction, sortable, pagination, selection, selectedKeys, onSelectionChange, onRowClick, reorderable, onReorder, activeKey, groupBy, wrapperClassName, className, heightClassName, scrollClassName, headerClassName, before, after, }: GridTableViewProps<TRecord>): React.JSX.Element;
5
5
  export default GridTableView;
@@ -29,6 +29,26 @@ export type GridGalleryField<TRecord> = {
29
29
  export type GridTableViewConfig = {
30
30
  /** Show a built-in "Columns" dropdown letting the user show/hide table columns. Default `false`. */
31
31
  columnPicker?: boolean;
32
+ /** CSS classes on the underlying `<table>` element — forwarded to `Table`'s own `className`.
33
+ * A `<table>` with `table-layout: auto` (the default) shrinks its columns to fit the
34
+ * container instead of overflowing it, so wide/many-column content never triggers a
35
+ * horizontal scrollbar on its own — set a `min-w-[…]` here (combined with `views.table`'s
36
+ * ancestor allowing horizontal overflow, e.g. via `wrapperClassName="overflow-x-auto"`) to
37
+ * force the table wider than its container when needed. */
38
+ className?: string;
39
+ /** Tailwind height/max-height class for the inner viewport — forwarded to `Table`'s own
40
+ * `heightClassName`. When set, `Table` enables internal vertical scrolling automatically AND
41
+ * pins its own `<thead>`/`<tfoot>` to the top/bottom of that same scrolling viewport, so only
42
+ * the body rows move — no extra prop needed for that, it's `Table`'s own default behavior
43
+ * whenever it is height-bound. */
44
+ heightClassName?: string;
45
+ /** Additional class on the inner scrolling viewport — forwarded to `Table`'s own
46
+ * `scrollClassName`. Use as an addon for overflow styling or fine-grained tweaks. */
47
+ scrollClassName?: string;
48
+ /** Optional extra classes on `<thead>` — forwarded to `Table`'s own `headerClassName`. Purely
49
+ * cosmetic (e.g. extra padding/typography); the sticky-header-when-scrollable behavior itself
50
+ * is automatic (see `heightClassName` above) and does not depend on this prop being set. */
51
+ headerClassName?: string;
32
52
  };
33
53
  /** Gallery-specific view options, grouped under `views.gallery`. */
34
54
  export type GridGalleryViewConfig<TRecord> = {
@@ -220,6 +240,22 @@ export type GridPresentation<TRecord> = {
220
240
  sticky?: GridSticky;
221
241
  /** CSS classes on the outermost wrapper element. */
222
242
  wrapperClassName?: string;
243
+ /** CSS classes on the Card's own root box — the bordered element itself (`ui/Card.tsx`'s
244
+ * `cardRootClassName`), distinct from `wrapperClassName` (the element AROUND the card,
245
+ * e.g. for `before`/`after` layout) and `bodyClassName` (the padded area inside the card).
246
+ * The root box has no `display: flex`/height by default — its body only actually gets a
247
+ * bounded height (and therefore an internal scrollbar) when THIS box is also a flex column
248
+ * with a real height, e.g. `"flex h-full min-h-0 flex-col"`. Needed together with
249
+ * `bodyClassName`/`views.table.heightClassName` for a full-height Grid to scroll internally
250
+ * instead of growing past its container. */
251
+ cardClassName?: string;
252
+ /** CSS classes on the Card body — the container directly around the table/gallery view.
253
+ * Grid always renders inside a `Card` (`ui/Card.tsx`); its body defaults to a fixed padding
254
+ * with no height constraint. Use this (typically with `flex-1 min-h-0 overflow-hidden p-0`,
255
+ * paired with `wrapperClassName`/`cardClassName` making the card itself a bounded flex
256
+ * column, and `views.table.heightClassName`) to make the table's own internal scroll fill
257
+ * the remaining height of a flex ancestor instead of growing the page. */
258
+ bodyClassName?: string;
223
259
  /** Show a loading skeleton instead of rows. */
224
260
  loading?: boolean;
225
261
  /** Title rendered in the Grid header area. */
@@ -229,6 +265,15 @@ export type GridPresentation<TRecord> = {
229
265
  /** Content rendered after the Grid. */
230
266
  after?: React.ReactNode;
231
267
  };
268
+ /** Config for `GridBehavior.searchable` beyond a plain `true` — restrict which fields match,
269
+ * or override the input's placeholder. */
270
+ export type GridSearchConfig<TRecord> = {
271
+ /** Placeholder text for the search input — defaults to the localized `common.search`
272
+ * ("Search"). */
273
+ placeholder?: string;
274
+ /** Field keys to match against — omit to search every string-valued field on each record. */
275
+ fields?: Array<keyof TRecord | string>;
276
+ };
232
277
  /** Interaction / behaviour props for `<Grid>`. */
233
278
  export type GridBehavior<TRecord> = {
234
279
  /** Enable global sorting. Pass an `OrderConfig` to set a default sort. */
@@ -245,6 +290,14 @@ export type GridBehavior<TRecord> = {
245
290
  onReorder?: GridReorderHandler<TRecord>;
246
291
  /** Group rows by a field or array of fields. */
247
292
  groupBy?: keyof TRecord | string | Array<keyof TRecord | string>;
293
+ /** Enable a built-in search box in Grid's own default header — filters the records actually
294
+ * rendered (table/gallery, and whatever they in turn compute: sort/pagination/selection all
295
+ * see the FILTERED set, not the original one) by a case-insensitive substring match. `true`
296
+ * searches every string-valued field on each record; pass a `GridSearchConfig` to restrict to
297
+ * specific fields or customize the placeholder. Only rendered as part of Grid's OWN default
298
+ * header — same as `views.table.columnPicker`/`views.toggle`, a fully custom `header` prop
299
+ * bypasses it entirely (render your own search input there instead). */
300
+ searchable?: boolean | GridSearchConfig<TRecord>;
248
301
  };
249
302
  /** Data-mutation hooks for `<Grid>`. */
250
303
  export type GridPersistence<TRecord> = {
@@ -329,6 +382,10 @@ export type GridTableViewProps<TRecord extends RecordProps> = {
329
382
  activeKey?: string | null;
330
383
  groupBy?: keyof TRecord | string | Array<keyof TRecord | string>;
331
384
  wrapperClassName?: string;
385
+ className?: string;
386
+ heightClassName?: string;
387
+ scrollClassName?: string;
388
+ headerClassName?: string;
332
389
  before?: React.ReactNode;
333
390
  after?: React.ReactNode;
334
391
  };
@@ -21,7 +21,7 @@ export { PromptUtils } from './libs/promptUtils';
21
21
  export type { PromptAction, PromptStatusItem, PromptRunStats } from './components/widgets/Prompt';
22
22
  export type { TableHeaderProp, TableReorderHandler, TableReorderMeta, TableSelectionChangeHandler, TableSelectionState } from './components/ui/Table';
23
23
  export type { GalleryRecord, GallerySelectionChangeHandler, GallerySelectionState } from './components/ui/Gallery';
24
- export type { GridAction, GridActions, GridAfterActionArgs, GridAfterActionHandler, GridArrayProps, GridColumn, GridCoreProps, GridDBPath, GridDBQuery, GridDBProps, GridFooterContext, GridFormContext, GridGalleryField, GridGalleryViewProps, GridGalleryViewConfig, GridHeaderContext, GridLayout, GridMutationDeleteArgs, GridMutationDeleteHandler, GridMutationSaveArgs, GridMutationSaveHandler, GridReorderHandler, GridReorderMeta, GridRecordKey, GridProps, GridSelectionChangeHandler, GridSelectionMode, GridSelectionState, GridSticky, GridTableViewProps, GridTableViewConfig, GridViewsConfig, GridCellContext, } from './components/widgets/Grid';
24
+ export type { GridAction, GridActions, GridAfterActionArgs, GridAfterActionHandler, GridArrayProps, GridColumn, GridCoreProps, GridDBPath, GridDBQuery, GridDBProps, GridFooterContext, GridFormContext, GridGalleryField, GridGalleryViewProps, GridGalleryViewConfig, GridHeaderContext, GridLayout, GridMutationDeleteArgs, GridMutationDeleteHandler, GridMutationSaveArgs, GridMutationSaveHandler, GridReorderHandler, GridReorderMeta, GridRecordKey, GridProps, GridSearchConfig, GridSelectionChangeHandler, GridSelectionMode, GridSelectionState, GridSticky, GridTableViewProps, GridTableViewConfig, GridViewsConfig, GridCellContext, } from './components/widgets/Grid';
25
25
  export type { ProviderConfigurable, ProviderConfigurationState } from './providers/ProviderConfiguration';
26
26
  export { getProviderConfigurationState } from './providers/ProviderConfiguration';
27
27
  export type { ProviderAdapterMap, ProviderService, SetProviderFn, ProviderRegistrySnapshot } from './providers/ProviderRegistryContext';
@@ -26,5 +26,5 @@ export interface AuthProviderAdapter extends ProviderConfigurable {
26
26
  * Read-only/UI convenience — never a security boundary, the server always
27
27
  * re-verifies the raw token itself rather than trusting client-read claims.
28
28
  */
29
- getIdTokenClaims?(): Promise<Record<string, unknown> | null>;
29
+ getIdTokenClaims?(forceRefresh?: boolean): Promise<Record<string, unknown> | null>;
30
30
  }
@@ -45,5 +45,5 @@ export declare class FirebaseAuthProvider implements AuthProviderAdapter {
45
45
  signOut(): Promise<void>;
46
46
  onAuthChange(callback: (user: UserProfile | null) => void): () => void;
47
47
  getAccessToken(): Promise<string>;
48
- getIdTokenClaims(): Promise<Record<string, unknown> | null>;
48
+ getIdTokenClaims(forceRefresh?: boolean): Promise<Record<string, unknown> | null>;
49
49
  }
package/dist/vite.js CHANGED
@@ -1,2 +1,3 @@
1
1
  Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./logHeader-CbzOhx-u.js");let t=require("node:fs/promises"),n=require("node:path");n=e.r(n);var r=`x-llmnative-proxy`,i=`/api/proxy`,a=async e=>{let t=[];for await(let n of e)t.push(Buffer.isBuffer(n)?n:Buffer.from(n));return t.length===0?void 0:Buffer.concat(t)};function o(e){return e.replace(/[^a-zA-Z0-9_-]/g,``).slice(0,128)||`unknown`}function s(e){if(!e.trim())return`(empty body)`;try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}async function c(e,r,i,a,c,l){try{await(0,t.mkdir)(e.dir,{recursive:!0});let u=[``,`=`.repeat(80),`Turn @ ${new Date().toISOString()}`,`Target: ${i}`,`--- REQUEST BODY ---`,s(a),`--- RESPONSE BODY (status ${c}) ---`,s(l),`=`.repeat(80),``].join(`
2
- `);await(0,t.appendFile)(n.default.join(e.dir,`${o(r)}.txt`),u,`utf-8`)}catch(e){console.warn(`[llmnative-dev-proxy] Failed to write LLM log:`,e)}}var l=async(t,n,i,o)=>{let s=new URL(t.url||`/`,`http://localhost`).searchParams.get(`url`);if(t.headers[r]!==`1`){n.statusCode=403,n.setHeader(`Content-Type`,`application/json`),n.end(JSON.stringify({error:`Proxy access denied.`}));return}if(!s){n.statusCode=400,n.setHeader(`Content-Type`,`application/json`),n.end(JSON.stringify({error:`Missing "url" query parameter.`}));return}try{let l=new URL(s);if(l.pathname.startsWith(i)&&[`localhost`,`127.0.0.1`].includes(l.hostname)){n.statusCode=508,n.setHeader(`Content-Type`,`application/json`),n.end(JSON.stringify({error:`Recursive proxy target detected.`,target:s}));return}let u=await a(t),d=t.headers[e.t],f=new Headers,p=new Set([r,e.t,`host`,`origin`,`referer`,`content-length`,`transfer-encoding`,`connection`]);Object.entries(t.headers).forEach(([e,t])=>{!t||p.has(e.toLowerCase())||f.set(e,Array.isArray(t)?t.join(`, `):t)});let m=await fetch(s,{method:t.method||`GET`,headers:f,body:u&&![`GET`,`HEAD`].includes((t.method||`GET`).toUpperCase())?Uint8Array.from(u):void 0,redirect:`follow`}),h=Buffer.from(await m.arrayBuffer());o?.enabled&&typeof d==`string`&&d&&c(o,d,s,u?.toString(`utf-8`)??``,m.status,h.toString(`utf-8`)),n.statusCode=m.status,m.headers.forEach((e,t)=>{t.toLowerCase()!==`content-encoding`&&n.setHeader(t,e)}),n.end(h)}catch(e){n.statusCode=502,n.setHeader(`Content-Type`,`application/json`),n.end(JSON.stringify({error:e instanceof Error?e.message:`Proxy request failed.`}))}},u=(e=i,t)=>({name:`llmnative-dev-proxy`,configureServer(n){n.middlewares.use(e,(n,r)=>{l(n,r,e,t)})},configurePreviewServer(n){n.middlewares.use(e,(n,r)=>{l(n,r,e,t)})}});exports.PROXY_PATH=i,exports.createProxyPlugin=u;
2
+ `);await(0,t.appendFile)(n.default.join(e.dir,`${o(r)}.txt`),u,`utf-8`)}catch(e){console.warn(`[llmnative-dev-proxy] Failed to write LLM log:`,e)}}var l=async(t,n,i,o)=>{let s=new URL(t.url||`/`,`http://localhost`).searchParams.get(`url`);if(t.headers[r]!==`1`){n.statusCode=403,n.setHeader(`Content-Type`,`application/json`),n.end(JSON.stringify({error:`Proxy access denied.`}));return}if(!s){n.statusCode=400,n.setHeader(`Content-Type`,`application/json`),n.end(JSON.stringify({error:`Missing "url" query parameter.`}));return}try{let l=new URL(s);if(l.pathname.startsWith(i)&&[`localhost`,`127.0.0.1`].includes(l.hostname)){n.statusCode=508,n.setHeader(`Content-Type`,`application/json`),n.end(JSON.stringify({error:`Recursive proxy target detected.`,target:s}));return}let u=await a(t),d=t.headers[e.t],f=new Headers,p=new Set([r,e.t,`host`,`origin`,`referer`,`content-length`,`transfer-encoding`,`connection`]);Object.entries(t.headers).forEach(([e,t])=>{!t||p.has(e.toLowerCase())||f.set(e,Array.isArray(t)?t.join(`, `):t)});let m=await fetch(s,{method:t.method||`GET`,headers:f,body:u&&![`GET`,`HEAD`].includes((t.method||`GET`).toUpperCase())?Uint8Array.from(u):void 0,redirect:`follow`}),h=Buffer.from(await m.arrayBuffer());o?.enabled&&typeof d==`string`&&d&&c(o,d,s,u?.toString(`utf-8`)??``,m.status,h.toString(`utf-8`)),n.statusCode=m.status,m.headers.forEach((e,t)=>{t.toLowerCase()!==`content-encoding`&&n.setHeader(t,e)}),n.end(h)}catch(e){n.statusCode=502,n.setHeader(`Content-Type`,`application/json`),n.end(JSON.stringify({error:e instanceof Error?e.message:`Proxy request failed.`}))}},u=(e=i,t)=>({name:`llmnative-dev-proxy`,configureServer(n){n.middlewares.use(e,(n,r)=>{l(n,r,e,t)})},configurePreviewServer(n){n.middlewares.use(e,(n,r)=>{l(n,r,e,t)})}});exports.PROXY_PATH=i,exports.createProxyPlugin=u;
3
+ //# sourceMappingURL=vite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite.js","names":[],"sources":["../src/providers/proxy/vite.ts"],"sourcesContent":["import { appendFile, mkdir } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport type { Plugin } from 'vite';\nimport { LLM_LOG_ID_HEADER } from './logHeader';\n\nconst PROXY_CALLER_HEADER = 'x-llmnative-proxy';\nexport const PROXY_PATH = '/api/proxy';\n\n/** Logging file di richiesta/risposta LLM — SOLO dev, mai attivo di default. Un file per\n * conversazione: il client (useAgent, CMS) genera un `logId` stabile per l'intera sessione e\n * lo manda su ogni chiamata come header `x-llmnative-log-id`; il proxy accoda ogni turno allo\n * stesso file, non lo sovrascrive. Vedi AICompleteRequest.logId per il lato client. */\nexport interface ProxyLogOptions {\n enabled: boolean;\n /** Cartella di destinazione, relativa alla cwd del dev server (il progetto consumer, es.\n * il CMS) o assoluta. Creata se assente. */\n dir: string;\n}\n\nconst readRequestBody = async (req: IncomingMessage): Promise<Buffer | undefined> => {\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n return chunks.length === 0 ? undefined : Buffer.concat(chunks);\n};\n\n/** Solo caratteri sicuri per un nome file — l'id è generato da noi (crypto.randomUUID() lato\n * client), ma niente path traversal per costruzione anche se un client si comportasse male. */\nfunction sanitizeLogId(logId: string): string {\n return logId.replace(/[^a-zA-Z0-9_-]/g, '').slice(0, 128) || 'unknown';\n}\n\nfunction prettyJsonOrRaw(text: string): string {\n if (!text.trim()) return '(empty body)';\n try {\n return JSON.stringify(JSON.parse(text), null, 2);\n } catch {\n return text;\n }\n}\n\nasync function appendLogBlock(logOptions: ProxyLogOptions, logId: string, target: string, requestBody: string, status: number, responseBody: string) {\n try {\n await mkdir(logOptions.dir, { recursive: true });\n const block = [\n '',\n '='.repeat(80),\n `Turn @ ${new Date().toISOString()}`,\n `Target: ${target}`,\n '--- REQUEST BODY ---',\n prettyJsonOrRaw(requestBody),\n `--- RESPONSE BODY (status ${status}) ---`,\n prettyJsonOrRaw(responseBody),\n '='.repeat(80),\n '',\n ].join('\\n');\n await appendFile(path.join(logOptions.dir, `${sanitizeLogId(logId)}.txt`), block, 'utf-8');\n } catch (error) {\n // Un log fallito non deve mai far fallire la vera richiesta — solo un avviso in console.\n console.warn('[llmnative-dev-proxy] Failed to write LLM log:', error);\n }\n}\n\nconst writeProxyResponse = async (req: IncomingMessage, res: ServerResponse, route: string, logOptions?: ProxyLogOptions) => {\n const requestUrl = new URL(req.url || '/', 'http://localhost');\n const target = requestUrl.searchParams.get('url');\n\n if (req.headers[PROXY_CALLER_HEADER] !== '1') {\n res.statusCode = 403;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: 'Proxy access denied.' }));\n return;\n }\n\n if (!target) {\n res.statusCode = 400;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: 'Missing \"url\" query parameter.' }));\n return;\n }\n\n try {\n const targetUrl = new URL(target);\n if (targetUrl.pathname.startsWith(route) && ['localhost', '127.0.0.1'].includes(targetUrl.hostname)) {\n res.statusCode = 508;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: 'Recursive proxy target detected.', target }));\n return;\n }\n\n const body = await readRequestBody(req);\n const logId = req.headers[LLM_LOG_ID_HEADER];\n const headers = new Headers();\n // L'header di log è consumato QUI, mai inoltrato al provider upstream (stesso\n // trattamento del PROXY_CALLER_HEADER) — è un dettaglio nostro, non dell'API esterna.\n const skipHeaders = new Set([PROXY_CALLER_HEADER, LLM_LOG_ID_HEADER, 'host', 'origin', 'referer', 'content-length', 'transfer-encoding', 'connection']);\n\n Object.entries(req.headers).forEach(([key, value]) => {\n if (!value || skipHeaders.has(key.toLowerCase())) return;\n headers.set(key, Array.isArray(value) ? value.join(', ') : value);\n });\n\n const upstream = await fetch(target, {\n method: req.method || 'GET',\n headers,\n body: body && !['GET', 'HEAD'].includes((req.method || 'GET').toUpperCase())\n ? Uint8Array.from(body)\n : undefined,\n redirect: 'follow',\n });\n\n const responseBuffer = Buffer.from(await upstream.arrayBuffer());\n\n if (logOptions?.enabled && typeof logId === 'string' && logId) {\n void appendLogBlock(logOptions, logId, target, body?.toString('utf-8') ?? '', upstream.status, responseBuffer.toString('utf-8'));\n }\n\n res.statusCode = upstream.status;\n upstream.headers.forEach((value, key) => {\n if (key.toLowerCase() === 'content-encoding') return;\n res.setHeader(key, value);\n });\n res.end(responseBuffer);\n } catch (error) {\n res.statusCode = 502;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: error instanceof Error ? error.message : 'Proxy request failed.' }));\n }\n};\n\nexport const createProxyPlugin = (route = PROXY_PATH, logOptions?: ProxyLogOptions): Plugin => ({\n name: 'llmnative-dev-proxy',\n configureServer(server) {\n server.middlewares.use(route, (req, res) => { void writeProxyResponse(req, res, route, logOptions); });\n },\n configurePreviewServer(server) {\n server.middlewares.use(route, (req, res) => { void writeProxyResponse(req, res, route, logOptions); });\n },\n});\n"],"mappings":"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"}
package/dist/vite.mjs CHANGED
@@ -97,3 +97,5 @@ var u = async (t, n, r, a) => {
97
97
  });
98
98
  //#endregion
99
99
  export { a as PROXY_PATH, d as createProxyPlugin };
100
+
101
+ //# sourceMappingURL=vite.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite.mjs","names":[],"sources":["../src/providers/proxy/vite.ts"],"sourcesContent":["import { appendFile, mkdir } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport type { Plugin } from 'vite';\nimport { LLM_LOG_ID_HEADER } from './logHeader';\n\nconst PROXY_CALLER_HEADER = 'x-llmnative-proxy';\nexport const PROXY_PATH = '/api/proxy';\n\n/** Logging file di richiesta/risposta LLM — SOLO dev, mai attivo di default. Un file per\n * conversazione: il client (useAgent, CMS) genera un `logId` stabile per l'intera sessione e\n * lo manda su ogni chiamata come header `x-llmnative-log-id`; il proxy accoda ogni turno allo\n * stesso file, non lo sovrascrive. Vedi AICompleteRequest.logId per il lato client. */\nexport interface ProxyLogOptions {\n enabled: boolean;\n /** Cartella di destinazione, relativa alla cwd del dev server (il progetto consumer, es.\n * il CMS) o assoluta. Creata se assente. */\n dir: string;\n}\n\nconst readRequestBody = async (req: IncomingMessage): Promise<Buffer | undefined> => {\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n return chunks.length === 0 ? undefined : Buffer.concat(chunks);\n};\n\n/** Solo caratteri sicuri per un nome file — l'id è generato da noi (crypto.randomUUID() lato\n * client), ma niente path traversal per costruzione anche se un client si comportasse male. */\nfunction sanitizeLogId(logId: string): string {\n return logId.replace(/[^a-zA-Z0-9_-]/g, '').slice(0, 128) || 'unknown';\n}\n\nfunction prettyJsonOrRaw(text: string): string {\n if (!text.trim()) return '(empty body)';\n try {\n return JSON.stringify(JSON.parse(text), null, 2);\n } catch {\n return text;\n }\n}\n\nasync function appendLogBlock(logOptions: ProxyLogOptions, logId: string, target: string, requestBody: string, status: number, responseBody: string) {\n try {\n await mkdir(logOptions.dir, { recursive: true });\n const block = [\n '',\n '='.repeat(80),\n `Turn @ ${new Date().toISOString()}`,\n `Target: ${target}`,\n '--- REQUEST BODY ---',\n prettyJsonOrRaw(requestBody),\n `--- RESPONSE BODY (status ${status}) ---`,\n prettyJsonOrRaw(responseBody),\n '='.repeat(80),\n '',\n ].join('\\n');\n await appendFile(path.join(logOptions.dir, `${sanitizeLogId(logId)}.txt`), block, 'utf-8');\n } catch (error) {\n // Un log fallito non deve mai far fallire la vera richiesta — solo un avviso in console.\n console.warn('[llmnative-dev-proxy] Failed to write LLM log:', error);\n }\n}\n\nconst writeProxyResponse = async (req: IncomingMessage, res: ServerResponse, route: string, logOptions?: ProxyLogOptions) => {\n const requestUrl = new URL(req.url || '/', 'http://localhost');\n const target = requestUrl.searchParams.get('url');\n\n if (req.headers[PROXY_CALLER_HEADER] !== '1') {\n res.statusCode = 403;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: 'Proxy access denied.' }));\n return;\n }\n\n if (!target) {\n res.statusCode = 400;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: 'Missing \"url\" query parameter.' }));\n return;\n }\n\n try {\n const targetUrl = new URL(target);\n if (targetUrl.pathname.startsWith(route) && ['localhost', '127.0.0.1'].includes(targetUrl.hostname)) {\n res.statusCode = 508;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: 'Recursive proxy target detected.', target }));\n return;\n }\n\n const body = await readRequestBody(req);\n const logId = req.headers[LLM_LOG_ID_HEADER];\n const headers = new Headers();\n // L'header di log è consumato QUI, mai inoltrato al provider upstream (stesso\n // trattamento del PROXY_CALLER_HEADER) — è un dettaglio nostro, non dell'API esterna.\n const skipHeaders = new Set([PROXY_CALLER_HEADER, LLM_LOG_ID_HEADER, 'host', 'origin', 'referer', 'content-length', 'transfer-encoding', 'connection']);\n\n Object.entries(req.headers).forEach(([key, value]) => {\n if (!value || skipHeaders.has(key.toLowerCase())) return;\n headers.set(key, Array.isArray(value) ? value.join(', ') : value);\n });\n\n const upstream = await fetch(target, {\n method: req.method || 'GET',\n headers,\n body: body && !['GET', 'HEAD'].includes((req.method || 'GET').toUpperCase())\n ? Uint8Array.from(body)\n : undefined,\n redirect: 'follow',\n });\n\n const responseBuffer = Buffer.from(await upstream.arrayBuffer());\n\n if (logOptions?.enabled && typeof logId === 'string' && logId) {\n void appendLogBlock(logOptions, logId, target, body?.toString('utf-8') ?? '', upstream.status, responseBuffer.toString('utf-8'));\n }\n\n res.statusCode = upstream.status;\n upstream.headers.forEach((value, key) => {\n if (key.toLowerCase() === 'content-encoding') return;\n res.setHeader(key, value);\n });\n res.end(responseBuffer);\n } catch (error) {\n res.statusCode = 502;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: error instanceof Error ? error.message : 'Proxy request failed.' }));\n }\n};\n\nexport const createProxyPlugin = (route = PROXY_PATH, logOptions?: ProxyLogOptions): Plugin => ({\n name: 'llmnative-dev-proxy',\n configureServer(server) {\n server.middlewares.use(route, (req, res) => { void writeProxyResponse(req, res, route, logOptions); });\n },\n configurePreviewServer(server) {\n server.middlewares.use(route, (req, res) => { void writeProxyResponse(req, res, route, logOptions); });\n },\n});\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llmnative/react",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "LLM-native React framework for deterministic UI generation. Data-driven by default, schema-driven optional. Ultra-low token consumption — built for AI agents, not just humans.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",