New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

@onlyworlds/sdk

Package Overview
Dependencies
Maintainers
1
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@onlyworlds/sdk - npm Package Compare versions

Comparing version
2.1.3
to
2.2.0
+353
-307
dist/index.js

@@ -42,309 +42,2 @@ "use strict";

// src/token-resource.ts
var TokenResource = class {
constructor(client) {
this.client = client;
}
/**
* Get current token status for authenticated user
*
* Returns daily token allowance, usage, and availability.
* Matches base-tool's checkStatus() pattern.
*
* @returns Current token status
* @example
* ```typescript
* const status = await client.tokens.getStatus();
* console.log(`Available: ${status.tokens_available_today}/${status.token_rating}`);
* console.log(`Used today: ${status.tokens_used_today}`);
* console.log(`Active sessions: ${status.sessions_active}`);
* ```
*/
async getStatus() {
return this.client.request("GET", "/tokens/status/");
}
/**
* Consume tokens for service usage
*
* Reports token consumption to track daily usage. Allows consumption even if
* exceeds available tokens (tracks as debt), but warns via error field.
* Matches base-tool's reportUsage() pattern.
*
* @param params - Token consumption parameters
* @returns Consumption result with updated balance
* @example
* ```typescript
* const result = await client.tokens.consume({
* amount: 500,
* service: 'worldbuilding_tool',
* metadata: {
* feature: 'character_generation',
* model: 'gpt-4',
* prompt_tokens: 300,
* completion_tokens: 200
* }
* });
*
* if (result.error) {
* console.warn('Token warning:', result.error);
* }
* console.log(`${result.tokens_remaining} tokens remaining`);
* ```
*/
async consume(params) {
return this.client.request("POST", "/tokens/consume/", {
body: {
amount: params.amount,
service: params.service || "sdk_client",
session_id: params.sessionId ?? null,
metadata: params.metadata ?? null
}
});
}
/**
* Get encrypted OpenAI API key (advanced use case)
*
* Requires minimum 100 tokens available. Creates a 1-hour session for tracking.
* Returns encrypted key that must be decrypted client-side using Fernet.
*
* See base-tool/src/llm/token-service.ts:99-155 for full implementation example
* including client-side decryption with the 'fernet' npm package.
*
* @returns Encrypted access key and session info
* @throws Error if insufficient tokens (< 100)
* @example
* ```typescript
* // Get encrypted key
* const access = await client.tokens.getAccessKey();
*
* // Decrypt using fernet library (see base-tool for full example)
* // 1. Derive key from world ID using SHA-256
* // 2. Use 'fernet' npm package to decrypt
* // 3. Use decrypted OpenAI key for direct API calls
* // 4. Report usage with access.session_id
*
* console.log('Session:', access.session_id);
* console.log('Expires:', access.expires_at);
* ```
*/
async getAccessKey() {
return this.client.request("GET", "/tokens/access-key/");
}
/**
* Revoke a specific token session
*
* Invalidates the session ID obtained from getAccessKey().
* Use when cleaning up or on logout.
*
* @param sessionId - Session ID to revoke
* @returns Revocation result
* @example
* ```typescript
* await client.tokens.revokeSession('session-id-here');
* ```
*/
async revokeSession(sessionId) {
return this.client.request(
"POST",
`/tokens/revoke-session/?session_id=${encodeURIComponent(sessionId)}`
);
}
/**
* Revoke all active sessions (emergency use)
*
* Invalidates all token sessions for the authenticated user.
* Use for security cleanup or when sessions are stuck.
*
* @returns Revocation result with count of revoked sessions
* @example
* ```typescript
* const result = await client.tokens.revokeAllSessions();
* console.log(`Revoked ${result.sessions_revoked} sessions`);
* ```
*/
async revokeAllSessions() {
return this.client.request("POST", "/tokens/revoke-all-sessions/");
}
/**
* Get public encryption info (no auth required)
*
* Returns algorithm details and example code for client-side decryption.
* Public endpoint - can be called without authentication.
*
* @returns Encryption algorithm and implementation details
* @example
* ```typescript
* const info = await client.tokens.getEncryptionInfo();
* console.log('Algorithm:', info.algorithm);
* console.log('Key derivation:', info.key_derivation);
* console.log(info.javascript_example);
* ```
*/
async getEncryptionInfo() {
return this.client.request("GET", "/tokens/encryption-info/");
}
};
// src/client.ts
var Resource = class {
constructor(client, elementType) {
this.client = client;
this.elementType = elementType;
}
async list(options) {
const response = await this.client.request("GET", `/${this.elementType}/`, { params: options });
if (Array.isArray(response)) {
return {
count: response.length,
next: null,
previous: null,
results: response
};
}
return response;
}
async get(id) {
return this.client.request("GET", `/${this.elementType}/${id}/`);
}
async create(data) {
const body = this.elementType === "pin" ? this.roundPinCoordinates(data) : data;
return this.client.request("POST", `/${this.elementType}/`, { body });
}
async update(id, data) {
const body = this.elementType === "pin" ? this.roundPinCoordinates(data) : data;
return this.client.request("PATCH", `/${this.elementType}/${id}/`, { body });
}
async delete(id) {
return this.client.request("DELETE", `/${this.elementType}/${id}/`);
}
/**
* Round Pin coordinates to integers (API requirement)
* @private
*/
roundPinCoordinates(data) {
const rounded = { ...data };
if (typeof rounded.x === "number") rounded.x = Math.round(rounded.x);
if (typeof rounded.y === "number") rounded.y = Math.round(rounded.y);
if (typeof rounded.z === "number") rounded.z = Math.round(rounded.z);
return rounded;
}
};
var WorldResource = class {
constructor(client) {
this.client = client;
}
/**
* Get the world associated with the current API key
* Returns the world directly (not wrapped in pagination)
*/
async get() {
return this.client.request("GET", "/world/");
}
/**
* Update the current world
*/
async update(data) {
return this.client.request("PATCH", "/world/", { body: data });
}
};
var OnlyWorldsClient = class {
constructor(config) {
this.baseUrl = config.baseUrl || "https://www.onlyworlds.com/api/worldapi";
this.headers = {
"Content-Type": "application/json",
"API-Key": config.apiKey,
"API-Pin": config.apiPin
};
this.worlds = new WorldResource(this);
this.tokens = new TokenResource(this);
this.abilities = new Resource(this, "ability");
this.characters = new Resource(this, "character");
this.collectives = new Resource(this, "collective");
this.constructs = new Resource(this, "construct");
this.creatures = new Resource(this, "creature");
this.events = new Resource(this, "event");
this.families = new Resource(this, "family");
this.institutions = new Resource(this, "institution");
this.languages = new Resource(this, "language");
this.laws = new Resource(this, "law");
this.locations = new Resource(this, "location");
this.maps = new Resource(this, "map");
this.markers = new Resource(this, "marker");
this.narratives = new Resource(this, "narrative");
this.objects = new Resource(this, "object");
this.phenomena = new Resource(this, "phenomenon");
this.pins = new Resource(this, "pin");
this.relations = new Resource(this, "relation");
this.species = new Resource(this, "species");
this.titles = new Resource(this, "title");
this.traits = new Resource(this, "trait");
this.zones = new Resource(this, "zone");
}
/**
* Make a request to the OnlyWorlds API
*/
async request(method, path, options) {
const url = new URL(`${this.baseUrl}${path}`);
if (options?.params) {
Object.entries(options.params).forEach(([key, value]) => {
if (value !== void 0 && value !== null) {
url.searchParams.append(key, String(value));
}
});
}
const fetchOptions = {
method,
headers: this.headers
};
if (options?.body && ["POST", "PATCH", "PUT"].includes(method)) {
fetchOptions.body = JSON.stringify(options.body);
}
const response = await fetch(url.toString(), fetchOptions);
if (!response.ok) {
let errorMessage = `API Error ${response.status}`;
try {
const errorText = await response.text();
if (errorText) {
try {
const errorJson = JSON.parse(errorText);
if (Array.isArray(errorJson.detail)) {
const validationErrors = errorJson.detail.map((err) => {
const location = err.loc ? err.loc.join(".") : "unknown";
return `${location}: ${err.msg}`;
}).join("; ");
errorMessage += `: ${validationErrors}`;
} else {
errorMessage += `: ${errorJson.detail || errorJson.error || errorText}`;
}
} catch {
errorMessage += `: ${errorText}`;
}
}
} catch {
}
throw new Error(errorMessage);
}
if (response.status === 204) {
return void 0;
}
return response.json();
}
/**
* Helper to convert nested objects to _id/_ids format
*/
static prepareInput(data) {
const result = { ...data };
for (const [key, value] of Object.entries(result)) {
if (value && typeof value === "object" && "id" in value) {
delete result[key];
result[`${key}_id`] = value.id;
} else if (Array.isArray(value) && value.length > 0 && typeof value[0] === "object" && "id" in value[0]) {
delete result[key];
result[`${key}_ids`] = value.map((item) => item.id);
}
}
return result;
}
};
// src/types.ts

