@llmnative/react 1.2.1 → 1.4.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.
- package/dist/index.css +1 -1
- package/dist/index.js +15 -15
- package/dist/index.mjs +4490 -4275
- package/dist/logHeader-Busbc8OO.mjs +4 -0
- package/dist/logHeader-CbzOhx-u.js +1 -0
- package/dist/types/src/I18n.d.ts +1 -0
- package/dist/types/src/components/blocks/Dropdown.d.ts +3 -1
- package/dist/types/src/components/index.d.ts +2 -0
- package/dist/types/src/components/ui/Table.d.ts +8 -1
- package/dist/types/src/components/ui/useStableRecordKey.d.ts +14 -1
- package/dist/types/src/components/widgets/Chatbot.d.ts +75 -0
- package/dist/types/src/components/widgets/Prompt.d.ts +4 -6
- package/dist/types/src/libs/csv.d.ts +13 -0
- package/dist/types/src/libs/index.d.ts +1 -0
- package/dist/types/src/libs/promptUtils.d.ts +12 -0
- package/dist/types/src/providers/ProviderRegistryContext.d.ts +1 -1
- package/dist/types/src/providers/ProviderSession.d.ts +7 -0
- package/dist/types/src/providers/ai/AIProvider.d.ts +7 -0
- package/dist/types/src/providers/ai/shared.d.ts +1 -1
- package/dist/types/src/providers/proxy/logHeader.d.ts +7 -0
- package/dist/types/src/providers/proxy/vite.d.ts +11 -1
- package/dist/vite.js +2 -1
- package/dist/vite.mjs +65 -29
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
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}});
|
package/dist/types/src/I18n.d.ts
CHANGED
|
@@ -20,6 +20,8 @@ export interface DropdownProps extends MotionUIProps {
|
|
|
20
20
|
/** Controlled open state. */
|
|
21
21
|
open?: boolean;
|
|
22
22
|
onOpenChange?: (open: boolean) => void;
|
|
23
|
+
/** Close after an official `DropdownItem` selection. Defaults to `true`. */
|
|
24
|
+
closeOnSelect?: boolean;
|
|
23
25
|
/** When `true`, the menu stays open regardless of interaction. */
|
|
24
26
|
staticOpen?: boolean;
|
|
25
27
|
/** Horizontal alignment relative to the trigger. */
|
|
@@ -83,7 +85,7 @@ interface DropdownHeaderProps {
|
|
|
83
85
|
interface DropdownDividerProps {
|
|
84
86
|
className?: string;
|
|
85
87
|
}
|
|
86
|
-
export declare const Dropdown: ({ children, trigger, badge, header, footer, defaultOpen, open: controlledOpen, onOpenChange, staticOpen, position, placement, strategy, wrapperClassName, className, before, after, triggerClassName, menuClassName, badgeClassName, headerClassName, footerClassName, motion: motionConfig, }: DropdownProps) => React.JSX.Element;
|
|
88
|
+
export declare const Dropdown: ({ children, trigger, badge, header, footer, defaultOpen, open: controlledOpen, onOpenChange, closeOnSelect, staticOpen, position, placement, strategy, wrapperClassName, className, before, after, triggerClassName, menuClassName, badgeClassName, headerClassName, footerClassName, motion: motionConfig, }: DropdownProps) => React.JSX.Element;
|
|
87
89
|
/**
|
|
88
90
|
* Async/searchable extension of Dropdown. It owns only the interaction model; applications
|
|
89
91
|
* supply their own data reader and selection action (site, tenant, entity, or any resource).
|
|
@@ -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';
|
|
@@ -65,6 +66,7 @@ export { default as ImageEditor } from './widgets/ImageEditor';
|
|
|
65
66
|
export { default as MarkdownReader } from './widgets/MarkdownReader';
|
|
66
67
|
export type { MarkdownReaderProps } from './widgets/MarkdownReader';
|
|
67
68
|
export * from './widgets/Prompt';
|
|
69
|
+
export * from './widgets/Chatbot';
|
|
68
70
|
export { default as TabDynamic } from './widgets/TabDynamic';
|
|
69
71
|
export { default as Component } from './Component';
|
|
70
72
|
export { ComponentBlock } from './Component';
|
|
@@ -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
|
-
|
|
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 {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type EditorCommand } from '../ui/fields/ContextMenu';
|
|
3
|
+
/**
|
|
4
|
+
* Chatbot — l'input/composer AI condiviso, estratto da `Prompt` (CR-071): textarea,
|
|
5
|
+
* slash-command, allegati, model picker, dropdown tono/stile/lingua/voce/temperatura
|
|
6
|
+
* opzionali, bottone invio/stop. NON dipende da `Form` (nessun `useFormContext`), NON
|
|
7
|
+
* conosce provider AI/variabili di template — riceve solo ciò che gli viene passato e
|
|
8
|
+
* restituisce a `onSubmit` il pacchetto risolto dell'interazione dell'utente. Chi lo usa
|
|
9
|
+
* (`Prompt` in modalità RUN, o un consumer di conversazione multi-turno come Agentico nel
|
|
10
|
+
* CMS) decide cosa fare con quel pacchetto — questa è la sua "politica", mai
|
|
11
|
+
* responsabilità di Chatbot. Non renderizza una lista messaggi/transcript: quello resta
|
|
12
|
+
* specifico di chi lo usa.
|
|
13
|
+
*/
|
|
14
|
+
export type ChatbotAction = {
|
|
15
|
+
key: string;
|
|
16
|
+
icon: string;
|
|
17
|
+
label?: string;
|
|
18
|
+
content?: React.ReactNode;
|
|
19
|
+
};
|
|
20
|
+
export interface ChatbotSubmitPayload {
|
|
21
|
+
text: string;
|
|
22
|
+
/** Grezzi — la conversione a un formato specifico (es. AIAttachment base64) resta
|
|
23
|
+
* scelta del chiamante, mai di Chatbot. */
|
|
24
|
+
files: File[];
|
|
25
|
+
model: string;
|
|
26
|
+
role?: string;
|
|
27
|
+
voice?: string;
|
|
28
|
+
style?: string;
|
|
29
|
+
language?: string;
|
|
30
|
+
temperature?: number;
|
|
31
|
+
}
|
|
32
|
+
export interface ChatbotModelOption {
|
|
33
|
+
label: string;
|
|
34
|
+
value: string;
|
|
35
|
+
}
|
|
36
|
+
export interface ChatbotProps {
|
|
37
|
+
/** Attributo `name` nativo sulla textarea sottostante — puro attributo DOM, MAI un
|
|
38
|
+
* binding a Form (Chatbot non lo legge/scrive): serve solo a chi ha bisogno di un
|
|
39
|
+
* selettore stabile (test, e2e) sull'elemento reale. */
|
|
40
|
+
name?: string;
|
|
41
|
+
value: string;
|
|
42
|
+
onChange: (value: string) => void;
|
|
43
|
+
placeholder?: string;
|
|
44
|
+
/** Turno in corso — mai usato per disabilitare il click (vedi bottone invio/stop
|
|
45
|
+
* sotto): uno stato "stopping" transitorio parte subito al click, indipendente dal
|
|
46
|
+
* vero completamento dell'abort. */
|
|
47
|
+
running?: boolean;
|
|
48
|
+
onStop?: () => void;
|
|
49
|
+
/** "Non posso inviare ora" — il motivo (AI non configurata, ecc.) resta del chiamante,
|
|
50
|
+
* Chatbot si limita a disabilitare il bottone invio. */
|
|
51
|
+
disabled?: boolean;
|
|
52
|
+
onSubmit: (payload: ChatbotSubmitPayload) => void;
|
|
53
|
+
attachments?: boolean;
|
|
54
|
+
commands?: EditorCommand[];
|
|
55
|
+
commandsTrigger?: string;
|
|
56
|
+
models?: ChatbotModelOption[];
|
|
57
|
+
selectedModel?: string;
|
|
58
|
+
onModelChange?: (id: string) => void;
|
|
59
|
+
/** Dropdown ruolo/lingua/voce/stile/temperatura — stato interno non controllato
|
|
60
|
+
* (seedato da default*), incluso nel payload di ogni submit. Default `false`: un
|
|
61
|
+
* consumer di chat libera (Agentico) non ne ha bisogno. */
|
|
62
|
+
showSettings?: boolean;
|
|
63
|
+
defaultRole?: string;
|
|
64
|
+
defaultVoice?: string;
|
|
65
|
+
defaultStyle?: string;
|
|
66
|
+
defaultLanguage?: string;
|
|
67
|
+
defaultTemperature?: number;
|
|
68
|
+
actions?: ChatbotAction[];
|
|
69
|
+
minHeight?: number;
|
|
70
|
+
maxHeight?: number;
|
|
71
|
+
wrapperClassName?: string;
|
|
72
|
+
className?: string;
|
|
73
|
+
}
|
|
74
|
+
export declare function Chatbot({ name, value, onChange, placeholder, running, onStop, disabled, onSubmit, attachments, commands, commandsTrigger, models, selectedModel, onModelChange, showSettings, defaultRole, defaultVoice, defaultStyle, defaultLanguage, defaultTemperature, actions, minHeight, maxHeight, wrapperClassName, className, }: ChatbotProps): React.JSX.Element;
|
|
75
|
+
export default Chatbot;
|
|
@@ -4,16 +4,14 @@ import { type AIProviderAdapter, type AIRequestOptions, type AIAttachment } from
|
|
|
4
4
|
import { RecordProps } from '../../providers/data/DataProvider';
|
|
5
5
|
import { type EditorCommand } from '../ui/fields/ContextMenu';
|
|
6
6
|
import { FormFieldProps } from './Form';
|
|
7
|
+
import { type ChatbotAction } from './Chatbot';
|
|
7
8
|
export declare enum PromptMode {
|
|
8
9
|
EDIT = "edit",
|
|
9
10
|
RUN = "run"
|
|
10
11
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
label?: string;
|
|
15
|
-
content?: React.ReactNode;
|
|
16
|
-
};
|
|
12
|
+
/** Alias mantenuto per compatibilità pubblica — la vera definizione vive ora in
|
|
13
|
+
* `Chatbot.tsx` (CR-071), da cui `PromptRun` la eredita passandola a `<Chatbot actions>`. */
|
|
14
|
+
export type PromptAction = ChatbotAction;
|
|
17
15
|
export type PromptStatusItem = 'tokensIn' | 'tokensOut' | 'contextPercent' | 'model' | 'duration' | {
|
|
18
16
|
key: string;
|
|
19
17
|
render: (stats: PromptRunStats) => React.ReactNode;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface CsvParseResult {
|
|
2
|
+
data: Record<string, string | null | undefined>[];
|
|
3
|
+
fields: string[];
|
|
4
|
+
}
|
|
5
|
+
/** Parsing puro di testo CSV/TSV già in memoria — stesso motore (papaparse) di
|
|
6
|
+
* `<UploadCSV>` (ui/fields/UploadCSV.tsx), estratto qui perché un chiamante che ha già il testo
|
|
7
|
+
* (es. un allegato decodificato lato agente, non un evento `<input type="file">`) non ha un
|
|
8
|
+
* `File`/`ChangeEvent` da passare a quel componente — solo la stringa. `UploadCSV` resta
|
|
9
|
+
* l'ingresso quando l'origine è davvero un file scelto dall'utente nella UI; questa funzione è
|
|
10
|
+
* l'ingresso quando l'origine è testo già in mano al chiamante. */
|
|
11
|
+
export declare function parseCsvText(text: string, opts?: {
|
|
12
|
+
delimiter?: string;
|
|
13
|
+
}): CsvParseResult;
|
|
@@ -21,4 +21,16 @@ export declare const PromptUtils: {
|
|
|
21
21
|
* Convert a browser File into the transport-ready attachment shape used by AI providers.
|
|
22
22
|
*/
|
|
23
23
|
fileToAttachment(file: File): Promise<AIAttachment>;
|
|
24
|
+
/**
|
|
25
|
+
* Whether an attachment's mimetype is plain text (or a text-like format such as JSON/CSV/
|
|
26
|
+
* XML) and should therefore be decoded and inlined as a text content block by the calling
|
|
27
|
+
* provider adapter, instead of sent as a binary document/inline-data block.
|
|
28
|
+
*/
|
|
29
|
+
isTextAttachment(mimeType: string): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Decodes a base64 attachment payload back to UTF-8 text — `atob` alone only yields a
|
|
32
|
+
* "binary string" (Latin-1), which corrupts multi-byte characters (accents, emoji); this
|
|
33
|
+
* re-interprets those bytes as UTF-8 first.
|
|
34
|
+
*/
|
|
35
|
+
decodeBase64Text(base64: string): string;
|
|
24
36
|
};
|
|
@@ -23,7 +23,7 @@ export type ProviderRegistrySnapshot = Record<string, {
|
|
|
23
23
|
* Replaces a single named adapter in a provider registry at runtime, in ANY
|
|
24
24
|
* category — one of the 6 built-in ones (typed) or an arbitrary custom one a
|
|
25
25
|
* vertical registered (string). Calls the previous adapter's dispose() (if
|
|
26
|
-
* defined)
|
|
26
|
+
* defined) only when the instance changes, then propagates the new registry to every
|
|
27
27
|
* useXProvider()/useProvider() consumer. If `category` didn't exist yet, it's
|
|
28
28
|
* created — categories can be registered after bootstrap, not only via <App providers>.
|
|
29
29
|
*/
|
|
@@ -40,6 +40,13 @@ export type ProviderSessionFactory = (assignment: ProviderSessionAssignment) =>
|
|
|
40
40
|
export declare const registerProviderSessionFactory: (category: string, type: string, factory: ProviderSessionFactory) => void;
|
|
41
41
|
export type ProviderSessionSwitchOptions = {
|
|
42
42
|
fetchOptions?: RequestInit;
|
|
43
|
+
/**
|
|
44
|
+
* Stable identity of the logical session being requested (for example a tenant id).
|
|
45
|
+
* Concurrent calls with the same key share one switch, including calls made by
|
|
46
|
+
* different useProviderSession() consumers. Once active, requesting that key again
|
|
47
|
+
* is a no-op until another keyed session is requested.
|
|
48
|
+
*/
|
|
49
|
+
sessionKey?: string;
|
|
43
50
|
/**
|
|
44
51
|
* Inspect or transform the raw response before it's applied — e.g. inject an extra
|
|
45
52
|
* category, filter one out, forward it to an audit log. Whatever this returns
|
|
@@ -73,6 +73,13 @@ export interface AICompleteRequest extends AIRequestOptions {
|
|
|
73
73
|
tools?: AIToolDefinition[];
|
|
74
74
|
/** Annulla la richiesta in corso (es. un bottone "interrompi" lato chiamante). */
|
|
75
75
|
signal?: AbortSignal;
|
|
76
|
+
/** Id stabile per l'intera conversazione (generato una volta da chi possiede la sessione,
|
|
77
|
+
* es. useAgent — mai dal provider), trasportato come header `x-llmnative-log-id` verso il
|
|
78
|
+
* proxy dev di Vite: se il logging file è abilitato (`createProxyPlugin` con
|
|
79
|
+
* `ProxyLogOptions.enabled`), il proxy accoda request/response di OGNI turno con questo id
|
|
80
|
+
* allo stesso file — un file per conversazione. Assente = nessun logging per questa
|
|
81
|
+
* chiamata, comportamento identico a oggi. */
|
|
82
|
+
logId?: string;
|
|
76
83
|
}
|
|
77
84
|
/** Esito di una chiamata completate — testo semplice, oppure una o più richieste di tool
|
|
78
85
|
* call (eventualmente accompagnate da testo, quando il modello commenta prima di invocare). */
|
|
@@ -21,7 +21,7 @@ export type AIProviderDefinition = {
|
|
|
21
21
|
credentialsHint?: string;
|
|
22
22
|
capabilities?: Omit<AIProviderCapabilities, 'models'>;
|
|
23
23
|
discoverModels: (apiKey: string) => Promise<string[]>;
|
|
24
|
-
complete: (apiKey: string, request: Required<Pick<AICompleteRequest, 'prompt' | 'model'>> & AIRequestOptions & Pick<AICompleteRequest, 'history' | 'tools' | 'signal'>) => Promise<AICompleteResult | null>;
|
|
24
|
+
complete: (apiKey: string, request: Required<Pick<AICompleteRequest, 'prompt' | 'model'>> & AIRequestOptions & Pick<AICompleteRequest, 'history' | 'tools' | 'signal' | 'logId'>) => Promise<AICompleteResult | null>;
|
|
25
25
|
/**
|
|
26
26
|
* Per-provider auth check. When omitted, the default implementation in
|
|
27
27
|
* RuntimeAIProvider calls discoverModels() live (no cache) and treats any
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Nome dell'header che trasporta l'id di conversazione dal client (provider AI, via
|
|
2
|
+
* `AICompleteRequest.logId`) al proxy dev di Vite (`vite.ts`), che lo usa per accodare
|
|
3
|
+
* request/response allo stesso file di log per l'intera conversazione. File separato e
|
|
4
|
+
* privo di import Node-specific — i provider AI lato browser (anthropic.ts/gemini.ts/
|
|
5
|
+
* openaiCompatible.ts) lo importano per impostare l'header, senza portarsi dietro le
|
|
6
|
+
* dipendenze Node di vite.ts nel proprio bundle. */
|
|
7
|
+
export declare const LLM_LOG_ID_HEADER = "x-llmnative-log-id";
|
|
@@ -1,3 +1,13 @@
|
|
|
1
1
|
import type { Plugin } from 'vite';
|
|
2
2
|
export declare const PROXY_PATH = "/api/proxy";
|
|
3
|
-
|
|
3
|
+
/** Logging file di richiesta/risposta LLM — SOLO dev, mai attivo di default. Un file per
|
|
4
|
+
* conversazione: il client (useAgent, CMS) genera un `logId` stabile per l'intera sessione e
|
|
5
|
+
* lo manda su ogni chiamata come header `x-llmnative-log-id`; il proxy accoda ogni turno allo
|
|
6
|
+
* stesso file, non lo sovrascrive. Vedi AICompleteRequest.logId per il lato client. */
|
|
7
|
+
export interface ProxyLogOptions {
|
|
8
|
+
enabled: boolean;
|
|
9
|
+
/** Cartella di destinazione, relativa alla cwd del dev server (il progetto consumer, es.
|
|
10
|
+
* il CMS) o assoluta. Creata se assente. */
|
|
11
|
+
dir: string;
|
|
12
|
+
}
|
|
13
|
+
export declare const createProxyPlugin: (route?: string, logOptions?: ProxyLogOptions) => Plugin;
|
package/dist/vite.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var
|
|
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;
|
package/dist/vite.mjs
CHANGED
|
@@ -1,28 +1,64 @@
|
|
|
1
|
+
import { t as e } from "./logHeader-Busbc8OO.mjs";
|
|
2
|
+
import { appendFile as t, mkdir as n } from "node:fs/promises";
|
|
3
|
+
import r from "node:path";
|
|
1
4
|
//#region src/providers/proxy/vite.ts
|
|
2
|
-
var
|
|
5
|
+
var i = "x-llmnative-proxy", a = "/api/proxy", o = async (e) => {
|
|
3
6
|
let t = [];
|
|
4
7
|
for await (let n of e) t.push(Buffer.isBuffer(n) ? n : Buffer.from(n));
|
|
5
8
|
return t.length === 0 ? void 0 : Buffer.concat(t);
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
};
|
|
10
|
+
function s(e) {
|
|
11
|
+
return e.replace(/[^a-zA-Z0-9_-]/g, "").slice(0, 128) || "unknown";
|
|
12
|
+
}
|
|
13
|
+
function c(e) {
|
|
14
|
+
if (!e.trim()) return "(empty body)";
|
|
15
|
+
try {
|
|
16
|
+
return JSON.stringify(JSON.parse(e), null, 2);
|
|
17
|
+
} catch {
|
|
18
|
+
return e;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async function l(e, i, a, o, l, u) {
|
|
22
|
+
try {
|
|
23
|
+
await n(e.dir, { recursive: !0 });
|
|
24
|
+
let d = [
|
|
25
|
+
"",
|
|
26
|
+
"=".repeat(80),
|
|
27
|
+
`Turn @ ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
28
|
+
`Target: ${a}`,
|
|
29
|
+
"--- REQUEST BODY ---",
|
|
30
|
+
c(o),
|
|
31
|
+
`--- RESPONSE BODY (status ${l}) ---`,
|
|
32
|
+
c(u),
|
|
33
|
+
"=".repeat(80),
|
|
34
|
+
""
|
|
35
|
+
].join("\n");
|
|
36
|
+
await t(r.join(e.dir, `${s(i)}.txt`), d, "utf-8");
|
|
37
|
+
} catch (e) {
|
|
38
|
+
console.warn("[llmnative-dev-proxy] Failed to write LLM log:", e);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
var u = async (t, n, r, a) => {
|
|
42
|
+
let s = new URL(t.url || "/", "http://localhost").searchParams.get("url");
|
|
43
|
+
if (t.headers[i] !== "1") {
|
|
44
|
+
n.statusCode = 403, n.setHeader("Content-Type", "application/json"), n.end(JSON.stringify({ error: "Proxy access denied." }));
|
|
10
45
|
return;
|
|
11
46
|
}
|
|
12
|
-
if (!
|
|
13
|
-
|
|
47
|
+
if (!s) {
|
|
48
|
+
n.statusCode = 400, n.setHeader("Content-Type", "application/json"), n.end(JSON.stringify({ error: "Missing \"url\" query parameter." }));
|
|
14
49
|
return;
|
|
15
50
|
}
|
|
16
51
|
try {
|
|
17
|
-
let
|
|
18
|
-
if (
|
|
19
|
-
|
|
52
|
+
let c = new URL(s);
|
|
53
|
+
if (c.pathname.startsWith(r) && ["localhost", "127.0.0.1"].includes(c.hostname)) {
|
|
54
|
+
n.statusCode = 508, n.setHeader("Content-Type", "application/json"), n.end(JSON.stringify({
|
|
20
55
|
error: "Recursive proxy target detected.",
|
|
21
|
-
target:
|
|
56
|
+
target: s
|
|
22
57
|
}));
|
|
23
58
|
return;
|
|
24
59
|
}
|
|
25
|
-
let
|
|
60
|
+
let u = await o(t), d = t.headers[e], f = new Headers(), p = /* @__PURE__ */ new Set([
|
|
61
|
+
i,
|
|
26
62
|
e,
|
|
27
63
|
"host",
|
|
28
64
|
"origin",
|
|
@@ -32,32 +68,32 @@ var e = "x-llmnative-proxy", t = "/api/proxy", n = async (e) => {
|
|
|
32
68
|
"connection"
|
|
33
69
|
]);
|
|
34
70
|
Object.entries(t.headers).forEach(([e, t]) => {
|
|
35
|
-
!t ||
|
|
71
|
+
!t || p.has(e.toLowerCase()) || f.set(e, Array.isArray(t) ? t.join(", ") : t);
|
|
36
72
|
});
|
|
37
|
-
let
|
|
73
|
+
let m = await fetch(s, {
|
|
38
74
|
method: t.method || "GET",
|
|
39
|
-
headers:
|
|
40
|
-
body:
|
|
75
|
+
headers: f,
|
|
76
|
+
body: u && !["GET", "HEAD"].includes((t.method || "GET").toUpperCase()) ? Uint8Array.from(u) : void 0,
|
|
41
77
|
redirect: "follow"
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
t.toLowerCase() !== "content-encoding" &&
|
|
45
|
-
}),
|
|
78
|
+
}), h = Buffer.from(await m.arrayBuffer());
|
|
79
|
+
a?.enabled && typeof d == "string" && d && l(a, d, s, u?.toString("utf-8") ?? "", m.status, h.toString("utf-8")), n.statusCode = m.status, m.headers.forEach((e, t) => {
|
|
80
|
+
t.toLowerCase() !== "content-encoding" && n.setHeader(t, e);
|
|
81
|
+
}), n.end(h);
|
|
46
82
|
} catch (e) {
|
|
47
|
-
|
|
83
|
+
n.statusCode = 502, n.setHeader("Content-Type", "application/json"), n.end(JSON.stringify({ error: e instanceof Error ? e.message : "Proxy request failed." }));
|
|
48
84
|
}
|
|
49
|
-
},
|
|
85
|
+
}, d = (e = a, t) => ({
|
|
50
86
|
name: "llmnative-dev-proxy",
|
|
51
|
-
configureServer(
|
|
52
|
-
|
|
53
|
-
|
|
87
|
+
configureServer(n) {
|
|
88
|
+
n.middlewares.use(e, (n, r) => {
|
|
89
|
+
u(n, r, e, t);
|
|
54
90
|
});
|
|
55
91
|
},
|
|
56
|
-
configurePreviewServer(
|
|
57
|
-
|
|
58
|
-
|
|
92
|
+
configurePreviewServer(n) {
|
|
93
|
+
n.middlewares.use(e, (n, r) => {
|
|
94
|
+
u(n, r, e, t);
|
|
59
95
|
});
|
|
60
96
|
}
|
|
61
97
|
});
|
|
62
98
|
//#endregion
|
|
63
|
-
export {
|
|
99
|
+
export { a as PROXY_PATH, d as createProxyPlugin };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llmnative/react",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.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",
|