npm: fixed publish

This commit is contained in:
Daniel Salazar
2025-09-19 16:26:16 -07:00
parent a3cbee1b58
commit d6fbe94364
6 changed files with 766 additions and 689 deletions

View File

@@ -1,479 +1,496 @@
// index.d.ts
declare global {
interface Window {
puter: Puter;
}
}
declare namespace Puter {
// Main Puter interface
interface Puter {
// Properties
appID: string;
env: 'app' | 'web' | 'gui';
declare class Puter {
// Properties
appID: string;
env: 'app' | 'web' | 'gui';
// Utility methods
print(text: string, options?: { code?: boolean }): void;
randName(separator?: string): string;
exit(statusCode?: number): void;
// Utility methods
print(text: string, options?: { code?: boolean }): void;
randName(separator?: string): string;
exit(statusCode?: number): void;
// Sub-modules
ai: AI;
apps: Apps;
auth: Auth;
drivers: Drivers;
fs: FileSystem;
hosting: Hosting;
kv: KeyValue;
net: Networking;
perms: Permissions;
ui: UI;
workers: Workers;
}
// AI Module
interface AI {
chat(prompt: string, options?: ChatOptions): Promise<ChatResponse>;
chat(prompt: string, testMode?: boolean, options?: ChatOptions): Promise<ChatResponse>;
chat(prompt: string, imageURL?: string, testMode?: boolean, options?: ChatOptions): Promise<ChatResponse>;
chat(prompt: string, imageURLArray?: string[], testMode?: boolean, options?: ChatOptions): Promise<ChatResponse>;
chat(messages: ChatMessage[], testMode?: boolean, options?: ChatOptions): Promise<ChatResponse>;
img2txt(image: string | File | Blob, testMode?: boolean): Promise<string>;
txt2img(prompt: string, testMode?: boolean): Promise<HTMLImageElement>;
txt2img(prompt: string, options?: Txt2ImgOptions): Promise<HTMLImageElement>;
txt2speech(text: string): Promise<HTMLAudioElement>;
txt2speech(text: string, options?: Txt2SpeechOptions): Promise<HTMLAudioElement>;
txt2speech(text: string, language?: string): Promise<HTMLAudioElement>;
txt2speech(text: string, language?: string, voice?: string): Promise<HTMLAudioElement>;
txt2speech(text: string, language?: string, voice?: string, engine?: string): Promise<HTMLAudioElement>;
}
interface ChatOptions {
model?: string;
stream?: boolean;
max_tokens?: number;
temperature?: number;
tools?: ToolDefinition[];
}
interface ToolDefinition {
type: 'function';
function: {
name: string;
description: string;
parameters: object;
strict?: boolean;
};
}
interface ChatMessage {
role: 'system' | 'assistant' | 'user' | 'function' | 'tool';
content: string | ContentObject[];
tool_call_id?: string;
}
interface ContentObject {
type: 'text' | 'file';
text?: string;
puter_path?: string;
}
interface ChatResponse {
message: {
role: string;
content: string;
tool_calls?: ToolCall[];
};
}
interface ToolCall {
id: string;
function: {
name: string;
arguments: string;
};
}
interface Txt2ImgOptions {
model?: 'gpt-image-1' | 'gemini-2.5-flash-image-preview' | 'dall-e-3';
quality?: 'high' | 'medium' | 'low' | 'hd' | 'standard';
input_image?: string;
input_image_mime_type?: string;
}
interface Txt2SpeechOptions {
language?: string;
voice?: string;
engine?: 'standard' | 'neural' | 'generative';
}
// Apps Module
interface Apps {
create(name: string, indexURL: string): Promise<App>;
create(name: string, indexURL: string, title?: string): Promise<App>;
create(name: string, indexURL: string, title?: string, description?: string): Promise<App>;
create(options: CreateAppOptions): Promise<App>;
delete(name: string): Promise<App>;
get(name: string, options?: GetAppOptions): Promise<App>;
list(options?: ListAppOptions): Promise<App[]>;
update(name: string, attributes: UpdateAppAttributes): Promise<App>;
}
interface CreateAppOptions {
name: string;
indexURL: string;
title?: string;
description?: string;
icon?: string;
maximizeOnStart?: boolean;
filetypeAssociations?: string[];
}
interface GetAppOptions {
stats_period?: StatsPeriod;
icon_size?: null | 16 | 32 | 64 | 128 | 256 | 512;
}
interface ListAppOptions extends GetAppOptions { }
interface UpdateAppAttributes {
name?: string;
indexURL?: string;
title?: string;
description?: string;
icon?: string;
maximizeOnStart?: boolean;
filetypeAssociations?: string[];
}
type StatsPeriod = 'all' | 'today' | 'yesterday' | '7d' | '30d' | 'this_month' | 'last_month' | 'this_year' | 'last_year' | 'month_to_date' | 'year_to_date' | 'last_12_months';
interface App {
uid: string;
name: string;
icon: string;
description: string;
title: string;
maximize_on_start: boolean;
index_url: string;
created_at: string;
background: boolean;
filetype_associations: string[];
open_count: number;
user_count: number;
}
// Auth Module
interface Auth {
signIn(options?: { attempt_temp_user_creation?: boolean }): Promise<boolean>;
signOut(): void;
isSignedIn(): boolean;
getUser(): Promise<User>;
}
interface User {
uuid: string;
username: string;
email_confirmed: boolean;
}
// Drivers Module
interface Drivers {
call(interface: string, driver: string, method: string, args?: object): Promise<any>;
}
// FileSystem Module
interface FileSystem {
copy(source: string, destination: string, options?: CopyOptions): Promise<FSItem>;
delete(path: string, options?: DeleteOptions): Promise<void>;
getReadURL(path: string, expiresIn?: number): Promise<string>;
mkdir(path: string, options?: MkdirOptions): Promise<FSItem>;
move(source: string, destination: string, options?: MoveOptions): Promise<FSItem>;
read(path: string, options?: ReadOptions): Promise<Blob>;
readdir(path: string, options?: ReaddirOptions): Promise<FSItem[]>;
readdir(options?: ReaddirOptions): Promise<FSItem[]>;
rename(path: string, newName: string): Promise<FSItem>;
space(): Promise<SpaceInfo>;
stat(path: string): Promise<FSItem>;
upload(items: FileList | File[] | Blob[], dirPath?: string, options?: object): Promise<FSItem[]>;
write(path: string, data?: string | File | Blob, options?: WriteOptions): Promise<FSItem>;
}
interface CopyOptions {
overwrite?: boolean;
dedupeName?: boolean;
newName?: string;
}
interface DeleteOptions {
recursive?: boolean;
descendantsOnly?: boolean;
}
interface MkdirOptions {
overwrite?: boolean;
dedupeName?: boolean;
createMissingParents?: boolean;
}
interface MoveOptions extends CopyOptions {
createMissingParents?: boolean;
}
interface ReadOptions {
offset?: number;
byte_count?: number;
}
interface ReaddirOptions {
path?: string;
uid?: string;
}
interface WriteOptions {
overwrite?: boolean;
dedupeName?: boolean;
createMissingParents?: boolean;
}
interface SpaceInfo {
capacity: number;
used: number;
}
interface FSItem {
id: string;
uid: string;
name: string;
path: string;
is_dir: boolean;
parent_id: string;
parent_uid: string;
created: number;
modified: number;
accessed: number;
size: number | null;
writable: boolean;
read(): Promise<Blob>;
readdir(): Promise<FSItem[]>;
}
// Hosting Module
interface Hosting {
create(subdomain: string, dirPath?: string): Promise<Subdomain>;
delete(subdomain: string): Promise<boolean>;
get(subdomain: string): Promise<Subdomain>;
list(): Promise<Subdomain[]>;
update(subdomain: string, dirPath?: string): Promise<Subdomain>;
}
interface Subdomain {
uid: string;
subdomain: string;
root_dir: FSItem;
}
// KeyValue Module
interface KeyValue {
set(key: string, value: string | number | boolean | object | any[]): Promise<boolean>;
get(key: string): Promise<any>;
del(key: string): Promise<boolean>;
incr(key: string, amount?: number): Promise<number>;
decr(key: string, amount?: number): Promise<number>;
list(pattern?: string, returnValues?: boolean): Promise<string[] | KeyValuePair[]>;
list(returnValues?: boolean): Promise<string[] | KeyValuePair[]>;
flush(): Promise<boolean>;
}
interface KeyValuePair {
key: string;
value: any;
}
// Networking Module
interface Networking {
fetch(url: string, options?: RequestInit): Promise<Response>;
Socket: typeof Socket;
tls: {
TLSSocket: typeof TLSSocket;
};
}
class Socket {
constructor(hostname: string, port: number);
write(data: ArrayBuffer | Uint8Array | string): void;
close(): void;
on(event: 'open', callback: () => void): void;
on(event: 'data', callback: (buffer: Uint8Array) => void): void;
on(event: 'error', callback: (reason: string) => void): void;
on(event: 'close', callback: (hadError: boolean) => void): void;
}
class TLSSocket extends Socket {
constructor(hostname: string, port: number);
}
// Permissions Module
interface Permissions {
grantApp(app_uid: string, permissionString: string): Promise<object>;
grantAppAnyUser(app_uid: string, permissionString: string): Promise<object>;
grantGroup(group_uid: string, permissionString: string): Promise<object>;
grantOrigin(origin: string, permissionString: string): Promise<object>;
grantUser(username: string, permissionString: string): Promise<object>;
revokeApp(app_uid: string, permissionString: string): Promise<object>;
revokeAppAnyUser(app_uid: string, permissionString: string): Promise<object>;
revokeGroup(group_uid: string, permissionString: string): Promise<object>;
revokeOrigin(origin: string, permissionString: string): Promise<object>;
revokeUser(username: string, permissionString: string): Promise<object>;
}
// UI Module
interface UI {
alert(message?: string, buttons?: AlertButton[]): Promise<string>;
prompt(message?: string, defaultValue?: string): Promise<string | null>;
authenticateWithPuter(): Promise<boolean>;
contextMenu(options: ContextMenuOptions): void;
createWindow(options?: WindowOptions): void;
exit(statusCode?: number): void;
getLanguage(): Promise<string>;
hideSpinner(): void;
launchApp(appName?: string, args?: object): Promise<AppConnection>;
launchApp(options: LaunchAppOptions): Promise<AppConnection>;
on(eventName: 'localeChanged', handler: (data: { language: string }) => void): void;
on(eventName: 'themeChanged', handler: (data: ThemeData) => void): void;
onItemsOpened(handler: (items: FSItem[]) => void): void;
onLaunchedWithItems(handler: (items: FSItem[]) => void): void;
onWindowClose(handler: () => void): void;
parentApp(): AppConnection | null;
setMenubar(options: MenubarOptions): void;
setWindowHeight(height: number): void;
setWindowPosition(x: number, y: number): void;
setWindowSize(width: number, height: number): void;
setWindowTitle(title: string): void;
setWindowWidth(width: number): void;
setWindowX(x: number): void;
setWindowY(y: number): void;
showColorPicker(defaultColor?: string): Promise<string>;
showColorPicker(options?: object): Promise<string>;
showDirectoryPicker(options?: { multiple?: boolean }): Promise<FSItem | FSItem[]>;
showFontPicker(defaultFont?: string): Promise<{ fontFamily: string }>;
showFontPicker(options?: object): Promise<{ fontFamily: string }>;
showOpenFilePicker(options?: FilePickerOptions): Promise<FSItem | FSItem[]>;
showSaveFilePicker(data?: any, defaultFileName?: string): Promise<FSItem>;
showSpinner(): void;
socialShare(url: string, message?: string, options?: { left?: number; top?: number }): void;
wasLaunchedWithItems(): boolean;
}
interface AlertButton {
label: string;
value?: string;
type?: 'primary' | 'success' | 'info' | 'warning' | 'danger';
}
interface ContextMenuOptions {
items: (ContextMenuItem | '-')[];
}
interface ContextMenuItem {
label: string;
action?: () => void;
icon?: string;
icon_active?: string;
disabled?: boolean;
items?: (ContextMenuItem | '-')[];
}
interface WindowOptions {
center?: boolean;
content?: string;
disable_parent_window?: boolean;
has_head?: boolean;
height?: number;
is_resizable?: boolean;
show_in_taskbar?: boolean;
title?: string;
width?: number;
}
interface LaunchAppOptions {
name?: string;
args?: object;
}
interface ThemeData {
palette: {
primaryHue: number;
primarySaturation: string;
primaryLightness: string;
primaryAlpha: number;
primaryColor: string;
};
}
interface MenubarOptions {
items: MenuItem[];
}
interface MenuItem {
label: string;
action?: () => void;
items?: MenuItem[];
}
interface FilePickerOptions {
multiple?: boolean;
accept?: string | string[];
}
interface AppConnection {
usesSDK: boolean;
on(eventName: 'message', handler: (message: any) => void): void;
on(eventName: 'close', handler: (data: { appInstanceID: string }) => void): void;
off(eventName: string, handler: Function): void;
postMessage(message: any): void;
close(): void;
}
// Workers Module
interface Workers {
create(workerName: string, filePath: string): Promise<WorkerDeployment>;
delete(workerName: string): Promise<boolean>;
exec(workerURL: string, options?: WorkerExecOptions): Promise<Response>;
get(workerName: string): Promise<WorkerInfo>;
list(): Promise<WorkerInfo[]>;
}
interface WorkerDeployment {
success: boolean;
url: string;
errors: any[];
}
interface WorkerExecOptions extends RequestInit {
method?: string;
headers?: object;
body?: string | object;
cache?: RequestCache;
credentials?: RequestCredentials;
mode?: RequestMode;
redirect?: RequestRedirect;
referrer?: string;
signal?: AbortSignal;
}
interface WorkerInfo {
name: string;
url: string;
file_path: string;
file_uid: string;
created_at: string;
}
// Sub-modules
ai: AI;
apps: Apps;
auth: Auth;
drivers: Drivers;
fs: FileSystem;
hosting: Hosting;
kv: KV;
net: Networking;
perms: Permissions;
ui: UI;
workers: Workers;
}
declare const puter: Puter.Puter;
// AI Module
interface AI {
chat(prompt: string, options?: ChatOptions): Promise<ChatResponse>;
chat(prompt: string, testMode?: boolean, options?: ChatOptions): Promise<ChatResponse>;
chat(prompt: string, imageURL?: string, testMode?: boolean, options?: ChatOptions): Promise<ChatResponse>;
chat(prompt: string, imageURLArray?: string[], testMode?: boolean, options?: ChatOptions): Promise<ChatResponse>;
chat(messages: ChatMessage[], testMode?: boolean, options?: ChatOptions): Promise<ChatResponse>;
img2txt(image: string | File | Blob, testMode?: boolean): Promise<string>;
txt2img(prompt: string, testMode?: boolean): Promise<HTMLImageElement>;
txt2img(prompt: string, options?: Txt2ImgOptions): Promise<HTMLImageElement>;
txt2speech(text: string): Promise<HTMLAudioElement>;
txt2speech(text: string, options?: Txt2SpeechOptions): Promise<HTMLAudioElement>;
txt2speech(text: string, language?: string): Promise<HTMLAudioElement>;
txt2speech(text: string, language?: string, voice?: string): Promise<HTMLAudioElement>;
txt2speech(text: string, language?: string, voice?: string, engine?: string): Promise<HTMLAudioElement>;
}
interface ChatOptions {
model?: string;
stream?: boolean;
max_tokens?: number;
temperature?: number;
tools?: ToolDefinition[];
}
interface ToolDefinition {
type: 'function';
function: {
name: string;
description: string;
parameters: object;
strict?: boolean;
};
}
interface ChatMessage {
role: 'system' | 'assistant' | 'user' | 'function' | 'tool';
content: string | ContentObject[];
tool_call_id?: string;
}
interface ContentObject {
type: 'text' | 'file';
text?: string;
puter_path?: string;
}
interface ChatResponse {
message: {
role: string;
content: string;
tool_calls?: ToolCall[];
};
}
interface ToolCall {
id: string;
function: {
name: string;
arguments: string;
};
}
interface Txt2ImgOptions {
model?: 'gpt-image-1' | 'gemini-2.5-flash-image-preview' | 'dall-e-3';
quality?: 'high' | 'medium' | 'low' | 'hd' | 'standard';
input_image?: string;
input_image_mime_type?: string;
}
interface Txt2SpeechOptions {
language?: string;
voice?: string;
engine?: 'standard' | 'neural' | 'generative';
}
// Apps Module
interface Apps {
create(name: string, indexURL: string): Promise<App>;
create(name: string, indexURL: string, title?: string): Promise<App>;
create(name: string, indexURL: string, title?: string, description?: string): Promise<App>;
create(options: CreateAppOptions): Promise<App>;
delete(name: string): Promise<App>;
get(name: string, options?: GetAppOptions): Promise<App>;
list(options?: ListAppOptions): Promise<App[]>;
update(name: string, attributes: UpdateAppAttributes): Promise<App>;
}
interface CreateAppOptions {
name: string;
indexURL: string;
title?: string;
description?: string;
icon?: string;
maximizeOnStart?: boolean;
filetypeAssociations?: string[];
}
interface GetAppOptions {
stats_period?: StatsPeriod;
icon_size?: null | 16 | 32 | 64 | 128 | 256 | 512;
}
interface ListAppOptions extends GetAppOptions { }
interface UpdateAppAttributes {
name?: string;
indexURL?: string;
title?: string;
description?: string;
icon?: string;
maximizeOnStart?: boolean;
filetypeAssociations?: string[];
}
type StatsPeriod = 'all' | 'today' | 'yesterday' | '7d' | '30d' | 'this_month' | 'last_month' | 'this_year' | 'last_year' | 'month_to_date' | 'year_to_date' | 'last_12_months';
interface App {
uid: string;
name: string;
icon: string;
description: string;
title: string;
maximize_on_start: boolean;
index_url: string;
created_at: string;
background: boolean;
filetype_associations: string[];
open_count: number;
user_count: number;
}
// Auth Module
interface Auth {
signIn(options?: { attempt_temp_user_creation?: boolean }): Promise<boolean>;
signOut(): void;
isSignedIn(): boolean;
getUser(): Promise<User>;
}
interface User {
uuid: string;
username: string;
email_confirmed: boolean;
}
// Drivers Module
interface Drivers {
call(interface: string, driver: string, method: string, args?: object): Promise<any>;
}
// FileSystem Module
interface FileSystem {
copy(source: string, destination: string, options?: CopyOptions): Promise<FSItem>;
delete(path: string, options?: DeleteOptions): Promise<void>;
getReadURL(path: string, expiresIn?: number): Promise<string>;
mkdir(path: string, options?: MkdirOptions): Promise<FSItem>;
move(source: string, destination: string, options?: MoveOptions): Promise<FSItem>;
read(path: string, options?: ReadOptions): Promise<Blob>;
readdir(path: string, options?: ReaddirOptions): Promise<FSItem[]>;
readdir(options?: ReaddirOptions): Promise<FSItem[]>;
rename(path: string, newName: string): Promise<FSItem>;
space(): Promise<SpaceInfo>;
stat(path: string): Promise<FSItem>;
upload(items: FileList | File[] | Blob[], dirPath?: string, options?: object): Promise<FSItem[]>;
write(path: string, data?: string | File | Blob, options?: WriteOptions): Promise<FSItem>;
}
interface CopyOptions {
overwrite?: boolean;
dedupeName?: boolean;
newName?: string;
}
interface DeleteOptions {
recursive?: boolean;
descendantsOnly?: boolean;
}
interface MkdirOptions {
overwrite?: boolean;
dedupeName?: boolean;
createMissingParents?: boolean;
}
interface MoveOptions extends CopyOptions {
createMissingParents?: boolean;
}
interface ReadOptions {
offset?: number;
byte_count?: number;
}
interface ReaddirOptions {
path?: string;
uid?: string;
}
interface WriteOptions {
overwrite?: boolean;
dedupeName?: boolean;
createMissingParents?: boolean;
}
interface SpaceInfo {
capacity: number;
used: number;
}
interface FSItem {
id: string;
uid: string;
name: string;
path: string;
is_dir: boolean;
parent_id: string;
parent_uid: string;
created: number;
modified: number;
accessed: number;
size: number | null;
writable: boolean;
read(): Promise<Blob>;
readdir(): Promise<FSItem[]>;
}
// Hosting Module
interface Hosting {
create(subdomain: string, dirPath?: string): Promise<Subdomain>;
delete(subdomain: string): Promise<boolean>;
get(subdomain: string): Promise<Subdomain>;
list(): Promise<Subdomain[]>;
update(subdomain: string, dirPath?: string): Promise<Subdomain>;
}
interface Subdomain {
uid: string;
subdomain: string;
root_dir: FSItem;
}
// Key-Value Store Module
interface KV {
set(key: string, value: string | number | boolean | object | any[]): Promise<boolean>;
get(key: string): Promise<any>;
del(key: string): Promise<boolean>;
incr(key: string, amount?: number): Promise<number>;
decr(key: string, amount?: number): Promise<number>;
list(pattern?: string, returnValues?: boolean): Promise<string[] | KVPair[]>;
list(returnValues?: boolean): Promise<string[] | KVPair[]>;
flush(): Promise<boolean>;
}
interface KVPair {
key: string;
value: any;
}
// Networking Module
interface Networking {
fetch(url: string, options?: RequestInit): Promise<Response>;
Socket: typeof Socket;
tls: {
TLSSocket: typeof TLSSocket;
};
}
declare class Socket {
constructor(hostname: string, port: number);
write(data: ArrayBuffer | Uint8Array | string): void;
close(): void;
on(event: 'open', callback: () => void): void;
on(event: 'data', callback: (buffer: Uint8Array) => void): void;
on(event: 'error', callback: (reason: string) => void): void;
on(event: 'close', callback: (hadError: boolean) => void): void;
}
declare class TLSSocket extends Socket {
constructor(hostname: string, port: number);
}
// Permissions Module
interface Permissions {
grantApp(app_uid: string, permissionString: string): Promise<object>;
grantAppAnyUser(app_uid: string, permissionString: string): Promise<object>;
grantGroup(group_uid: string, permissionString: string): Promise<object>;
grantOrigin(origin: string, permissionString: string): Promise<object>;
grantUser(username: string, permissionString: string): Promise<object>;
revokeApp(app_uid: string, permissionString: string): Promise<object>;
revokeAppAnyUser(app_uid: string, permissionString: string): Promise<object>;
revokeGroup(group_uid: string, permissionString: string): Promise<object>;
revokeOrigin(origin: string, permissionString: string): Promise<object>;
revokeUser(username: string, permissionString: string): Promise<object>;
}
// UI Module
interface UI {
alert(message?: string, buttons?: AlertButton[]): Promise<string>;
prompt(message?: string, defaultValue?: string): Promise<string | null>;
authenticateWithPuter(): Promise<boolean>;
contextMenu(options: ContextMenuOptions): void;
createWindow(options?: WindowOptions): void;
exit(statusCode?: number): void;
getLanguage(): Promise<string>;
hideSpinner(): void;
launchApp(appName?: string, args?: object): Promise<AppConnection>;
launchApp(options: LaunchAppOptions): Promise<AppConnection>;
on(eventName: 'localeChanged', handler: (data: { language: string }) => void): void;
on(eventName: 'themeChanged', handler: (data: ThemeData) => void): void;
onItemsOpened(handler: (items: FSItem[]) => void): void;
onLaunchedWithItems(handler: (items: FSItem[]) => void): void;
onWindowClose(handler: () => void): void;
parentApp(): AppConnection | null;
setMenubar(options: MenubarOptions): void;
setWindowHeight(height: number): void;
setWindowPosition(x: number, y: number): void;
setWindowSize(width: number, height: number): void;
setWindowTitle(title: string): void;
setWindowWidth(width: number): void;
setWindowX(x: number): void;
setWindowY(y: number): void;
showColorPicker(defaultColor?: string): Promise<string>;
showColorPicker(options?: object): Promise<string>;
showDirectoryPicker(options?: { multiple?: boolean }): Promise<FSItem | FSItem[]>;
showFontPicker(defaultFont?: string): Promise<{ fontFamily: string }>;
showFontPicker(options?: object): Promise<{ fontFamily: string }>;
showOpenFilePicker(options?: FilePickerOptions): Promise<FSItem | FSItem[]>;
showSaveFilePicker(data?: any, defaultFileName?: string): Promise<FSItem>;
showSpinner(): void;
socialShare(url: string, message?: string, options?: { left?: number; top?: number }): void;
wasLaunchedWithItems(): boolean;
}
interface AlertButton {
label: string;
value?: string;
type?: 'primary' | 'success' | 'info' | 'warning' | 'danger';
}
interface ContextMenuOptions {
items: (ContextMenuItem | '-')[];
}
interface ContextMenuItem {
label: string;
action?: () => void;
icon?: string;
icon_active?: string;
disabled?: boolean;
items?: (ContextMenuItem | '-')[];
}
interface WindowOptions {
center?: boolean;
content?: string;
disable_parent_window?: boolean;
has_head?: boolean;
height?: number;
is_resizable?: boolean;
show_in_taskbar?: boolean;
title?: string;
width?: number;
}
interface LaunchAppOptions {
name?: string;
args?: object;
}
interface ThemeData {
palette: {
primaryHue: number;
primarySaturation: string;
primaryLightness: string;
primaryAlpha: number;
primaryColor: string;
};
}
interface MenubarOptions {
items: MenuItem[];
}
interface MenuItem {
label: string;
action?: () => void;
items?: MenuItem[];
}
interface FilePickerOptions {
multiple?: boolean;
accept?: string | string[];
}
interface AppConnection {
usesSDK: boolean;
on(eventName: 'message', handler: (message: any) => void): void;
on(eventName: 'close', handler: (data: { appInstanceID: string }) => void): void;
off(eventName: string, handler: Function): void;
postMessage(message: any): void;
close(): void;
}
// Workers Module
interface Workers {
create(workerName: string, filePath: string): Promise<WorkerDeployment>;
delete(workerName: string): Promise<boolean>;
exec(workerURL: string, options?: WorkerExecOptions): Promise<Response>;
get(workerName: string): Promise<WorkerInfo>;
list(): Promise<WorkerInfo[]>;
}
interface WorkerDeployment {
success: boolean;
url: string;
errors: any[];
}
interface WorkerExecOptions extends RequestInit {
method?: string;
headers?: object;
body?: string | object;
cache?: RequestCache;
credentials?: RequestCredentials;
mode?: RequestMode;
redirect?: RequestRedirect;
referrer?: string;
signal?: AbortSignal;
}
interface WorkerInfo {
name: string;
url: string;
file_path: string;
file_uid: string;
created_at: string;
}
// Global puter instance
declare const puter: Puter;
// Export the Puter class as both default and named export
export default puter;
export { puter };
// Also export all the interfaces for users who want to use them
export {
AI, AlertButton, App, AppConnection, Apps,
Auth, ChatMessage, ChatOptions, ChatResponse, ContentObject, ContextMenuItem, ContextMenuOptions, CopyOptions, CreateAppOptions, DeleteOptions, Drivers, FilePickerOptions, FileSystem, FSItem, GetAppOptions, Hosting,
KV,
KVPair, LaunchAppOptions, MenubarOptions,
MenuItem, MkdirOptions,
MoveOptions, Networking,
Permissions, Puter, ReaddirOptions, ReadOptions, SpaceInfo, StatsPeriod, Subdomain, ThemeData, ToolCall, ToolDefinition, Txt2ImgOptions,
Txt2SpeechOptions, UI, UpdateAppAttributes, User, WindowOptions, WorkerDeployment,
WorkerExecOptions,
WorkerInfo, Workers, WriteOptions
};
export = Puter;

View File

@@ -1,15 +1,16 @@
{
"name": "@heyputer/puter.js",
"version": "2.0.1",
"version": "2.0.9",
"description": "Puter.js - A JavaScript library for interacting with Puter services.",
"main": "src/index.js",
"types": "index.d.ts",
"typings": "index.d.ts",
"type": "module",
"files": [
"src/index.js",
"dist/puter.js",
"src/",
"index.d.ts"
],
"type": "module",
"publishConfig": {
"registry": "https://registry.npmjs.org/"
},

View File

@@ -1,9 +1,10 @@
import putility from '@heyputer/putility';
import kvjs from '@heyputer/kv.js';
import APICallLogger from './lib/APICallLogger.js';
import path from './lib/path.js';
import localStorageMemory from './lib/polyfills/localStorage.js'
import xhrshim from './lib/polyfills/xhrshim.js'
import localStorageMemory from './lib/polyfills/localStorage.js';
import xhrshim from './lib/polyfills/xhrshim.js';
import * as utils from './lib/utils.js';
import AI from './modules/AI.js';
import Apps from './modules/Apps.js';
@@ -15,7 +16,7 @@ import FSItem from './modules/FSItem.js';
import Hosting from './modules/Hosting.js';
import KV from './modules/KV.js';
import { PSocket } from './modules/networking/PSocket.js';
import { PTLSSocket } from "./modules/networking/PTLS.js"
import { PTLSSocket } from './modules/networking/PTLS.js';
import { pFetch } from './modules/networking/requests.js';
import OS from './modules/OS.js';
import Perms from './modules/Perms.js';
@@ -28,14 +29,13 @@ import { FilesystemService } from './services/Filesystem.js';
import { FSRelayService } from './services/FSRelay.js';
import { NoPuterYetService } from './services/NoPuterYet.js';
import { XDIncomingService } from './services/XDIncoming.js';
import kvjs from '@heyputer/kv.js';
// TODO: This is for a safe-guard below; we should check if we can
// generalize this behavior rather than hard-coding it.
// (using defaultGUIOrigin breaks locally-hosted apps)
const PROD_ORIGIN = 'https://puter.com';
export default globalThis.puter = (function() {
const puterInit = (function() {
'use strict';
class Puter{
@@ -58,7 +58,7 @@ export default globalThis.puter = (function() {
puterAuthState = {
isPromptOpen: false,
authGranted: null,
resolver: null
resolver: null,
};
// Holds the unique app instance ID that is provided by the host environment
@@ -78,12 +78,12 @@ export default globalThis.puter = (function() {
/**
* Puter.js Modules
*
*
* These are the modules you see on docs.puter.com; for example:
* - puter.fs
* - puter.kv
* - puter.ui
*
*
* initSubmodules is called from the constructor of this class.
*/
initSubmodules = function(){
@@ -108,14 +108,13 @@ export default globalThis.puter = (function() {
// Path
this.path = path;
}
};
// --------------------------------------------
// Constructor
// --------------------------------------------
constructor(options) {
options = options ?? {};
constructor() {
// Initialize the cache using kv.js
this._cache = new kvjs();
@@ -131,57 +130,57 @@ export default globalThis.puter = (function() {
this.context = context;
context.services = this.services;
// Holds the query parameters found in the current URL
let URLParams = new URLSearchParams(globalThis.location?.search);
// Figure out the environment in which the SDK is running
if (URLParams.has('puter.app_instance_id')) {
if ( URLParams.has('puter.app_instance_id') ) {
this.env = 'app';
} else if(globalThis.puter_gui_enabled === true)
} else if ( globalThis.puter_gui_enabled === true )
{
this.env = 'gui';
else if (globalThis.WorkerGlobalScope) {
if (globalThis.ServiceWorkerGlobalScope) {
this.env = 'service-worker'
if (!globalThis.XMLHttpRequest) {
globalThis.XMLHttpRequest = xhrshim
}
else if ( globalThis.WorkerGlobalScope ) {
if ( globalThis.ServiceWorkerGlobalScope ) {
this.env = 'service-worker';
if ( !globalThis.XMLHttpRequest ) {
globalThis.XMLHttpRequest = xhrshim;
}
if (!globalThis.location) {
globalThis.location = new URL("https://puter.site/");
if ( !globalThis.location ) {
globalThis.location = new URL('https://puter.site/');
}
// XHRShimGlobalize here
} else {
this.env = 'web-worker'
this.env = 'web-worker';
}
if (!globalThis.localStorage) {
if ( !globalThis.localStorage ) {
globalThis.localStorage = localStorageMemory;
}
} else if (globalThis.process) {
} else if ( globalThis.process ) {
this.env = 'nodejs';
if (!globalThis.localStorage) {
if ( !globalThis.localStorage ) {
globalThis.localStorage = localStorageMemory;
}
if (!globalThis.XMLHttpRequest) {
globalThis.XMLHttpRequest = xhrshim
if ( !globalThis.XMLHttpRequest ) {
globalThis.XMLHttpRequest = xhrshim;
}
if (!globalThis.location) {
globalThis.location = new URL("https://nodejs.puter.site/");
if ( !globalThis.location ) {
globalThis.location = new URL('https://nodejs.puter.site/');
}
if (!globalThis.addEventListener) {
globalThis.addEventListener = () => {} // API Stub
if ( !globalThis.addEventListener ) {
globalThis.addEventListener = () => {
}; // API Stub
}
} else {
this.env = 'web';
}
// There are some specific situations where puter is definitely loaded in GUI mode
// we're going to check for those situations here so that we don't break anything unintentionally
// if navigator URL's hostname is 'puter.com'
if(this.env !== 'gui'){
if ( this.env !== 'gui' ){
// Retrieve the hostname from the URL: Remove the trailing dot if it exists. This is to handle the case where the URL is, for example, `https://puter.com.` (note the trailing dot).
// This is necessary because the trailing dot can cause the hostname to not match the expected value.
// This is necessary because the trailing dot can cause the hostname to not match the expected value.
let hostname = location.hostname.replace(/\.$/, '');
// Create a new URL object with the URL string
@@ -191,45 +190,45 @@ export default globalThis.puter = (function() {
const gui_hostname = url.hostname;
// If the hostname matches the GUI hostname, then the SDK is running in the GUI environment
if(hostname === gui_hostname){
if ( hostname === gui_hostname ){
this.env = 'gui';
}
}
// Get the 'args' from the URL. This is used to pass arguments to the app.
if(URLParams.has('puter.args')){
if ( URLParams.has('puter.args') ){
this.args = JSON.parse(decodeURIComponent(URLParams.get('puter.args')));
}else{
} else {
this.args = {};
}
// Try to extract appInstanceID from the URL. appInstanceID is included in every messaage
// sent to the host environment. This is used to help host environment identify the app
// instance that sent the message and communicate back to it.
if(URLParams.has('puter.app_instance_id')){
if ( URLParams.has('puter.app_instance_id') ){
this.appInstanceID = decodeURIComponent(URLParams.get('puter.app_instance_id'));
}
// Try to extract parentInstanceID from the URL. If another app launched this app instance, parentInstanceID
// holds its instance ID, and is used to communicate with that parent app.
if(URLParams.has('puter.parent_instance_id')){
if ( URLParams.has('puter.parent_instance_id') ){
this.parentInstanceID = decodeURIComponent(URLParams.get('puter.parent_instance_id'));
}
// Try to extract `puter.app.id` from the URL. `puter.app.id` is the unique ID of the app.
// App ID is useful for identifying the app when communicating with the Puter API, among other things.
if(URLParams.has('puter.app.id')){
if ( URLParams.has('puter.app.id') ){
this.appID = decodeURIComponent(URLParams.get('puter.app.id'));
}
// Extract app name (added later)
if(URLParams.has('puter.app.name')){
if ( URLParams.has('puter.app.name') ){
this.appName = decodeURIComponent(URLParams.get('puter.app.name'));
}
// Construct this App's AppData path based on the appID. AppData path is used to store files that are specific to this app.
// The default AppData path is `~/AppData/<appID>`.
if(this.appID){
if ( this.appID ){
this.appDataPath = `~/AppData/${this.appID}`;
}
@@ -239,9 +238,9 @@ export default globalThis.puter = (function() {
// is constructed as `https://api.<puter.domain>`.
// This should only be done when the SDK is running in 'app' mode.
this.APIOrigin = this.defaultAPIOrigin;
if(URLParams.has('puter.api_origin') && this.env === 'app'){
if ( URLParams.has('puter.api_origin') && this.env === 'app' ){
this.APIOrigin = decodeURIComponent(URLParams.get('puter.api_origin'));
}else if(URLParams.has('puter.domain') && this.env === 'app'){
} else if ( URLParams.has('puter.domain') && this.env === 'app' ){
this.APIOrigin = 'https://api.' + URLParams.get('puter.domain');
}
@@ -251,10 +250,9 @@ export default globalThis.puter = (function() {
let logger = new putility.libs.log.ConsoleLogger();
// logs can be toggled based on categories
logger = new putility.libs.log.CategorizedToggleLogger(
{ delegate: logger });
logger = new putility.libs.log.CategorizedToggleLogger({ delegate: logger });
const cat_logger = logger;
// create facade for easy logging
this.logger = new putility.libs.log.LoggerFacade({
impl: logger,
@@ -263,7 +261,7 @@ export default globalThis.puter = (function() {
// Initialize API call logger
this.apiCallLogger = new APICallLogger({
enabled: false // Disabled by default
enabled: false, // Disabled by default
});
// === START :: Services === //
@@ -285,17 +283,16 @@ export default globalThis.puter = (function() {
svc_apiAccess.auth_token = this.authToken;
svc_apiAccess.api_origin = this.APIOrigin;
[
['authToken','auth_token'],
['APIOrigin','api_origin'],
].forEach(([k1,k2]) => {
['authToken', 'auth_token'],
['APIOrigin', 'api_origin'],
].forEach(([k1, k2]) => {
Object.defineProperty(this, k1, {
get () {
get() {
return svc_apiAccess[k2];
},
set (v) {
set(v) {
svc_apiAccess[k2] = v;
return true;
}
},
});
});
})();
@@ -303,27 +300,27 @@ export default globalThis.puter = (function() {
// === Start :: Modules === //
// The SDK is running in the Puter GUI (i.e. 'gui')
if(this.env === 'gui'){
if ( this.env === 'gui' ){
this.authToken = window.auth_token;
// initialize submodules
this.initSubmodules();
}
// Loaded in an iframe in the Puter GUI (i.e. 'app')
// When SDK is loaded in App mode the initiation process should start when the DOM is ready
else if (this.env === 'app') {
else if ( this.env === 'app' ) {
this.authToken = decodeURIComponent(URLParams.get('puter.auth.token'));
// initialize submodules
this.initSubmodules();
// If the authToken is already set in localStorage, then we don't need to show the dialog
try {
if(localStorage.getItem('puter.auth.token')){
if ( localStorage.getItem('puter.auth.token') ){
this.setAuthToken(localStorage.getItem('puter.auth.token'));
}
// if appID is already set in localStorage, then we don't need to show the dialog
if(localStorage.getItem('puter.app.id')){
if ( localStorage.getItem('puter.app.id') ){
this.setAppID(localStorage.getItem('puter.app.id'));
}
} catch (error) {
} catch( error ) {
// Handle the error here
console.error('Error accessing localStorage:', error);
}
@@ -331,23 +328,23 @@ export default globalThis.puter = (function() {
// SDK was loaded in a 3rd-party website.
// When SDK is loaded in GUI the initiation process should start when the DOM is ready. This is because
// the SDK needs to show a dialog to the user to ask for permission to access their Puter account.
else if(this.env === 'web') {
else if ( this.env === 'web' ) {
// initialize submodules
this.initSubmodules();
try{
try {
// If the authToken is already set in localStorage, then we don't need to show the dialog
if(localStorage.getItem('puter.auth.token')){
if ( localStorage.getItem('puter.auth.token') ){
this.setAuthToken(localStorage.getItem('puter.auth.token'));
}
// if appID is already set in localStorage, then we don't need to show the dialog
if(localStorage.getItem('puter.app.id')){
if ( localStorage.getItem('puter.app.id') ){
this.setAppID(localStorage.getItem('puter.app.id'));
}
} catch (error) {
} catch( error ) {
// Handle the error here
console.error('Error accessing localStorage:', error);
}
} else if (this.env === 'web-worker' || this.env === 'service-worker' || this.env === 'nodejs') {
} else if ( this.env === 'web-worker' || this.env === 'service-worker' || this.env === 'nodejs' ) {
this.initSubmodules();
}
@@ -364,7 +361,7 @@ export default globalThis.puter = (function() {
this.logger.impl = logger;
})();
// Lock to prevent multiple requests to `/rao`
this.lock_rao_ = new putility.libs.promise.Lock();
// Promise that resolves when it's okay to request `/rao`
@@ -388,14 +385,14 @@ export default globalThis.puter = (function() {
},
body: JSON.stringify({}),
})).json());
return `${wispServer}/${wispToken}/`
return `${wispServer}/${wispToken}/`;
},
Socket: PSocket,
tls: {
TLSSocket: PTLSSocket
TLSSocket: PTLSSocket,
},
fetch: pFetch
}
fetch: pFetch,
};
this.workers = new WorkersHandler(this.authToken);
@@ -408,13 +405,13 @@ export default globalThis.puter = (function() {
* Makes a request to `/rao`. This method aquires a lock to prevent
* multiple requests, and is effectively idempotent.
*/
async request_rao_ () {
async request_rao_() {
await this.p_can_request_rao_;
if ( this.env === 'gui' ) {
return;
}
// setAuthToken is called more than once when auth completes, which
// causes multiple requests to /rao. This lock prevents that.
await this.lock_rao_.acquire();
@@ -429,11 +426,11 @@ export default globalThis.puter = (function() {
method: 'POST',
headers: {
Authorization: `Bearer ${this.authToken}`,
Origin: location.origin // This is ignored in the browser but needed for workers and nodejs
}
Origin: location.origin, // This is ignored in the browser but needed for workers and nodejs
},
});
return await resp.json();
} catch (e) {
} catch( e ) {
had_error = true;
console.error(e);
} finally {
@@ -443,8 +440,8 @@ export default globalThis.puter = (function() {
this.rao_requested_ = true;
}
}
registerModule (name, cls, parameters = {}) {
registerModule(name, cls, parameters = {}) {
const instance = new cls(this.context, parameters);
this.modules_.push(name);
this[name] = instance;
@@ -460,24 +457,24 @@ export default globalThis.puter = (function() {
}
}
setAppID = function (appID) {
setAppID = function(appID) {
// save to localStorage
try{
try {
localStorage.setItem('puter.app.id', appID);
} catch (error) {
} catch( error ) {
// Handle the error here
console.error('Error accessing localStorage:', error);
}
this.appID = appID;
}
};
setAuthToken = function (authToken) {
setAuthToken = function(authToken) {
this.authToken = authToken;
// If the SDK is running on a 3rd-party site or an app, then save the authToken in localStorage
if(this.env === 'web' || this.env === 'app'){
try{
if ( this.env === 'web' || this.env === 'app' ){
try {
localStorage.setItem('puter.auth.token', authToken);
} catch (error) {
} catch( error ) {
// Handle the error here
console.error('Error accessing localStorage:', error);
}
@@ -486,41 +483,41 @@ export default globalThis.puter = (function() {
this.updateSubmodules();
// rao
this.request_rao_();
}
};
setAPIOrigin = function (APIOrigin) {
setAPIOrigin = function(APIOrigin) {
this.APIOrigin = APIOrigin;
// reinitialize submodules
this.updateSubmodules();
}
};
resetAuthToken = function () {
resetAuthToken = function() {
this.authToken = null;
// If the SDK is running on a 3rd-party site or an app, then save the authToken in localStorage
if(this.env === 'web' || this.env === 'app'){
try{
if ( this.env === 'web' || this.env === 'app' ){
try {
localStorage.removeItem('puter.auth.token');
} catch (error) {
} catch( error ) {
// Handle the error here
console.error('Error accessing localStorage:', error);
}
}
// reinitialize submodules
this.updateSubmodules();
}
};
exit = function(statusCode = 0) {
if (statusCode && (typeof statusCode !== 'number')) {
if ( statusCode && (typeof statusCode !== 'number') ) {
console.warn('puter.exit() requires status code to be a number. Treating it as 1');
statusCode = 1;
}
globalThis.parent.postMessage({
msg: "exit",
msg: 'exit',
appInstanceID: this.appInstanceID,
statusCode,
}, '*');
}
};
/**
* A function that generates a domain-safe name by combining a random adjective, a random noun, and a random number (between 0 and 9999).
@@ -532,29 +529,29 @@ export default globalThis.puter = (function() {
*
*/
randName = function(separateWith = '-'){
const first_adj = ['helpful','sensible', 'loyal', 'honest', 'clever', 'capable','calm', 'smart', 'genius', 'bright', 'charming', 'creative', 'diligent', 'elegant', 'fancy',
'colorful', 'avid', 'active', 'gentle', 'happy', 'intelligent', 'jolly', 'kind', 'lively', 'merry', 'nice', 'optimistic', 'polite',
'quiet', 'relaxed', 'silly', 'victorious', 'witty', 'young', 'zealous', 'strong', 'brave', 'agile', 'bold'];
const first_adj = ['helpful', 'sensible', 'loyal', 'honest', 'clever', 'capable', 'calm', 'smart', 'genius', 'bright', 'charming', 'creative', 'diligent', 'elegant', 'fancy',
'colorful', 'avid', 'active', 'gentle', 'happy', 'intelligent', 'jolly', 'kind', 'lively', 'merry', 'nice', 'optimistic', 'polite',
'quiet', 'relaxed', 'silly', 'victorious', 'witty', 'young', 'zealous', 'strong', 'brave', 'agile', 'bold'];
const nouns = ['street', 'roof', 'floor', 'tv', 'idea', 'morning', 'game', 'wheel', 'shoe', 'bag', 'clock', 'pencil', 'pen',
'magnet', 'chair', 'table', 'house', 'dog', 'room', 'book', 'car', 'cat', 'tree',
'flower', 'bird', 'fish', 'sun', 'moon', 'star', 'cloud', 'rain', 'snow', 'wind', 'mountain',
'river', 'lake', 'sea', 'ocean', 'island', 'bridge', 'road', 'train', 'plane', 'ship', 'bicycle',
'horse', 'elephant', 'lion', 'tiger', 'bear', 'zebra', 'giraffe', 'monkey', 'snake', 'rabbit', 'duck',
'goose', 'penguin', 'frog', 'crab', 'shrimp', 'whale', 'octopus', 'spider', 'ant', 'bee', 'butterfly', 'dragonfly',
'ladybug', 'snail', 'camel', 'kangaroo', 'koala', 'panda', 'piglet', 'sheep', 'wolf', 'fox', 'deer', 'mouse', 'seal',
'chicken', 'cow', 'dinosaur', 'puppy', 'kitten', 'circle', 'square', 'garden', 'otter', 'bunny', 'meerkat', 'harp']
const nouns = ['street', 'roof', 'floor', 'tv', 'idea', 'morning', 'game', 'wheel', 'shoe', 'bag', 'clock', 'pencil', 'pen',
'magnet', 'chair', 'table', 'house', 'dog', 'room', 'book', 'car', 'cat', 'tree',
'flower', 'bird', 'fish', 'sun', 'moon', 'star', 'cloud', 'rain', 'snow', 'wind', 'mountain',
'river', 'lake', 'sea', 'ocean', 'island', 'bridge', 'road', 'train', 'plane', 'ship', 'bicycle',
'horse', 'elephant', 'lion', 'tiger', 'bear', 'zebra', 'giraffe', 'monkey', 'snake', 'rabbit', 'duck',
'goose', 'penguin', 'frog', 'crab', 'shrimp', 'whale', 'octopus', 'spider', 'ant', 'bee', 'butterfly', 'dragonfly',
'ladybug', 'snail', 'camel', 'kangaroo', 'koala', 'panda', 'piglet', 'sheep', 'wolf', 'fox', 'deer', 'mouse', 'seal',
'chicken', 'cow', 'dinosaur', 'puppy', 'kitten', 'circle', 'square', 'garden', 'otter', 'bunny', 'meerkat', 'harp'];
// return a random combination of first_adj + noun + number (between 0 and 9999)
// e.g. clever-idea-123
return first_adj[Math.floor(Math.random() * first_adj.length)] + separateWith + nouns[Math.floor(Math.random() * nouns.length)] + separateWith + Math.floor(Math.random() * 10000);
}
};
getUser = function(...args){
let options;
// If first argument is an object, it's the options
if (typeof args[0] === 'object' && args[0] !== null) {
if ( typeof args[0] === 'object' && args[0] !== null ) {
options = args[0];
} else {
// Otherwise, we assume separate arguments are provided
@@ -563,43 +560,43 @@ export default globalThis.puter = (function() {
error: args[1],
};
}
return new Promise((resolve, reject) => {
const xhr = utils.initXhr('/whoami', this.APIOrigin, this.authToken, 'get');
// set up event handlers for load and error events
utils.setupXhrEventHandlers(xhr, options.success, options.error, resolve, reject);
xhr.send();
})
}
});
};
print = function(...args){
// Check if the last argument is an options object with escapeHTML or code property
let options = {};
if(args.length > 0 && typeof args[args.length - 1] === 'object' && args[args.length - 1] !== null &&
('escapeHTML' in args[args.length - 1] || 'code' in args[args.length - 1])) {
if ( args.length > 0 && typeof args[args.length - 1] === 'object' && args[args.length - 1] !== null &&
('escapeHTML' in args[args.length - 1] || 'code' in args[args.length - 1]) ) {
options = args.pop();
}
for(let arg of args){
for ( let arg of args ){
// Escape HTML if the option is set to true or if code option is true
if((options.escapeHTML === true || options.code === true) && typeof arg === 'string') {
if ( (options.escapeHTML === true || options.code === true) && typeof arg === 'string' ) {
arg = arg.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
}
// Wrap in code/pre tags if code option is true
if(options.code === true) {
if ( options.code === true ) {
arg = `<code><pre>${arg}</pre></code>`;
}
document.body.innerHTML += arg;
}
}
};
/**
* Configures API call logging settings
* @param {Object} config - Configuration options for API call logging
@@ -607,32 +604,32 @@ export default globalThis.puter = (function() {
* @param {boolean} config.enabled - Enable/disable API call logging
*/
configureAPILogging = function(config = {}){
if (this.apiCallLogger) {
if ( this.apiCallLogger ) {
this.apiCallLogger.updateConfig(config);
}
return this;
}
};
/**
* Enables API call logging with optional configuration
* @param {Object} config - Optional configuration to apply when enabling
*/
enableAPILogging = function(config = {}) {
if (this.apiCallLogger) {
if ( this.apiCallLogger ) {
this.apiCallLogger.updateConfig({ ...config, enabled: true });
}
return this;
}
};
/**
* Disables API call logging
*/
disableAPILogging = function() {
if (this.apiCallLogger) {
if ( this.apiCallLogger ) {
this.apiCallLogger.disable();
}
return this;
}
};
/**
* Initializes network connectivity monitoring to purge cache when connection is lost
@@ -640,8 +637,8 @@ export default globalThis.puter = (function() {
*/
initNetworkMonitoring = function() {
// Only initialize in environments that support navigator.onLine and window events
if (typeof globalThis.navigator === 'undefined' ||
typeof globalThis.addEventListener !== 'function') {
if ( typeof globalThis.navigator === 'undefined' ||
typeof globalThis.addEventListener !== 'function' ) {
return;
}
@@ -651,13 +648,13 @@ export default globalThis.puter = (function() {
// Function to handle network state changes
const handleNetworkChange = () => {
const isOnline = navigator.onLine;
// If we went from online to offline, purge the cache
if (wasOnline && !isOnline) {
if ( wasOnline && !isOnline ) {
console.log('Network connection lost - purging cache');
this.purgeCache();
}
// Update the previous state
wasOnline = isOnline;
};
@@ -668,13 +665,13 @@ export default globalThis.puter = (function() {
// Also listen for visibility change as an additional indicator
// (some browsers don't fire offline events reliably)
if (typeof document !== 'undefined') {
if ( typeof document !== 'undefined' ) {
document.addEventListener('visibilitychange', () => {
// Small delay to allow network state to update
setTimeout(handleNetworkChange, 100);
});
}
}
};
/**
* Purges all cached data
@@ -682,17 +679,17 @@ export default globalThis.puter = (function() {
*/
purgeCache = function() {
try {
if (this._cache && typeof this._cache.flushall === 'function') {
if ( this._cache && typeof this._cache.flushall === 'function' ) {
this._cache.flushall();
console.log('Cache purged successfully');
} else {
console.warn('Cache purge failed: cache instance not available');
}
} catch (error) {
} catch( error ) {
console.error('Error purging cache:', error);
}
}
};
}
// Create a new Puter object and return it
@@ -700,18 +697,22 @@ export default globalThis.puter = (function() {
// Return the Puter object
return puterobj;
}());
});
globalThis.addEventListener('message', async (event) => {
export const puter = puterInit();
export default puter;
globalThis.puter = puter;
globalThis.addEventListener && globalThis.addEventListener('message', async (event) => {
// if the message is not from Puter, then ignore it
if(event.origin !== puter.defaultGUIOrigin) return;
if ( event.origin !== puter.defaultGUIOrigin ) return;
if(event.data.msg && event.data.msg === 'requestOrigin'){
if ( event.data.msg && event.data.msg === 'requestOrigin' ){
event.source.postMessage({
msg: "originResponse",
}, '*');
msg: 'originResponse',
}, '*');
}
else if (event.data.msg === 'puter.token') {
else if ( event.data.msg === 'puter.token' ) {
// puterDialog.close();
// Set the authToken property
puter.setAuthToken(event.data.token);
@@ -725,16 +726,16 @@ globalThis.addEventListener('message', async (event) => {
// resolve();
// Call onAuth callback
if(puter.onAuth && typeof puter.onAuth === 'function'){
if ( puter.onAuth && typeof puter.onAuth === 'function' ){
puter.getUser().then((user) => {
puter.onAuth(user)
puter.onAuth(user);
});
}
puter.puterAuthState.isPromptOpen = false;
// Resolve or reject any waiting promises.
if (puter.puterAuthState.resolver) {
if (puter.puterAuthState.authGranted) {
if ( puter.puterAuthState.resolver ) {
if ( puter.puterAuthState.authGranted ) {
puter.puterAuthState.resolver.resolve();
} else {
puter.puterAuthState.resolver.reject();
@@ -742,4 +743,4 @@ globalThis.addEventListener('message', async (event) => {
puter.puterAuthState.resolver = null;
};
}
})
});

View File

@@ -1,23 +1,23 @@
import io from '../../lib/socket.io/socket.io.esm.min.js';
// Operations
import space from "./operations/space.js";
import mkdir from "./operations/mkdir.js";
import copy from "./operations/copy.js";
import rename from "./operations/rename.js";
import upload from "./operations/upload.js";
import read from "./operations/read.js";
import move from "./operations/move.js";
import write from "./operations/write.js";
import sign from "./operations/sign.js";
import symlink from './operations/symlink.js';
import copy from './operations/copy.js';
import mkdir from './operations/mkdir.js';
import move from './operations/move.js';
import read from './operations/read.js';
import readdir from './operations/readdir.js';
import rename from './operations/rename.js';
import sign from './operations/sign.js';
import space from './operations/space.js';
import stat from './operations/stat.js';
// Why is this called deleteFSEntry instead of just delete? because delete is
import symlink from './operations/symlink.js';
import upload from './operations/upload.js';
import write from './operations/write.js';
// Why is this called deleteFSEntry instead of just delete? because delete is
// a reserved keyword in javascript
import deleteFSEntry from "./operations/deleteFSEntry.js";
import { AdvancedBase } from '../../../../putility/index.js';
import FSItem from '../FSItem.js';
import deleteFSEntry from './operations/deleteFSEntry.js';
import getReadURL from './operations/getReadUrl.js';
export class PuterJSFileSystemModule extends AdvancedBase {
@@ -39,7 +39,7 @@ export class PuterJSFileSystemModule extends AdvancedBase {
readdir = readdir;
stat = stat;
FSItem = FSItem
FSItem = FSItem;
static NARI_METHODS = {
// stat: {
@@ -50,7 +50,7 @@ export class PuterJSFileSystemModule extends AdvancedBase {
// return svc_fs.filesystem.stat(parameters);
// }
// },
}
};
/**
* Creates a new instance with the given authentication token, API origin, and app ID,
@@ -61,7 +61,7 @@ export class PuterJSFileSystemModule extends AdvancedBase {
* @param {string} APIOrigin - Origin of the API server. Used to build the API endpoint URLs.
* @param {string} appID - ID of the app to use.
*/
constructor (context) {
constructor(context) {
super();
this.authToken = context.authToken;
this.APIOrigin = context.APIOrigin;
@@ -81,7 +81,6 @@ export class PuterJSFileSystemModule extends AdvancedBase {
});
}
/**
* Initializes the socket connection to the server using the current API origin.
* If a socket connection already exists, it disconnects it before creating a new one.
@@ -92,17 +91,19 @@ export class PuterJSFileSystemModule extends AdvancedBase {
* @returns {void}
*/
initializeSocket() {
if (this.socket) {
if ( this.socket ) {
this.socket.disconnect();
}
this.socket = io(this.APIOrigin, {
auth: {
auth_token: this.authToken,
}
},
autoUnref: this.context.env === 'nodejs',
});
this.bindSocketEvents();
}
bindSocketEvents() {
@@ -117,16 +118,20 @@ export class PuterJSFileSystemModule extends AdvancedBase {
this.socket.on('item.moved', (item) => {
// todo: NAIVE PURGE
puter._cache.flushall();
});
});
this.socket.on('connect', () => {
if(puter.debugMode)
if ( puter.debugMode )
{
console.log('FileSystem Socket: Connected', this.socket.id);
}
});
this.socket.on('disconnect', () => {
if(puter.debugMode)
if ( puter.debugMode )
{
console.log('FileSystem Socket: Disconnected');
}
// todo: NAIVE PURGE
// purge cache on disconnect since we may have become out of sync
@@ -134,28 +139,38 @@ export class PuterJSFileSystemModule extends AdvancedBase {
});
this.socket.on('reconnect', (attempt) => {
if(puter.debugMode)
if ( puter.debugMode )
{
console.log('FileSystem Socket: Reconnected', this.socket.id);
}
});
this.socket.on('reconnect_attempt', (attempt) => {
if(puter.debugMode)
if ( puter.debugMode )
{
console.log('FileSystem Socket: Reconnection Attemps', attempt);
}
});
this.socket.on('reconnect_error', (error) => {
if(puter.debugMode)
if ( puter.debugMode )
{
console.log('FileSystem Socket: Reconnection Error', error);
}
});
this.socket.on('reconnect_failed', () => {
if(puter.debugMode)
if ( puter.debugMode )
{
console.log('FileSystem Socket: Reconnection Failed');
}
});
this.socket.on('error', (error) => {
if(puter.debugMode)
if ( puter.debugMode )
{
console.error('FileSystem Socket Error:', error);
}
});
}
@@ -166,7 +181,7 @@ export class PuterJSFileSystemModule extends AdvancedBase {
* @memberof [FileSystem]
* @returns {void}
*/
setAuthToken (authToken) {
setAuthToken(authToken) {
this.authToken = authToken;
// reset socket
this.initializeSocket();
@@ -174,12 +189,12 @@ export class PuterJSFileSystemModule extends AdvancedBase {
/**
* Sets the API origin and resets the socket connection with the updated API origin.
*
*
* @param {string} APIOrigin - The new API origin.
* @memberof [Apps]
* @returns {void}
*/
setAPIOrigin (APIOrigin) {
setAPIOrigin(APIOrigin) {
this.APIOrigin = APIOrigin;
// reset socket
this.initializeSocket();

View File

@@ -0,0 +1,28 @@
const { readFileSync } = require('node:fs');
const vm = require('node:vm');
const { resolve } = require('node:path');
/**
* Method for loading puter.js in Node.js environment
* @param {string} authToken - Optional auth token to initialize puter with
* @returns {Promise<import('../index.d.ts').puter>} The `puter` object from puter.js
*/
const safeLoadPuterJs = (authToken) => {
const goodContext = {};
Object.getOwnPropertyNames(globalThis).forEach(name => {
try {
goodContext[name] = globalThis[name];
} catch {
// silent fail
}
});
goodContext.globalThis = goodContext;
const code = readFileSync(`${resolve(__filename, '..')}/../dist/puter.js`, 'utf8');
const context = vm.createContext(goodContext);
vm.runInNewContext(code, context);
if ( authToken ) {
goodContext.puter.setAuthToken(authToken);
}
return goodContext.puter;
};
module.exports = { safeLoadPuterJs };

View File

@@ -1,13 +1,13 @@
import putility from "@heyputer/putility";
import { PuterAPIFilesystem } from "../lib/filesystem/APIFS.js";
import { CachedFilesystem } from "../lib/filesystem/CacheFS.js";
import { ProxyFilesystem, TFilesystem } from "../lib/filesystem/definitions.js";
import putility from '@heyputer/putility';
import { PuterAPIFilesystem } from '../lib/filesystem/APIFS.js';
import { CachedFilesystem } from '../lib/filesystem/CacheFS.js';
import { ProxyFilesystem, TFilesystem } from '../lib/filesystem/definitions.js';
import { PostMessageFilesystem } from '../lib/filesystem/PostMessageFS.js';
import io from '../lib/socket.io/socket.io.esm.min.js';
import { PostMessageFilesystem } from "../lib/filesystem/PostMessageFS.js";
export class FilesystemService extends putility.concepts.Service {
static PROPERTIES = {
// filesystem:
// filesystem:
};
static DEPENDS = ['api-access'];
@@ -19,13 +19,13 @@ export class FilesystemService extends putility.concepts.Service {
re-initialize the socket connection whenever the
authentication token or API origin is changed.
`,
async do () {
async do() {
this.initializeSocket();
}
}
]
},
},
];
_init () {
_init() {
const env = this._.context.env;
if ( env === 'app' ) {
@@ -40,14 +40,14 @@ export class FilesystemService extends putility.concepts.Service {
this.initializeSocket();
}
init_app_fs_ () {
init_app_fs_() {
this.fs_nocache_ = new PostMessageFilesystem({
messageTarget: globalThis.parent,
rpc: this._.context.util.rpc,
}).as(TFilesystem);
this.filesystem = this.fs_nocache_;
}
init_top_fs_ () {
init_top_fs_() {
const api_info = this._.context.services.get('api-access').get_api_info();
this.fs_nocache_ = new PuterAPIFilesystem({ api_info }).as(TFilesystem);
this.fs_cache_ = new CachedFilesystem({ delegate: this.fs_nocache_ }).as(TFilesystem);
@@ -56,15 +56,15 @@ export class FilesystemService extends putility.concepts.Service {
this.filesystem = this.fs_proxy_.as(TFilesystem);
}
cache_on () {
cache_on() {
this.fs_proxy_.delegate = this.fs_cache_;
}
cache_off () {
cache_off() {
this.fs_proxy_.delegate = this.fs_nocache_;
}
async initializeSocket () {
if (this.socket) {
async initializeSocket() {
if ( this.socket ) {
this.socket.disconnect();
}
@@ -77,7 +77,8 @@ export class FilesystemService extends putility.concepts.Service {
}
this.socket = io(api_info.api_origin, {
auth: { auth_token: api_info.auth_token }
auth: { auth_token: api_info.auth_token },
autoUnref: this._.context.env === 'nodejs',
});
this.bindSocketEvents();
@@ -85,38 +86,52 @@ export class FilesystemService extends putility.concepts.Service {
bindSocketEvents() {
this.socket.on('connect', () => {
if(puter.debugMode)
if ( puter.debugMode )
{
console.log('FileSystem Socket: Connected', this.socket.id);
}
});
this.socket.on('disconnect', () => {
if(puter.debugMode)
if ( puter.debugMode )
{
console.log('FileSystem Socket: Disconnected');
}
});
this.socket.on('reconnect', (attempt) => {
if(puter.debugMode)
if ( puter.debugMode )
{
console.log('FileSystem Socket: Reconnected', this.socket.id);
}
});
this.socket.on('reconnect_attempt', (attempt) => {
if(puter.debugMode)
if ( puter.debugMode )
{
console.log('FileSystem Socket: Reconnection Attemps', attempt);
}
});
this.socket.on('reconnect_error', (error) => {
if(puter.debugMode)
if ( puter.debugMode )
{
console.log('FileSystem Socket: Reconnection Error', error);
}
});
this.socket.on('reconnect_failed', () => {
if(puter.debugMode)
if ( puter.debugMode )
{
console.log('FileSystem Socket: Reconnection Failed');
}
});
this.socket.on('error', (error) => {
if(puter.debugMode)
if ( puter.debugMode )
{
console.error('FileSystem Socket Error:', error);
}
});
}
}