mirror of
https://github.com/HeyPuter/puter.git
synced 2025-12-30 09:40:00 -06:00
@@ -12,16 +12,37 @@ npm install @heyputer/puter.js
|
||||
|
||||
### Importing
|
||||
|
||||
### Node.js (with Auth Token)
|
||||
|
||||
```js
|
||||
const {init} = require("@heyputer/puter.js/src/init.cjs"); // NODE JS ONLY
|
||||
// or
|
||||
import {init} from "@heyputer/puter.js/src/init.cjs";
|
||||
|
||||
const puter = init(process.env.puterAuthToken); // uses your auth token
|
||||
const puter2 = init(process.env.puterAuthToken2); // use some other auth token
|
||||
```
|
||||
|
||||
### Browser (without Auth Token)
|
||||
|
||||
#### ES Modules
|
||||
|
||||
```js
|
||||
import '@heyputer/puter.js';
|
||||
import {puter} from '@heyputer/puter.js';
|
||||
// or
|
||||
import puter from '@heyputer/puter.js';
|
||||
// or
|
||||
import '@heyputer/puter.js'; // puter will be available globally
|
||||
```
|
||||
|
||||
#### CommonJS
|
||||
|
||||
```js
|
||||
const {puter} = require('@heyputer/puter.js');
|
||||
// or
|
||||
const puter = require('@heyputer/puter.js');
|
||||
// or
|
||||
require('@heyputer/puter.js'); // puter will be available globally
|
||||
```
|
||||
|
||||
#### CDN
|
||||
|
||||
953
src/puter-js/index.d.ts
vendored
953
src/puter-js/index.d.ts
vendored
@@ -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;
|
||||
@@ -1,15 +1,16 @@
|
||||
{
|
||||
"name": "@heyputer/puter.js",
|
||||
"version": "2.0.1",
|
||||
"version": "2.0.11",
|
||||
"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/"
|
||||
},
|
||||
|
||||
@@ -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, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
|
||||
// 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;
|
||||
};
|
||||
}
|
||||
})
|
||||
});
|
||||
28
src/puter-js/src/init.cjs
Normal file
28
src/puter-js/src/init.cjs
Normal 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 with auth token
|
||||
* @param {string} authToken - Optional auth token to initialize puter with
|
||||
* @returns {import('../index').puter} The `puter` object from puter.js
|
||||
*/
|
||||
const init = (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 = { init };
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user