feat: libs 0005 — standalone sends the project header on lib requests (project-scoped server-side resolution); bump pcbjam-shared

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Gergő Törcsvári 2026-06-13 17:16:40 +02:00
commit f04915ee1d
No known key found for this signature in database
GPG key ID: 8E75F2CDE64E5322
4 changed files with 20 additions and 14 deletions

@ -1 +1 @@
Subproject commit a1d18b53a7b976c219a824800027c289b9e9fe2c
Subproject commit df297291b0bdfd0228c900e9c1a0c7279ffd1533

View file

@ -442,7 +442,7 @@ export function WasmTool({
log: append,
onStatus: setStatus,
onAbort: oom.onAbort,
libsSource: libsSourceConfig(),
libsSource: libsSourceConfig(projectId),
});
// Register the save sink before the file opens: from here on, every
// editor File→Save (MEMFS write) is routed onward through saveBytes.

View file

@ -69,14 +69,14 @@ export function libsOwner(): string {
return import.meta.env.VITE_LIBS_OWNER ?? "local-user";
}
export function libsSourceConfig(): LibsSource | null {
export function libsSourceConfig(projectId?: string): LibsSource | null {
const kind = import.meta.env.VITE_LIBS_SOURCE ?? "remote";
const base =
kind === "off"
? null
: kind === "static"
? staticLibsSource()
: remoteLibsSource(API_BASE_URL, libsOwner());
: remoteLibsSource(API_BASE_URL, libsOwner(), projectId);
// 0004-A spike: `?libwrite=1` adds one in-memory writable user lib so the
// editor save path works with no backend (a dev/test aid). The real remote

View file

@ -1,4 +1,4 @@
import { contract, OWNER_HEADER } from "@pcbjam/shared";
import { contract, OWNER_HEADER, PROJECT_HEADER } from "@pcbjam/shared";
import { initClient } from "@ts-rest/core";
import type { LibInfo, LibItemInfo, LibsSource } from "./source";
@ -7,16 +7,22 @@ import type { LibInfo, LibItemInfo, LibsSource } from "./source";
* server, or the GPL example backend). Read list ops go through the ts-rest
* client; item bodies stream from the raw text route
* `GET /api/libs/:lib/items/:kind/:name`, and writes hit the symmetric
* `PUT` route (binary/text doesn't round-trip ts-rest). The `owner` is carried
* on every request via `OWNER_HEADER` (thin per-user; absent ⇒ backend default).
* `PUT` route (binary/text doesn't round-trip ts-rest). Every request carries
* the `owner` (thin per-user) and the `project` (project-scoped server-side
* resolution) via `OWNER_HEADER`/`PROJECT_HEADER`; absent ⇒ backend default.
*/
export function remoteLibsSource(apiBase: string, owner?: string): LibsSource {
const ownerHeaders: Record<string, string> = owner
? { [OWNER_HEADER]: owner }
: {};
export function remoteLibsSource(
apiBase: string,
owner?: string,
project?: string,
): LibsSource {
const reqHeaders: Record<string, string> = {
...(owner ? { [OWNER_HEADER]: owner } : {}),
...(project ? { [PROJECT_HEADER]: project } : {}),
};
const client = initClient(contract, {
baseUrl: apiBase,
baseHeaders: ownerHeaders,
baseHeaders: reqHeaders,
});
const itemUrl = (libId: string, kind: string, name: string) =>
@ -47,7 +53,7 @@ export function remoteLibsSource(apiBase: string, owner?: string): LibsSource {
name: string,
): Promise<string | null> {
const res = await fetch(itemUrl(libId, kind, name), {
headers: ownerHeaders,
headers: reqHeaders,
});
if (!res.ok) return null;
return await res.text();
@ -61,7 +67,7 @@ export function remoteLibsSource(apiBase: string, owner?: string): LibsSource {
): Promise<boolean> {
const res = await fetch(itemUrl(libId, kind, name), {
method: "PUT",
headers: { ...ownerHeaders, "Content-Type": "text/plain; charset=utf-8" },
headers: { ...reqHeaders, "Content-Type": "text/plain; charset=utf-8" },
body,
});
return res.ok;