@@ -1168,2 +861,355 @@ var ElementType = /* @__PURE__ */ ((ElementType2) => {

// src/token-resource.ts
var TokenResource = class {
constructor(client) {
this.client = client;
}
/**
* Get current token status for authenticated user
*
* Returns daily token allowance, usage, and availability.
* Matches base-tool's checkStatus() pattern.
*
* @returns Current token status
* @example
* ```typescript
* const status = await client.tokens.getStatus();
* console.log(`Available: ${status.tokens_available_today}/${status.token_rating}`);
* console.log(`Used today: ${status.tokens_used_today}`);
* console.log(`Active sessions: ${status.sessions_active}`);
* ```
*/
async getStatus() {
return this.client.request("GET", "/tokens/status/");
}
/**
* Consume tokens for service usage
*
* Reports token consumption to track daily usage. Allows consumption even if
* exceeds available tokens (tracks as debt), but warns via error field.
* Matches base-tool's reportUsage() pattern.
*
* @param params - Token consumption parameters
* @returns Consumption result with updated balance
* @example
* ```typescript
* const result = await client.tokens.consume({
* amount: 500,
* service: 'worldbuilding_tool',
* metadata: {
* feature: 'character_generation',
* model: 'gpt-4',
* prompt_tokens: 300,
* completion_tokens: 200
* }
* });
*
* if (result.error) {
* console.warn('Token warning:', result.error);
* }
* console.log(`${result.tokens_remaining} tokens remaining`);
* ```
*/
async consume(params) {
return this.client.request("POST", "/tokens/consume/", {
body: {
amount: params.amount,
service: params.service || "sdk_client",
session_id: params.sessionId ?? null,
metadata: params.metadata ?? null
}
});
}
/**
* Get encrypted OpenAI API key (advanced use case)
*
* Requires minimum 100 tokens available. Creates a 1-hour session for tracking.
* Returns encrypted key that must be decrypted client-side using Fernet.
*
* See base-tool/src/llm/token-service.ts:99-155 for full implementation example
* including client-side decryption with the 'fernet' npm package.
*
* @returns Encrypted access key and session info
* @throws Error if insufficient tokens (< 100)
* @example
* ```typescript
* // Get encrypted key
* const access = await client.tokens.getAccessKey();
*
* // Decrypt using fernet library (see base-tool for full example)
* // 1. Derive key from world ID using SHA-256
* // 2. Use 'fernet' npm package to decrypt
* // 3. Use decrypted OpenAI key for direct API calls
* // 4. Report usage with access.session_id
*
* console.log('Session:', access.session_id);
* console.log('Expires:', access.expires_at);
* ```
*/
async getAccessKey() {
return this.client.request("GET", "/tokens/access-key/");
}
/**
* Revoke a specific token session
*
* Invalidates the session ID obtained from getAccessKey().
* Use when cleaning up or on logout.
*
* @param sessionId - Session ID to revoke
* @returns Revocation result
* @example
* ```typescript
* await client.tokens.revokeSession('session-id-here');
* ```
*/
async revokeSession(sessionId) {
return this.client.request(
"POST",
`/tokens/revoke-session/?session_id=${encodeURIComponent(sessionId)}`
);
}
/**
* Revoke all active sessions (emergency use)
*
* Invalidates all token sessions for the authenticated user.
* Use for security cleanup or when sessions are stuck.
*
* @returns Revocation result with count of revoked sessions
* @example
* ```typescript
* const result = await client.tokens.revokeAllSessions();
* console.log(`Revoked ${result.sessions_revoked} sessions`);
* ```
*/
async revokeAllSessions() {
return this.client.request("POST", "/tokens/revoke-all-sessions/");
}
/**
* Get public encryption info (no auth required)
*
* Returns algorithm details and example code for client-side decryption.
* Public endpoint - can be called without authentication.
*
* @returns Encryption algorithm and implementation details
* @example
* ```typescript
* const info = await client.tokens.getEncryptionInfo();
* console.log('Algorithm:', info.algorithm);
* console.log('Key derivation:', info.key_derivation);
* console.log(info.javascript_example);
* ```
*/
async getEncryptionInfo() {
return this.client.request("GET", "/tokens/encryption-info/");
}
};
// src/client.ts
var Resource = class {
constructor(client, elementType) {
this.client = client;
this.elementType = elementType;
}
async list(options) {
const response = await this.client.request("GET", `/${this.elementType}/`, { params: options });
if (Array.isArray(response)) {
return {
count: response.length,
next: null,
previous: null,
results: response
};
}
return response;
}
async get(id) {
return this.client.request("GET", `/${this.elementType}/${id}/`);
}
async create(data) {
let body = OnlyWorldsClient.prepareRelations(data, this.elementType);
if (this.elementType === "pin") body = this.roundPinCoordinates(body);
return this.client.request("POST", `/${this.elementType}/`, { body });
}
async update(id, data) {
let body = OnlyWorldsClient.prepareRelations(data, this.elementType);
if (this.elementType === "pin") body = this.roundPinCoordinates(body);
return this.client.request("PATCH", `/${this.elementType}/${id}/`, { body });
}
async delete(id) {
return this.client.request("DELETE", `/${this.elementType}/${id}/`);
}
/**
* Round Pin coordinates to integers (API requirement)
* @private
*/
roundPinCoordinates(data) {
const rounded = { ...data };
if (typeof rounded.x === "number") rounded.x = Math.round(rounded.x);
if (typeof rounded.y === "number") rounded.y = Math.round(rounded.y);
if (typeof rounded.z === "number") rounded.z = Math.round(rounded.z);
return rounded;
}
};
var WorldResource = class {
constructor(client) {
this.client = client;
}
/**
* Get the world associated with the current API key
* Returns the world directly (not wrapped in pagination)
*/
async get() {
return this.client.request("GET", "/world/");
}
/**
* Update the current world
*/
async update(data) {
return this.client.request("PATCH", "/world/", { body: data });
}
};
var OnlyWorldsClient = class {
constructor(config) {
this.baseUrl = config.baseUrl || "https://www.onlyworlds.com/api/worldapi";
this.headers = {
"Content-Type": "application/json",
"API-Key": config.apiKey,
"API-Pin": config.apiPin
};
this.worlds = new WorldResource(this);
this.tokens = new TokenResource(this);
this.abilities = new Resource(this, "ability");
this.characters = new Resource(this, "character");
this.collectives = new Resource(this, "collective");
this.constructs = new Resource(this, "construct");
this.creatures = new Resource(this, "creature");
this.events = new Resource(this, "event");
this.families = new Resource(this, "family");
this.institutions = new Resource(this, "institution");
this.languages = new Resource(this, "language");
this.laws = new Resource(this, "law");
this.locations = new Resource(this, "location");
this.maps = new Resource(this, "map");
this.markers = new Resource(this, "marker");
this.narratives = new Resource(this, "narrative");
this.objects = new Resource(this, "object");
this.phenomena = new Resource(this, "phenomenon");
this.pins = new Resource(this, "pin");
this.relations = new Resource(this, "relation");
this.species = new Resource(this, "species");
this.titles = new Resource(this, "title");
this.traits = new Resource(this, "trait");
this.zones = new Resource(this, "zone");
}
/**
* Make a request to the OnlyWorlds API
*/
async request(method, path, options) {
const url = new URL(`${this.baseUrl}${path}`);
if (options?.params) {
Object.entries(options.params).forEach(([key, value]) => {
if (value !== void 0 && value !== null) {
url.searchParams.append(key, String(value));
}
});
}
const fetchOptions = {
method,
headers: this.headers
};
if (options?.body && ["POST", "PATCH", "PUT"].includes(method)) {
fetchOptions.body = JSON.stringify(options.body);
}
const response = await fetch(url.toString(), fetchOptions);
if (!response.ok) {
let errorMessage = `API Error ${response.status}`;
try {
const errorText = await response.text();
if (errorText) {
try {
const errorJson = JSON.parse(errorText);
if (Array.isArray(errorJson.detail)) {
const validationErrors = errorJson.detail.map((err) => {
const location = err.loc ? err.loc.join(".") : "unknown";
return `${location}: ${err.msg}`;
}).join("; ");
errorMessage += `: ${validationErrors}`;
} else {
errorMessage += `: ${errorJson.detail || errorJson.error || errorText}`;
}
} catch {
errorMessage += `: ${errorText}`;
}
}
} catch {
}
throw new Error(errorMessage);
}
if (response.status === 204) {
return void 0;
}
return response.json();
}
/**
* Helper to convert nested objects to _id/_ids format (legacy method)
* @deprecated Use prepareRelations instead - it's called automatically in create/update
*/
static prepareInput(data) {
const result = { ...data };
for (const [key, value] of Object.entries(result)) {
if (value && typeof value === "object" && !Array.isArray(value) && "id" in value) {
delete result[key];
result[`${key}_id`] = value.id;
} else if (Array.isArray(value) && value.length > 0 && typeof value[0] === "object" && "id" in value[0]) {
delete result[key];
result[`${key}_ids`] = value.map((item) => item.id);
}
}
return result;
}
/**
* Convert relation fields to API format (_id/_ids suffix)
*
* The OnlyWorlds API expects:
* - single_link fields: fieldname_id (e.g., birthplace_id)
* - multi_link fields: fieldname_ids (e.g., species_ids)
*
* This method auto-converts based on FIELD_SCHEMA:
* - { species: ["id1", "id2"] } → { species_ids: ["id1", "id2"] }
* - { birthplace: "location-id" } → { birthplace_id: "location-id" }
* - { species: [{id: "id1", name: "X"}] } → { species_ids: ["id1"] }
*
* Called automatically by create() and update() methods.
*/
static prepareRelations(data, elementType) {
const schema = FIELD_SCHEMA[elementType];
if (!schema) return data;
const result = { ...data };
for (const [key, value] of Object.entries(result)) {
const fieldDef = schema[key];
if (!fieldDef) continue;
if (fieldDef.type === "single_link") {
delete result[key];
if (value && typeof value === "object" && "id" in value) {
result[`${key}_id`] = value.id;
} else if (typeof value === "string" || value === null) {
result[`${key}_id`] = value;
}
} else if (fieldDef.type === "multi_link") {
delete result[key];
if (Array.isArray(value)) {
if (value.length > 0 && typeof value[0] === "object" && "id" in value[0]) {
result[`${key}_ids`] = value.map((item) => item.id);
} else {
result[`${key}_ids`] = value;
}
} else {
result[`${key}_ids`] = [];
}
}
}
return result;
}
};
// src/token-types.ts

@@ -1170,0 +1216,0 @@ var GameTier = /* @__PURE__ */ ((GameTier2) => {

@@ -1,308 +0,1 @@

// src/token-resource.ts
var TokenResource = class {
constructor(client) {
this.client = client;
}
/**
* Get current token status for authenticated user
*
* Returns daily token allowance, usage, and availability.
* Matches base-tool's checkStatus() pattern.
*
* @returns Current token status
* @example
* ```typescript
* const status = await client.tokens.getStatus();
* console.log(`Available: ${status.tokens_available_today}/${status.token_rating}`);
* console.log(`Used today: ${status.tokens_used_today}`);
* console.log(`Active sessions: ${status.sessions_active}`);
* ```
*/
async getStatus() {
return this.client.request("GET", "/tokens/status/");
}
/**
* Consume tokens for service usage
*
* Reports token consumption to track daily usage. Allows consumption even if
* exceeds available tokens (tracks as debt), but warns via error field.
* Matches base-tool's reportUsage() pattern.
*
* @param params - Token consumption parameters
* @returns Consumption result with updated balance
* @example
* ```typescript
* const result = await client.tokens.consume({
* amount: 500,
* service: 'worldbuilding_tool',
* metadata: {
* feature: 'character_generation',
* model: 'gpt-4',
* prompt_tokens: 300,
* completion_tokens: 200
* }
* });
*
* if (result.error) {
* console.warn('Token warning:', result.error);
* }
* console.log(`${result.tokens_remaining} tokens remaining`);
* ```
*/
async consume(params) {
return this.client.request("POST", "/tokens/consume/", {
body: {
amount: params.amount,
service: params.service || "sdk_client",
session_id: params.sessionId ?? null,
metadata: params.metadata ?? null
}
});
}
/**
* Get encrypted OpenAI API key (advanced use case)
*
* Requires minimum 100 tokens available. Creates a 1-hour session for tracking.
* Returns encrypted key that must be decrypted client-side using Fernet.
*
* See base-tool/src/llm/token-service.ts:99-155 for full implementation example
* including client-side decryption with the 'fernet' npm package.
*
* @returns Encrypted access key and session info
* @throws Error if insufficient tokens (< 100)
* @example
* ```typescript
* // Get encrypted key
* const access = await client.tokens.getAccessKey();
*
* // Decrypt using fernet library (see base-tool for full example)
* // 1. Derive key from world ID using SHA-256
* // 2. Use 'fernet' npm package to decrypt
* // 3. Use decrypted OpenAI key for direct API calls
* // 4. Report usage with access.session_id
*
* console.log('Session:', access.session_id);
* console.log('Expires:', access.expires_at);
* ```
*/
async getAccessKey() {
return this.client.request("GET", "/tokens/access-key/");
}
/**
* Revoke a specific token session
*
* Invalidates the session ID obtained from getAccessKey().
* Use when cleaning up or on logout.
*
* @param sessionId - Session ID to revoke
* @returns Revocation result
* @example
* ```typescript
* await client.tokens.revokeSession('session-id-here');
* ```
*/
async revokeSession(sessionId) {
return this.client.request(
"POST",
`/tokens/revoke-session/?session_id=${encodeURIComponent(sessionId)}`
);
}
/**
* Revoke all active sessions (emergency use)
*
* Invalidates all token sessions for the authenticated user.
* Use for security cleanup or when sessions are stuck.
*
* @returns Revocation result with count of revoked sessions
* @example
* ```typescript
* const result = await client.tokens.revokeAllSessions();
* console.log(`Revoked ${result.sessions_revoked} sessions`);
* ```
*/
async revokeAllSessions() {
return this.client.request("POST", "/tokens/revoke-all-sessions/");
}
/**
* Get public encryption info (no auth required)
*
* Returns algorithm details and example code for client-side decryption.
* Public endpoint - can be called without authentication.
*
* @returns Encryption algorithm and implementation details
* @example
* ```typescript
* const info = await client.tokens.getEncryptionInfo();
* console.log('Algorithm:', info.algorithm);
* console.log('Key derivation:', info.key_derivation);
* console.log(info.javascript_example);
* ```
*/
async getEncryptionInfo() {
return this.client.request("GET", "/tokens/encryption-info/");
}
};
// src/client.ts
var Resource = class {
constructor(client, elementType) {
this.client = client;
this.elementType = elementType;
}
async list(options) {
const response = await this.client.request("GET", `/${this.elementType}/`, { params: options });
if (Array.isArray(response)) {
return {
count: response.length,
next: null,
previous: null,
results: response
};
}
return response;
}
async get(id) {
return this.client.request("GET", `/${this.elementType}/${id}/`);
}
async create(data) {
const body = this.elementType === "pin" ? this.roundPinCoordinates(data) : data;
return this.client.request("POST", `/${this.elementType}/`, { body });
}
async update(id, data) {
const body = this.elementType === "pin" ? this.roundPinCoordinates(data) : data;
return this.client.request("PATCH", `/${this.elementType}/${id}/`, { body });
}
async delete(id) {
return this.client.request("DELETE", `/${this.elementType}/${id}/`);
}
/**
* Round Pin coordinates to integers (API requirement)
* @private
*/
roundPinCoordinates(data) {
const rounded = { ...data };
if (typeof rounded.x === "number") rounded.x = Math.round(rounded.x);
if (typeof rounded.y === "number") rounded.y = Math.round(rounded.y);
if (typeof rounded.z === "number") rounded.z = Math.round(rounded.z);
return rounded;
}
};
var WorldResource = class {
constructor(client) {
this.client = client;
}
/**
* Get the world associated with the current API key
* Returns the world directly (not wrapped in pagination)
*/
async get() {
return this.client.request("GET", "/world/");
}
/**
* Update the current world
*/
async update(data) {
return this.client.request("PATCH", "/world/", { body: data });
}
};
var OnlyWorldsClient = class {
constructor(config) {
this.baseUrl = config.baseUrl || "https://www.onlyworlds.com/api/worldapi";
this.headers = {
"Content-Type": "application/json",
"API-Key": config.apiKey,
"API-Pin": config.apiPin
};
this.worlds = new WorldResource(this);
this.tokens = new TokenResource(this);
this.abilities = new Resource(this, "ability");
this.characters = new Resource(this, "character");
this.collectives = new Resource(this, "collective");
this.constructs = new Resource(this, "construct");
this.creatures = new Resource(this, "creature");
this.events = new Resource(this, "event");
this.families = new Resource(this, "family");
this.institutions = new Resource(this, "institution");
this.languages = new Resource(this, "language");
this.laws = new Resource(this, "law");
this.locations = new Resource(this, "location");
this.maps = new Resource(this, "map");
this.markers = new Resource(this, "marker");
this.narratives = new Resource(this, "narrative");
this.objects = new Resource(this, "object");
this.phenomena = new Resource(this, "phenomenon");
this.pins = new Resource(this, "pin");
this.relations = new Resource(this, "relation");
this.species = new Resource(this, "species");
this.titles = new Resource(this, "title");
this.traits = new Resource(this, "trait");
this.zones = new Resource(this, "zone");
}
/**
* Make a request to the OnlyWorlds API
*/
async request(method, path, options) {
const url = new URL(`${this.baseUrl}${path}`);
if (options?.params) {
Object.entries(options.params).forEach(([key, value]) => {
if (value !== void 0 && value !== null) {
url.searchParams.append(key, String(value));
}
});
}
const fetchOptions = {
method,
headers: this.headers
};
if (options?.body && ["POST", "PATCH", "PUT"].includes(method)) {
fetchOptions.body = JSON.stringify(options.body);
}
const response = await fetch(url.toString(), fetchOptions);
if (!response.ok) {
let errorMessage = `API Error ${response.status}`;
try {
const errorText = await response.text();
if (errorText) {
try {
const errorJson = JSON.parse(errorText);
if (Array.isArray(errorJson.detail)) {
const validationErrors = errorJson.detail.map((err) => {
const location = err.loc ? err.loc.join(".") : "unknown";
return `${location}: ${err.msg}`;
}).join("; ");
errorMessage += `: ${validationErrors}`;
} else {
errorMessage += `: ${errorJson.detail || errorJson.error || errorText}`;
}
} catch {
errorMessage += `: ${errorText}`;
}
}
} catch {
}
throw new Error(errorMessage);
}
if (response.status === 204) {
return void 0;
}
return response.json();
}
/**
* Helper to convert nested objects to _id/_ids format
*/
static prepareInput(data) {
const result = { ...data };
for (const [key, value] of Object.entries(result)) {
if (value && typeof value === "object" && "id" in value) {
delete result[key];
result[`${key}_id`] = value.id;
} else if (Array.isArray(value) && value.length > 0 && typeof value[0] === "object" && "id" in value[0]) {
delete result[key];
result[`${key}_ids`] = value.map((item) => item.id);
}
}
return result;
}
};
// src/types.ts

@@ -1126,2 +819,355 @@ var ElementType = /* @__PURE__ */ ((ElementType2) => {

// src/token-resource.ts
var TokenResource = class {
constructor(client) {
this.client = client;
}
/**
* Get current token status for authenticated user
*
* Returns daily token allowance, usage, and availability.
* Matches base-tool's checkStatus() pattern.
*
* @returns Current token status
* @example
* ```typescript
* const status = await client.tokens.getStatus();
* console.log(`Available: ${status.tokens_available_today}/${status.token_rating}`);
* console.log(`Used today: ${status.tokens_used_today}`);
* console.log(`Active sessions: ${status.sessions_active}`);
* ```
*/
async getStatus() {
return this.client.request("GET", "/tokens/status/");
}
/**
* Consume tokens for service usage
*
* Reports token consumption to track daily usage. Allows consumption even if
* exceeds available tokens (tracks as debt), but warns via error field.
* Matches base-tool's reportUsage() pattern.
*
* @param params - Token consumption parameters
* @returns Consumption result with updated balance
* @example
* ```typescript
* const result = await client.tokens.consume({
* amount: 500,
* service: 'worldbuilding_tool',
* metadata: {
* feature: 'character_generation',
* model: 'gpt-4',
* prompt_tokens: 300,
* completion_tokens: 200
* }
* });
*
* if (result.error) {
* console.warn('Token warning:', result.error);
* }
* console.log(`${result.tokens_remaining} tokens remaining`);
* ```
*/
async consume(params) {
return this.client.request("POST", "/tokens/consume/", {
body: {
amount: params.amount,
service: params.service || "sdk_client",
session_id: params.sessionId ?? null,
metadata: params.metadata ?? null
}
});
}
/**
* Get encrypted OpenAI API key (advanced use case)
*
* Requires minimum 100 tokens available. Creates a 1-hour session for tracking.
* Returns encrypted key that must be decrypted client-side using Fernet.
*
* See base-tool/src/llm/token-service.ts:99-155 for full implementation example
* including client-side decryption with the 'fernet' npm package.
*
* @returns Encrypted access key and session info
* @throws Error if insufficient tokens (< 100)
* @example
* ```typescript
* // Get encrypted key
* const access = await client.tokens.getAccessKey();
*
* // Decrypt using fernet library (see base-tool for full example)
* // 1. Derive key from world ID using SHA-256
* // 2. Use 'fernet' npm package to decrypt
* // 3. Use decrypted OpenAI key for direct API calls
* // 4. Report usage with access.session_id
*
* console.log('Session:', access.session_id);
* console.log('Expires:', access.expires_at);
* ```
*/
async getAccessKey() {
return this.client.request("GET", "/tokens/access-key/");
}
/**
* Revoke a specific token session
*
* Invalidates the session ID obtained from getAccessKey().
* Use when cleaning up or on logout.
*
* @param sessionId - Session ID to revoke
* @returns Revocation result
* @example
* ```typescript
* await client.tokens.revokeSession('session-id-here');
* ```
*/
async revokeSession(sessionId) {
return this.client.request(
"POST",
`/tokens/revoke-session/?session_id=${encodeURIComponent(sessionId)}`
);
}
/**
* Revoke all active sessions (emergency use)
*
* Invalidates all token sessions for the authenticated user.
* Use for security cleanup or when sessions are stuck.
*
* @returns Revocation result with count of revoked sessions
* @example
* ```typescript
* const result = await client.tokens.revokeAllSessions();
* console.log(`Revoked ${result.sessions_revoked} sessions`);
* ```
*/
async revokeAllSessions() {
return this.client.request("POST", "/tokens/revoke-all-sessions/");
}
/**
* Get public encryption info (no auth required)
*
* Returns algorithm details and example code for client-side decryption.
* Public endpoint - can be called without authentication.
*
* @returns Encryption algorithm and implementation details
* @example
* ```typescript
* const info = await client.tokens.getEncryptionInfo();
* console.log('Algorithm:', info.algorithm);
* console.log('Key derivation:', info.key_derivation);
* console.log(info.javascript_example);
* ```
*/
async getEncryptionInfo() {
return this.client.request("GET", "/tokens/encryption-info/");
}
};
// src/client.ts
var Resource = class {
constructor(client, elementType) {
this.client = client;
this.elementType = elementType;
}
async list(options) {
const response = await this.client.request("GET", `/${this.elementType}/`, { params: options });
if (Array.isArray(response)) {
return {
count: response.length,
next: null,
previous: null,
results: response
};
}
return response;
}
async get(id) {
return this.client.request("GET", `/${this.elementType}/${id}/`);
}
async create(data) {
let body = OnlyWorldsClient.prepareRelations(data, this.elementType);
if (this.elementType === "pin") body = this.roundPinCoordinates(body);
return this.client.request("POST", `/${this.elementType}/`, { body });
}
async update(id, data) {
let body = OnlyWorldsClient.prepareRelations(data, this.elementType);
if (this.elementType === "pin") body = this.roundPinCoordinates(body);
return this.client.request("PATCH", `/${this.elementType}/${id}/`, { body });
}
async delete(id) {
return this.client.request("DELETE", `/${this.elementType}/${id}/`);
}
/**
* Round Pin coordinates to integers (API requirement)
* @private
*/
roundPinCoordinates(data) {
const rounded = { ...data };
if (typeof rounded.x === "number") rounded.x = Math.round(rounded.x);
if (typeof rounded.y === "number") rounded.y = Math.round(rounded.y);
if (typeof rounded.z === "number") rounded.z = Math.round(rounded.z);
return rounded;
}
};
var WorldResource = class {
constructor(client) {
this.client = client;
}
/**
* Get the world associated with the current API key
* Returns the world directly (not wrapped in pagination)
*/
async get() {
return this.client.request("GET", "/world/");
}
/**
* Update the current world
*/
async update(data) {
return this.client.request("PATCH", "/world/", { body: data });
}
};
var OnlyWorldsClient = class {
constructor(config) {
this.baseUrl = config.baseUrl || "https://www.onlyworlds.com/api/worldapi";
this.headers = {
"Content-Type": "application/json",
"API-Key": config.apiKey,
"API-Pin": config.apiPin
};
this.worlds = new WorldResource(this);
this.tokens = new TokenResource(this);
this.abilities = new Resource(this, "ability");
this.characters = new Resource(this, "character");
this.collectives = new Resource(this, "collective");
this.constructs = new Resource(this, "construct");
this.creatures = new Resource(this, "creature");
this.events = new Resource(this, "event");
this.families = new Resource(this, "family");
this.institutions = new Resource(this, "institution");
this.languages = new Resource(this, "language");
this.laws = new Resource(this, "law");
this.locations = new Resource(this, "location");
this.maps = new Resource(this, "map");
this.markers = new Resource(this, "marker");
this.narratives = new Resource(this, "narrative");
this.objects = new Resource(this, "object");
this.phenomena = new Resource(this, "phenomenon");
this.pins = new Resource(this, "pin");
this.relations = new Resource(this, "relation");
this.species = new Resource(this, "species");
this.titles = new Resource(this, "title");
this.traits = new Resource(this, "trait");
this.zones = new Resource(this, "zone");
}
/**
* Make a request to the OnlyWorlds API
*/
async request(method, path, options) {
const url = new URL(`${this.baseUrl}${path}`);
if (options?.params) {
Object.entries(options.params).forEach(([key, value]) => {
if (value !== void 0 && value !== null) {
url.searchParams.append(key, String(value));
}
});
}
const fetchOptions = {
method,
headers: this.headers
};
if (options?.body && ["POST", "PATCH", "PUT"].includes(method)) {
fetchOptions.body = JSON.stringify(options.body);
}
const response = await fetch(url.toString(), fetchOptions);
if (!response.ok) {
let errorMessage = `API Error ${response.status}`;
try {
const errorText = await response.text();
if (errorText) {
try {
const errorJson = JSON.parse(errorText);
if (Array.isArray(errorJson.detail)) {
const validationErrors = errorJson.detail.map((err) => {
const location = err.loc ? err.loc.join(".") : "unknown";
return `${location}: ${err.msg}`;
}).join("; ");
errorMessage += `: ${validationErrors}`;
} else {
errorMessage += `: ${errorJson.detail || errorJson.error || errorText}`;
}
} catch {
errorMessage += `: ${errorText}`;
}
}
} catch {
}
throw new Error(errorMessage);
}
if (response.status === 204) {
return void 0;
}
return response.json();
}
/**
* Helper to convert nested objects to _id/_ids format (legacy method)
* @deprecated Use prepareRelations instead - it's called automatically in create/update
*/
static prepareInput(data) {
const result = { ...data };
for (const [key, value] of Object.entries(result)) {
if (value && typeof value === "object" && !Array.isArray(value) && "id" in value) {
delete result[key];
result[`${key}_id`] = value.id;
} else if (Array.isArray(value) && value.length > 0 && typeof value[0] === "object" && "id" in value[0]) {
delete result[key];
result[`${key}_ids`] = value.map((item) => item.id);
}
}
return result;
}
/**
* Convert relation fields to API format (_id/_ids suffix)
*
* The OnlyWorlds API expects:
* - single_link fields: fieldname_id (e.g., birthplace_id)
* - multi_link fields: fieldname_ids (e.g., species_ids)
*
* This method auto-converts based on FIELD_SCHEMA:
* - { species: ["id1", "id2"] } → { species_ids: ["id1", "id2"] }
* - { birthplace: "location-id" } → { birthplace_id: "location-id" }
* - { species: [{id: "id1", name: "X"}] } → { species_ids: ["id1"] }
*
* Called automatically by create() and update() methods.
*/
static prepareRelations(data, elementType) {
const schema = FIELD_SCHEMA[elementType];
if (!schema) return data;
const result = { ...data };
for (const [key, value] of Object.entries(result)) {
const fieldDef = schema[key];
if (!fieldDef) continue;
if (fieldDef.type === "single_link") {
delete result[key];
if (value && typeof value === "object" && "id" in value) {
result[`${key}_id`] = value.id;
} else if (typeof value === "string" || value === null) {
result[`${key}_id`] = value;
}
} else if (fieldDef.type === "multi_link") {
delete result[key];
if (Array.isArray(value)) {
if (value.length > 0 && typeof value[0] === "object" && "id" in value[0]) {
result[`${key}_ids`] = value.map((item) => item.id);
} else {
result[`${key}_ids`] = value;
}
} else {
result[`${key}_ids`] = [];
}
}
}
return result;
}
};
// src/token-types.ts

@@ -1128,0 +1174,0 @@ var GameTier = /* @__PURE__ */ ((GameTier2) => {

{
"name": "@onlyworlds/sdk",
"version": "2.1.3",
"version": "2.2.0",
"description": "TypeScript SDK for the OnlyWorlds API - build world-building applications with type safety",

@@ -5,0 +5,0 @@ "main": "dist/index.js",

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display