camox 0.59.0 → 0.61.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/core/createApp.d.ts +8 -8
  2. package/dist/core/createBlock.d.ts +1 -2
  3. package/dist/core/createLayout.d.ts +10 -2
  4. package/dist/core/createLayout.js +1 -3
  5. package/dist/core/editing/BlockEditingRuntime.d.ts +1 -0
  6. package/dist/core/editing/CompleteBlockEditingRuntime.js +12 -9
  7. package/dist/core/editing/createEditableBlock.d.ts +4 -4
  8. package/dist/core/editing/createEditableBlock.js +20 -26
  9. package/dist/core/lib/artifactData.js +2 -2
  10. package/dist/core/lib/contentType.js +5 -4
  11. package/dist/features/preview/components/SelectionEditor.js +1 -1
  12. package/dist/features/renderer/RendererApp.js +187 -103
  13. package/dist/features/renderer/rendererHost.js +23 -16
  14. package/dist/features/renderer/studioConnection.js +8 -4
  15. package/dist/features/runtime/loopbackFetch.d.ts +8 -0
  16. package/dist/features/runtime/publicCache.d.ts +21 -0
  17. package/dist/features/runtime/publishingDraft.d.ts +4 -0
  18. package/dist/features/runtime/runtime.js +54 -3
  19. package/dist/features/runtime/worker.js +1 -4
  20. package/dist/features/runtime/workerRequest.d.ts +8 -0
  21. package/dist/features/runtime/workerRuntime.d.ts +5 -0
  22. package/dist/features/runtime/workerRuntime.js +6 -0
  23. package/dist/features/sdkBundle/buildSdkBundle.d.ts +40 -0
  24. package/dist/features/sdkBundle/buildSdkBundle.js +323 -0
  25. package/dist/features/sdkBundle/localSdkBundle.d.ts +44 -0
  26. package/dist/features/sdkBundle/localSdkBundle.js +162 -0
  27. package/dist/features/sdkBundle/sdkBundle.d.ts +3 -0
  28. package/dist/features/sdkBundle/sdkBundle.js +4 -0
  29. package/dist/features/sdkBundle/sdkBundleName.d.ts +59 -0
  30. package/dist/features/sdkBundle/sdkBundleName.js +164 -0
  31. package/dist/features/sdkBundle/specifiers.d.ts +34 -0
  32. package/dist/features/sdkBundle/specifiers.js +146 -0
  33. package/dist/features/settings/SocialProfilesSettings.js +284 -151
  34. package/dist/features/settings/useProjectMutation.js +6 -5
  35. package/dist/features/studio/FollowAgentProvider.js +71 -12
  36. package/dist/features/studio/components/ChatWidget.js +85 -43
  37. package/dist/features/studio/components/Patrol.js +311 -208
  38. package/dist/features/studio/components/Squareling.js +164 -66
  39. package/dist/features/studio/components/ThreadComposer.js +139 -69
  40. package/dist/features/studio/components/ThreadMessages.js +115 -45
  41. package/dist/features/studio/editorWhereContext.js +11 -0
  42. package/dist/features/studio/useEditorSelection.js +135 -0
  43. package/dist/features/vite/hostedChecks.js +1 -1
  44. package/dist/features/vite/runtimeDev.js +2 -1
  45. package/dist/features/vite/sdkBundleBuild.js +74 -0
  46. package/dist/features/vite/staticAssetHeaders.js +7 -4
  47. package/dist/features/vite/vite.d.ts +0 -2
  48. package/dist/features/vite/vite.js +33 -3
  49. package/dist/lib/auth.js +24 -58
  50. package/dist/lib/server-auth-cookie.js +12 -2
  51. package/dist/lib/stored-auth-cookies.js +76 -0
  52. package/dist/lib/use-thread.js +9 -5
  53. package/dist/package.js +1 -1
  54. package/dist/studio.css +1 -1
  55. package/package.json +17 -4
