Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions packages/platform/package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
{
"name": "@base44/platform",
"version": "0.1.0",
"description": "Browser client for building on the Base44 platform",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"description": "Client and server SDK for building on the Base44 platform",
"main": "dist/client/index.js",
"types": "dist/client/index.d.ts",
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
"types": "./dist/client/index.d.ts",
"default": "./dist/client/index.js"
},
"./server": {
"types": "./dist/server/index.d.ts",
"default": "./dist/server/index.js"
},
"./package.json": "./package.json"
},
Expand Down Expand Up @@ -45,5 +49,12 @@
"bugs": {
"url": "https://github.com/base44/javascript-sdk/issues"
},
"homepage": "https://github.com/base44/javascript-sdk/tree/main/packages/platform#readme"
"homepage": "https://github.com/base44/javascript-sdk/tree/main/packages/platform#readme",
"typesVersions": {
"*": {
"server": [
"dist/server/index.d.ts"
]
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { PlatformClientOptions } from "./client.types.js";
import { normalizeServerUrl } from "../shared/server-url.js";
import { createBuilder } from "./modules/builder.js";
import type { BuilderModule } from "./modules/builder.types.js";

Expand All @@ -9,10 +10,6 @@ export class Base44PlatformClient {

/** Configure shared service/auth settings; each module initializes its own resources. */
constructor(options: PlatformClientOptions) {
const url = new URL(options.serverUrl);
if (!["https:", "http:"].includes(url.protocol) || url.username || url.password || url.search || url.hash || url.pathname !== "/") {
throw new TypeError("serverUrl must be an HTTP(S) origin without credentials, path, query or fragment");
}
this.builder = Object.freeze(createBuilder({ ...options, serverUrl: url.origin }));
this.builder = Object.freeze(createBuilder({ ...options, serverUrl: normalizeServerUrl(options.serverUrl) }));
}
}
File renamed without changes.
File renamed without changes.
3 changes: 3 additions & 0 deletions packages/platform/src/server/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/** Server platform modules, authenticated with a workspace API key. */
export { createBase44PlatformServer } from "./server.js";
export type { Base44PlatformServer, PlatformServerOptions } from "./server.types.js";
9 changes: 9 additions & 0 deletions packages/platform/src/server/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { normalizeServerUrl } from "../shared/server-url.js";
import type { Base44PlatformServer, PlatformServerOptions } from "./server.types.js";

/** Create a server platform client, authenticated with a workspace API key. Performs no network requests. */
export function createBase44PlatformServer(options: PlatformServerOptions): Base44PlatformServer {
if (!options.apiKey) throw new TypeError("apiKey is required");
normalizeServerUrl(options.serverUrl);
return Object.freeze({});
}
10 changes: 10 additions & 0 deletions packages/platform/src/server/server.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/** Shared configuration for server platform modules. */
export interface PlatformServerOptions {
/** Origin of the platform service, e.g. https://base44.app. No path/query/credentials. */
serverUrl: string;
/** Workspace API key. Keep it on your server; never ship it to a browser. */
apiKey: string;
}

/** Server platform client, returned by `createBase44PlatformServer`. Modules are added as properties. */
export interface Base44PlatformServer {}
8 changes: 8 additions & 0 deletions packages/platform/src/shared/server-url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/** Validate a platform origin and return it normalized. Shared by the client and server entrypoints. */
export function normalizeServerUrl(serverUrl: string): string {
const url = new URL(serverUrl);
if (!["https:", "http:"].includes(url.protocol) || url.username || url.password || url.search || url.hash || url.pathname !== "/") {
throw new TypeError("serverUrl must be an HTTP(S) origin without credentials, path, query or fragment");
}
return url.origin;
}
15 changes: 14 additions & 1 deletion packages/platform/tests/package.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ for (const name of Object.keys(manifest.dependencies)) {

test("the tarball contains the compiled entry point and no source or test trees", () => {
const files = new Set(packed.files.map(file => file.path));
for (const file of ["dist/index.js", "dist/index.d.ts", "README.md"]) assert.ok(files.has(file), file);
for (const file of ["dist/client/index.js", "dist/client/index.d.ts", "dist/server/index.js", "dist/server/index.d.ts", "README.md"]) assert.ok(files.has(file), file);
assert.equal([...files].some(file => /^(src|tests|examples)\//.test(file)), false);
});

Expand All @@ -41,7 +41,20 @@ test("the client resolves from the installed package without the runtime SDK", (
`]);
});

test("the server entrypoint resolves from the installed package", () => {
run(process.execPath, ["--input-type=module", "--eval", `
import assert from "node:assert/strict";
import { createBase44PlatformServer } from "@base44/platform/server";
assert.ok(Object.isFrozen(createBase44PlatformServer({ serverUrl: "https://example.test", apiKey: "key" })));
assert.throws(() => createBase44PlatformServer({ serverUrl: "https://example.test/path", apiKey: "key" }), TypeError);
`]);
});

const consumer = `
import { createBase44PlatformServer, type Base44PlatformServer } from "@base44/platform/server";
const server: Base44PlatformServer = createBase44PlatformServer({ serverUrl: "https://example.test", apiKey: "key" });
void server;

import { Base44PlatformClient, type PlatformEvent } from "@base44/platform";
const platform = new Base44PlatformClient({ serverUrl: "https://example.test", getSessionToken: async () => "token" });
platform.builder.init({ onError() {} }).subscribe("a".repeat(24), {
Expand Down
2 changes: 1 addition & 1 deletion packages/platform/tests/unit/client.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
import { Base44PlatformClient, type BuilderSession } from "../../src/index.js";
import { Base44PlatformClient, type BuilderSession } from "../../src/client/index.js";

const fake = vi.hoisted(() => {
const handlers: Record<string, (...args: any[]) => void> = {};
Expand Down
18 changes: 18 additions & 0 deletions packages/platform/tests/unit/server.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { describe, expect, test } from "vitest";
import { createBase44PlatformServer } from "../../src/server/index.js";

describe("createBase44PlatformServer", () => {
test("creates a frozen client without side effects", () => {
expect(Object.isFrozen(createBase44PlatformServer({ serverUrl: "https://api.example.test", apiKey: "key" }))).toBe(true);
});

test("rejects a serverUrl that is not a bare HTTP(S) origin", () => {
for (const serverUrl of ["ftp://example.test", "https://example.test/path", "https://u:p@example.test", "https://example.test?x=1"]) {
expect(() => createBase44PlatformServer({ serverUrl, apiKey: "key" })).toThrow(TypeError);
}
});

test("requires an apiKey", () => {
expect(() => createBase44PlatformServer({ serverUrl: "https://example.test", apiKey: "" })).toThrow(TypeError);
});
});
2 changes: 1 addition & 1 deletion packages/platform/tests/unit/socket.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createServer } from "node:http";
import { WebSocketServer } from "ws";
import { expect, test, vi } from "vitest";
import { Base44PlatformClient } from "../../src/index.js";
import { Base44PlatformClient } from "../../src/client/index.js";

// Minimal Engine.IO/Socket.IO peer exercises the real client without a new server dependency.
test("real Socket.IO handshake, join, snapshot, reconnect and session renewal", async () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/platform/tsconfig.type-tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"rootDir": ".",
"baseUrl": ".",
"paths": {
"@base44/platform": ["src/index.ts"]
"@base44/platform": ["src/client/index.ts"]
}
},
"include": ["src/**/*.ts", "tests/types/**/*.ts", "examples/**/*.ts"]
Expand Down
3 changes: 2 additions & 1 deletion packages/platform/typedoc.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"$schema": "https://typedoc.org/schema.json",
"entryPoints": [
"./src/index.ts"
"./src/client/index.ts",
"./src/server/index.ts"
],
"tsconfig": "./tsconfig.json",
"out": "docs",
Expand Down
Loading