Merge pull request 'cultcache-ts: browser-safe entry that excludes the node-only single-file store' (#24) from libby/cultcache-ts-browser-entry into main
Some checks failed
CultMesh QUIC native / Build win32-x64 (push) Has been skipped
CultMesh QUIC native / Build linux-x64 (push) Has been skipped
CultMesh QUIC native / Release native binaries (push) Has been skipped
CultMesh portability and performance / ubuntu-latest one-command multiplayer onboarding (push) Has been cancelled
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (push) Has been cancelled
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (push) Has been cancelled
Some checks failed
CultMesh QUIC native / Build win32-x64 (push) Has been skipped
CultMesh QUIC native / Build linux-x64 (push) Has been skipped
CultMesh QUIC native / Release native binaries (push) Has been skipped
CultMesh portability and performance / ubuntu-latest one-command multiplayer onboarding (push) Has been cancelled
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (push) Has been cancelled
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (push) Has been cancelled
This commit is contained in:
commit
a06798fdec
6 changed files with 107 additions and 48 deletions
|
|
@ -15,6 +15,8 @@ All notable changes to `@gamecult/cultcache-ts` are recorded here, following
|
|||
|
||||
### Added
|
||||
|
||||
- `@gamecult/cultcache-ts/browser`, also what a bundler resolves for the package root under the `browser` export condition: the root surface without `SingleFileMessagePackBackingStore` and `readSingleFileStore`, so it imports no `node:*` built-in and no `koffi` and bundles with esbuild `platform: "browser"`. The Node entry exports exactly what it did.
|
||||
|
||||
- `readSingleFileStore(path)`, the one synchronous reader of a single-file store, for consumers that must read a store file rather than decode it themselves. `pullAll`, `push`, `delete` and `pushAll` ask the same reader.
|
||||
- `StoreUnreadableError` for a store file this runtime cannot read, `SchemaConflictError` for a write the catalog cannot describe, and `schemaIdentityOf` with its result type `CultCacheSchemaIdentity` (`schemaId`, `schemaName`, `schemaVersion`, `compatibleSchemaIds`), the one derivation of a definition's identity.
|
||||
- `SingleFileStoreRead`, exported: the result type of `readSingleFileStore` (`absent`, `format`, `envelopes`, `repairedLegacyPayload`).
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ const note = decodeDocumentPayload(noteDocument, envelope.payload); // parsed No
|
|||
const payload = encodeDocumentPayload(noteDocument, note); // DocumentPayload
|
||||
```
|
||||
|
||||
Browsers import the same names from `@gamecult/cultcache-ts/document`, which holds no Node built-ins.
|
||||
Browsers import the same names from `@gamecult/cultcache-ts/document`, which holds no Node built-ins. The whole cache without the single-file store is `@gamecult/cultcache-ts/browser`, which is also what bundlers resolve for the package root (`browser` condition); bring your own `CacheBackingStore`.
|
||||
|
||||
## `.cc` Inspection
|
||||
|
||||
|
|
|
|||
|
|
@ -7,9 +7,15 @@
|
|||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"browser": "./dist/browser.js",
|
||||
"require": "./dist/index.js",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"./browser": {
|
||||
"types": "./dist/browser.d.ts",
|
||||
"require": "./dist/browser.js",
|
||||
"default": "./dist/browser.js"
|
||||
},
|
||||
"./document": {
|
||||
"types": "./dist/document.d.ts",
|
||||
"require": "./dist/document.js",
|
||||
|
|
@ -25,6 +31,9 @@
|
|||
"*": {
|
||||
"document": [
|
||||
"dist/document.d.ts"
|
||||
],
|
||||
"browser": [
|
||||
"dist/browser.d.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
|
|
|||
49
packages/cultcache-ts/src/browser.ts
Normal file
49
packages/cultcache-ts/src/browser.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// Browser-safe surface: everything the package root exports except the node-only single-file store
|
||||
// (node:fs, koffi). Bring your own CacheBackingStore, or none.
|
||||
export * as codecs from "./codec";
|
||||
export type { Codec, SlotsCodec } from "./codec";
|
||||
export { CultCache, CultCacheBuilder } from "./cult-cache";
|
||||
export { inspectCultCacheBytes } from "./cult-cache-inspector";
|
||||
export {
|
||||
decodeDocumentPayload,
|
||||
defineDocumentRegistry,
|
||||
defineDocumentType,
|
||||
documentPayloadBytes,
|
||||
documentPayloadFromBytes,
|
||||
encodeDocumentPayload,
|
||||
schemaIdentityOf,
|
||||
type CultCacheSchemaIdentity,
|
||||
} from "./document";
|
||||
export { CultCachePayloadDepthError, MAX_PAYLOAD_DEPTH } from "./payload-depth";
|
||||
export type { DocumentPayload } from "./document";
|
||||
export { SchemaConflictError, StoreUnreadableError } from "./store-format";
|
||||
export type {
|
||||
AnyCultCacheDocumentDefinition,
|
||||
CacheBackingStore,
|
||||
CultCacheDocumentAccessor,
|
||||
CultCacheDocumentDefinition,
|
||||
CultCacheDocumentFieldName,
|
||||
CultCacheDocumentFormatter,
|
||||
CultCacheDocumentIndexDefinition,
|
||||
CultCacheDocumentRegistry,
|
||||
CultCacheSchemaCatalogEntry,
|
||||
CultCacheSchemaCatalogMember,
|
||||
CultCacheSchema,
|
||||
CultCacheDocumentValue,
|
||||
CultCacheEnvelope,
|
||||
CultCacheIndexScalar,
|
||||
InferCultCacheSchemaValue,
|
||||
CultCacheStoreRegistration,
|
||||
PushAllOptions,
|
||||
} from "./types";
|
||||
export type {
|
||||
CultCacheInspection,
|
||||
InspectedCatalogEntry,
|
||||
InspectedCatalogMember,
|
||||
InspectedRecord,
|
||||
} from "./cult-cache-inspector";
|
||||
export {
|
||||
defineSwarmDocuments,
|
||||
type DefineDocumentType,
|
||||
type SwarmDocumentCatalog,
|
||||
} from "./swarm-documents";
|
||||
|
|
@ -1,48 +1,2 @@
|
|||
export * as codecs from "./codec";
|
||||
export type { Codec, SlotsCodec } from "./codec";
|
||||
export { CultCache, CultCacheBuilder } from "./cult-cache";
|
||||
export { inspectCultCacheBytes } from "./cult-cache-inspector";
|
||||
export {
|
||||
decodeDocumentPayload,
|
||||
defineDocumentRegistry,
|
||||
defineDocumentType,
|
||||
documentPayloadBytes,
|
||||
documentPayloadFromBytes,
|
||||
encodeDocumentPayload,
|
||||
schemaIdentityOf,
|
||||
type CultCacheSchemaIdentity,
|
||||
} from "./document";
|
||||
export { CultCachePayloadDepthError, MAX_PAYLOAD_DEPTH } from "./payload-depth";
|
||||
export type { DocumentPayload } from "./document";
|
||||
export * from "./browser";
|
||||
export { SingleFileMessagePackBackingStore, readSingleFileStore, type SingleFileStoreRead } from "./single-file-messagepack-backing-store";
|
||||
export { SchemaConflictError, StoreUnreadableError } from "./store-format";
|
||||
export type {
|
||||
AnyCultCacheDocumentDefinition,
|
||||
CacheBackingStore,
|
||||
CultCacheDocumentAccessor,
|
||||
CultCacheDocumentDefinition,
|
||||
CultCacheDocumentFieldName,
|
||||
CultCacheDocumentFormatter,
|
||||
CultCacheDocumentIndexDefinition,
|
||||
CultCacheDocumentRegistry,
|
||||
CultCacheSchemaCatalogEntry,
|
||||
CultCacheSchemaCatalogMember,
|
||||
CultCacheSchema,
|
||||
CultCacheDocumentValue,
|
||||
CultCacheEnvelope,
|
||||
CultCacheIndexScalar,
|
||||
InferCultCacheSchemaValue,
|
||||
CultCacheStoreRegistration,
|
||||
PushAllOptions,
|
||||
} from "./types";
|
||||
export type {
|
||||
CultCacheInspection,
|
||||
InspectedCatalogEntry,
|
||||
InspectedCatalogMember,
|
||||
InspectedRecord,
|
||||
} from "./cult-cache-inspector";
|
||||
export {
|
||||
defineSwarmDocuments,
|
||||
type DefineDocumentType,
|
||||
type SwarmDocumentCatalog,
|
||||
} from "./swarm-documents";
|
||||
|
|
|
|||
45
packages/cultcache-ts/test/browser-entry.test.ts
Normal file
45
packages/cultcache-ts/test/browser-entry.test.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import assert from "node:assert/strict";
|
||||
import { join } from "node:path";
|
||||
import test from "node:test";
|
||||
import { runInNewContext } from "node:vm";
|
||||
import { build } from "esbuild";
|
||||
|
||||
// dist-test/test/ -> dist-test/src/
|
||||
const srcDir = join(__dirname, "..", "src");
|
||||
|
||||
async function bundleForBrowser(entry: string): Promise<string> {
|
||||
const result = await build({
|
||||
entryPoints: [join(srcDir, entry)],
|
||||
bundle: true,
|
||||
platform: "browser",
|
||||
format: "iife",
|
||||
globalName: "cultcache",
|
||||
write: false,
|
||||
logLevel: "silent",
|
||||
});
|
||||
return result.outputFiles[0].text;
|
||||
}
|
||||
|
||||
test("the browser entry bundles for platform=browser and runs without node", async () => {
|
||||
const code = await bundleForBrowser("browser.js");
|
||||
assert.ok(!/node:fs|koffi/.test(code), "no node-only store in the browser bundle");
|
||||
// A context with no require/process/Buffer: only what a browser page has.
|
||||
const sandbox: Record<string, unknown> = { TextEncoder, TextDecoder };
|
||||
runInNewContext(code + "\nglobalThis.__api = cultcache;", sandbox);
|
||||
const api = sandbox.__api as Record<string, unknown>;
|
||||
assert.equal(typeof api.CultCache, "function");
|
||||
assert.equal(typeof api.defineDocumentType, "function");
|
||||
assert.equal(api.SingleFileMessagePackBackingStore, undefined);
|
||||
});
|
||||
|
||||
test("the node entry still exports the single-file store and everything the browser entry has", async () => {
|
||||
const node = await import(join(srcDir, "index.js"));
|
||||
const browser = await import(join(srcDir, "browser.js"));
|
||||
assert.equal(typeof node.SingleFileMessagePackBackingStore, "function");
|
||||
assert.equal(typeof node.readSingleFileStore, "function");
|
||||
for (const name of Object.keys(browser)) assert.ok(name in node, `node entry lost ${name}`);
|
||||
});
|
||||
|
||||
test("the package root keeps the node-only store out of the browser build", async () => {
|
||||
await assert.rejects(bundleForBrowser("index.js"), /node:fs|node:path|koffi/);
|
||||
});
|
||||
Loading…
Reference in a new issue