@@ -0,0 +1,164 @@
1
+ import { LUCIDE_ICON_HELPER, SDK_BUNDLE_TARGETS, loadsAtFirstPaint, packageOfSpecifier, sdkBundleEntryFile, sdkBundleSpecifiers } from "./specifiers.js";
2
+ import { parse } from "yaml";
3
+
4
+ //#region src/features/sdkBundle/sdkBundleName.ts
5
+ /**
6
+ * An SDK Bundle's name, computed from a pnpm lockfile alone, before anything is built: the
7
+ * bundle and the website's build both bake it in. Runs anywhere Web Crypto does, in Node or a
8
+ * Worker, so the platform can name a bundle without installing the website.
9
+ */
10
+ /**
11
+ * The version of the code that builds SDK Bundles. Bump it whenever a change to the builder
12
+ * changes what a bundle holds for the same packages and settings.
13
+ */
14
+ const SDK_BUNDLE_BUILDER_VERSION = "2";
15
+ /** Where a website's pages and Worker find an SDK Bundle's files: `/_camox/sdk/<name>/`. */
16
+ function sdkBundlePath(name) {
17
+ return `/_camox/sdk/${name}/`;
18
+ }
19
+ /**
20
+ * The file a website's build names its SDK Bundle in, as `SdkBundleNames` JSON, in the server
21
+ * build's folder beside its `wrangler.json`. A build without it is whole-app.
22
+ */
23
+ const SDK_BUNDLE_BUILD_MANIFEST = "camox-sdk-bundle.json";
24
+ /** What a hosted build whose SDK Bundle is missing fails with, followed by the names as JSON. */
25
+ const SDK_BUNDLE_MISSING = "CAMOX_SDK_BUNDLE_MISSING";
26
+ /** The line a build that's missing its SDK Bundle prints its error with. */
27
+ function sdkBundleMissingLine(names) {
28
+ return `${SDK_BUNDLE_MISSING} ${JSON.stringify({
29
+ browser: names.browser,
30
+ workerd: names.workerd
31
+ })}`;
32
+ }
33
+ /**
34
+ * The SDK Bundle a build's log says is missing, or null when the build failed for another reason:
35
+ * the caller ensures the bundle, then builds again.
36
+ */
37
+ function sdkBundleMissingIn(log) {
38
+ const json = new RegExp(`${SDK_BUNDLE_MISSING} (\\{[^\n]*?\\})`).exec(log)?.[1];
39
+ if (!json) return null;
40
+ try {
41
+ const { browser, workerd } = JSON.parse(json);
42
+ return typeof browser === "string" && typeof workerd === "string" ? {
43
+ browser,
44
+ workerd
45
+ } : null;
46
+ } catch {
47
+ return null;
48
+ }
49
+ }
50
+ /**
51
+ * Packages camox depends on that no specifier on the list reaches: its CLI runs in Node, beside
52
+ * the website's build.
53
+ */
54
+ const NEVER_BUNDLED = /* @__PURE__ */ new Set(["@camox/cli"]);
55
+ /**
56
+ * A digest of the builder's version, the target, the settings and the lockfile's resolution
57
+ * (name, version, integrity) of every package the target's specifiers reach.
58
+ */
59
+ async function sdkBundleName({ lockfile, importer = ".", target, settings, builderVersion = "2" }) {
60
+ const parsed = parse(lockfile);
61
+ const specifiers = heldSpecifiers(parsed, importer, target);
62
+ const resolutions = bundledResolutions(parsed, importer, [...new Set(specifiers.map(packageOfSpecifier))]);
63
+ const digested = JSON.stringify({
64
+ builderVersion,
65
+ target,
66
+ specifiers,
67
+ settings: {
68
+ iconIds: settings.iconIds,
69
+ telemetryDisabled: settings.telemetryDisabled,
70
+ experimentalFeatures: settings.experimentalFeatures,
71
+ apiUrl: settings.apiUrl
72
+ },
73
+ packages: resolutions
74
+ });
75
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(digested));
76
+ return [...new Uint8Array(digest).slice(0, 16)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
77
+ }
78
+ /**
79
+ * The specifiers the SDK Bundle of the website at `importer` holds for `target`: the packages it
80
+ * shares with camox are in only when it installs them.
81
+ */
82
+ function sdkBundleSpecifiersFor({ lockfile, importer = ".", target }) {
83
+ return heldSpecifiers(parse(lockfile), importer, target);
84
+ }
85
+ function heldSpecifiers(lockfile, importer, target) {
86
+ const locked = lockfile.importers?.[importer];
87
+ if (!locked) throw new Error(`Camox: the lockfile has no importer at ${JSON.stringify(importer)}.`);
88
+ const installed = new Set([
89
+ locked.dependencies,
90
+ locked.optionalDependencies,
91
+ locked.devDependencies
92
+ ].flatMap((deps) => Object.keys(deps ?? {})));
93
+ return sdkBundleSpecifiers(target, installed);
94
+ }
95
+ /** Each package the roots reach, as `name@version` with its resolution, sorted. */
96
+ function bundledResolutions(lockfile, importer, roots) {
97
+ const resolutions = /* @__PURE__ */ new Set();
98
+ const visited = /* @__PURE__ */ new Set();
99
+ const importerOf = (path) => {
100
+ const locked = lockfile.importers?.[path];
101
+ if (!locked) throw new Error(`Camox: the lockfile has no importer at ${JSON.stringify(path)}.`);
102
+ return locked;
103
+ };
104
+ const importerVersions = (locked, withDev) => Object.fromEntries([
105
+ ...Object.entries(locked.dependencies ?? {}),
106
+ ...Object.entries(locked.optionalDependencies ?? {}),
107
+ ...withDev ? Object.entries(locked.devDependencies ?? {}) : []
108
+ ].map(([name, { version }]) => [name, version]));
109
+ const visit = (name, version, from) => {
110
+ if (NEVER_BUNDLED.has(name)) return;
111
+ if (version.startsWith("link:")) {
112
+ const path = joinPath(from, version.slice(5));
113
+ if (visited.has(`link:${path}`)) return;
114
+ visited.add(`link:${path}`);
115
+ resolutions.add(`${name}@link:${path}`);
116
+ for (const [dependency, dependencyVersion] of Object.entries(importerVersions(importerOf(path), false))) visit(dependency, dependencyVersion, path);
117
+ return;
118
+ }
119
+ const { snapshotKey, packageKey } = lockedKeys(name, version);
120
+ if (visited.has(snapshotKey)) return;
121
+ visited.add(snapshotKey);
122
+ const locked = lockfile.packages?.[packageKey];
123
+ if (!locked) throw new Error(`Camox: the lockfile has no package ${packageKey}.`);
124
+ resolutions.add(`${packageKey} ${JSON.stringify(locked.resolution ?? {})}`);
125
+ const peers = new Set(Object.keys(locked.peerDependencies ?? {}));
126
+ const snapshot = lockfile.snapshots?.[snapshotKey] ?? {};
127
+ for (const [dependency, dependencyVersion] of Object.entries({
128
+ ...snapshot.dependencies,
129
+ ...snapshot.optionalDependencies
130
+ })) if (!peers.has(dependency)) visit(dependency, dependencyVersion, from);
131
+ };
132
+ const website = importerVersions(importerOf(importer), true);
133
+ for (const root of roots) {
134
+ const version = website[root];
135
+ if (!version) throw new Error(`Camox: the website doesn't install ${root}.`);
136
+ visit(root, version, importer);
137
+ }
138
+ return [...resolutions].sort();
139
+ }
140
+ /**
141
+ * Where a dependency's version finds it in the lockfile. pnpm writes `1.0.0(react@19.3.1)` for a
142
+ * package resolved with peers, and the target's own `name@version` for an aliased one.
143
+ */
144
+ function lockedKeys(name, version) {
145
+ const withoutPeers = version.split("(")[0];
146
+ if (/^(@?[^@]+)@(.+)$/.exec(withoutPeers)) return {
147
+ snapshotKey: version,
148
+ packageKey: withoutPeers
149
+ };
150
+ return {
151
+ snapshotKey: `${name}@${version}`,
152
+ packageKey: `${name}@${withoutPeers}`
153
+ };
154
+ }
155
+ /** A workspace folder, from the lockfile's, as pnpm names importers: `packages/sdk`, or `.`. */
156
+ function joinPath(from, relative) {
157
+ const parts = [];
158
+ for (const part of `${from}/${relative}`.split("/")) if (part === "..") parts.pop();
159
+ else if (part && part !== ".") parts.push(part);
160
+ return parts.join("/") || ".";
161
+ }
162
+
163
+ //#endregion
164
+ export { LUCIDE_ICON_HELPER, SDK_BUNDLE_BUILDER_VERSION, SDK_BUNDLE_BUILD_MANIFEST, SDK_BUNDLE_MISSING, SDK_BUNDLE_TARGETS, loadsAtFirstPaint, packageOfSpecifier, sdkBundleEntryFile, sdkBundleMissingIn, sdkBundleMissingLine, sdkBundleName, sdkBundlePath, sdkBundleSpecifiers, sdkBundleSpecifiersFor };
@@ -0,0 +1,34 @@
1
+ //#region src/features/sdkBundle/specifiers.d.ts
2
+ /**
3
+ * What an SDK Bundle holds: the bare specifiers a website's build leaves to it. The SDK owns this
4
+ * list, so an SDK release can change it. Each specifier gets one entry in each target it's for.
5
+ */
6
+ /** Who loads an SDK Bundle's version: visitors' browsers, or the Worker in workerd. */
7
+ export type SdkBundleTarget = "browser" | "workerd";
8
+ export declare const SDK_BUNDLE_TARGETS: readonly SdkBundleTarget[];
9
+ /**
10
+ * lucide's icon helper, which draws every icon. The website keeps its own icons, and they draw
11
+ * with the bundle's helper: a website's icon module imports it by relative path, from inside the
12
+ * package.
13
+ */
14
+ export declare const LUCIDE_ICON_HELPER = "lucide-react/dist/esm/createLucideIcon.mjs";
15
+ /**
16
+ * The bare specifiers an SDK Bundle's `target` version holds, each with an entry of its own, for
17
+ * a website that installs `websitePackages`.
18
+ */
19
+ export declare function sdkBundleSpecifiers(target: SdkBundleTarget, websitePackages: ReadonlySet<string>): string[];
20
+ /**
21
+ * Whether a page loads `specifier` at first paint: the camox entry points a page's code imports,
22
+ * the page's own client, React, and the packages websites share with camox. A page that imports
23
+ * another of camox's entry points loads one more chunk.
24
+ */
25
+ export declare function loadsAtFirstPaint(specifier: string): boolean;
26
+ /** The npm package a bare specifier imports from: `@tanstack/react-query` for itself. */
27
+ export declare function packageOfSpecifier(specifier: string): string;
28
+ /**
29
+ * The file a specifier's entry is, in its target's folder: `react/jsx-runtime` is
30
+ * `react~jsx-runtime.mjs`. Every entry sits at the folder's root, so the website's build can point
31
+ * at it from the bundle's name alone.
32
+ */
33
+ export declare function sdkBundleEntryFile(specifier: string): string;
34
+ //#endregion
@@ -0,0 +1,146 @@
1
+ //#region src/features/sdkBundle/specifiers.ts
2
+ const SDK_BUNDLE_TARGETS = ["browser", "workerd"];
3
+ /**
4
+ * lucide's icon helper, which draws every icon. The website keeps its own icons, and they draw
5
+ * with the bundle's helper: a website's icon module imports it by relative path, from inside the
6
+ * package.
7
+ */
8
+ const LUCIDE_ICON_HELPER = "lucide-react/dist/esm/createLucideIcon.mjs";
9
+ const SPECIFIERS = [
10
+ { specifier: "camox/createCollection" },
11
+ { specifier: "camox/dom" },
12
+ {
13
+ specifier: "camox/createApp",
14
+ page: true
15
+ },
16
+ {
17
+ specifier: "camox/createBlock",
18
+ page: true
19
+ },
20
+ {
21
+ specifier: "camox/createLayout",
22
+ page: true
23
+ },
24
+ {
25
+ specifier: "camox/document",
26
+ page: true
27
+ },
28
+ { specifier: "camox/CamoxPreview" },
29
+ { specifier: "camox/CamoxContent" },
30
+ { specifier: "camox/CamoxProvider" },
31
+ { specifier: "camox/CamoxStudio" },
32
+ {
33
+ specifier: "camox/navigation",
34
+ page: true
35
+ },
36
+ {
37
+ specifier: "camox/_internal/pageClient",
38
+ only: "browser",
39
+ page: true
40
+ },
41
+ {
42
+ specifier: "camox/_internal/studioClient",
43
+ only: "browser"
44
+ },
45
+ {
46
+ specifier: "camox/_internal/rendererClient",
47
+ only: "browser"
48
+ },
49
+ {
50
+ specifier: "camox/_internal/pageServer",
51
+ only: "workerd"
52
+ },
53
+ {
54
+ specifier: "camox/_internal/studioServer",
55
+ only: "workerd"
56
+ },
57
+ {
58
+ specifier: "camox/_internal/runtime",
59
+ only: "workerd"
60
+ },
61
+ {
62
+ specifier: "camox/_internal/imageResponse",
63
+ only: "workerd"
64
+ },
65
+ {
66
+ specifier: "camox/_internal/workerRuntime",
67
+ only: "workerd"
68
+ },
69
+ {
70
+ specifier: "react",
71
+ page: true
72
+ },
73
+ {
74
+ specifier: "react/jsx-runtime",
75
+ page: true
76
+ },
77
+ {
78
+ specifier: "react/compiler-runtime",
79
+ page: true
80
+ },
81
+ {
82
+ specifier: "react-dom",
83
+ page: true
84
+ },
85
+ {
86
+ specifier: "react-dom/client",
87
+ only: "browser",
88
+ page: true
89
+ },
90
+ {
91
+ specifier: "react-dom/server",
92
+ only: "workerd"
93
+ },
94
+ { specifier: "@tanstack/react-query" },
95
+ {
96
+ specifier: "tailwind-merge",
97
+ shared: true,
98
+ page: true
99
+ },
100
+ {
101
+ specifier: "clsx",
102
+ shared: true,
103
+ page: true
104
+ },
105
+ {
106
+ specifier: "class-variance-authority",
107
+ shared: true,
108
+ page: true
109
+ },
110
+ {
111
+ specifier: LUCIDE_ICON_HELPER,
112
+ shared: true,
113
+ page: true
114
+ }
115
+ ];
116
+ /**
117
+ * The bare specifiers an SDK Bundle's `target` version holds, each with an entry of its own, for
118
+ * a website that installs `websitePackages`.
119
+ */
120
+ function sdkBundleSpecifiers(target, websitePackages) {
121
+ return SPECIFIERS.filter(({ specifier, only, shared }) => (!only || only === target) && (!shared || websitePackages.has(packageOfSpecifier(specifier)))).map(({ specifier }) => specifier);
122
+ }
123
+ /**
124
+ * Whether a page loads `specifier` at first paint: the camox entry points a page's code imports,
125
+ * the page's own client, React, and the packages websites share with camox. A page that imports
126
+ * another of camox's entry points loads one more chunk.
127
+ */
128
+ function loadsAtFirstPaint(specifier) {
129
+ return SPECIFIERS.some((entry) => entry.specifier === specifier && entry.page);
130
+ }
131
+ /** The npm package a bare specifier imports from: `@tanstack/react-query` for itself. */
132
+ function packageOfSpecifier(specifier) {
133
+ const parts = specifier.split("/");
134
+ return specifier.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
135
+ }
136
+ /**
137
+ * The file a specifier's entry is, in its target's folder: `react/jsx-runtime` is
138
+ * `react~jsx-runtime.mjs`. Every entry sits at the folder's root, so the website's build can point
139
+ * at it from the bundle's name alone.
140
+ */
141
+ function sdkBundleEntryFile(specifier) {
142
+ return `${specifier.replace(/\.[cm]?js$/, "").replaceAll("/", "~")}.mjs`;
143
+ }
144
+
145
+ //#endregion
146
+ export { LUCIDE_ICON_HELPER, SDK_BUNDLE_TARGETS, loadsAtFirstPaint, packageOfSpecifier, sdkBundleEntryFile, sdkBundleSpecifiers };