@checkstack/script-packages-frontend 0.2.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.
@@ -0,0 +1,84 @@
1
+ import React from "react";
2
+ import { fetchApiRef, useApi, usePluginClient } from "@checkstack/frontend-api";
3
+ import type { AcquireTypes, AcquiredTypeFile } from "@checkstack/ui";
4
+ import { importablePackageNames } from "@checkstack/ui";
5
+ import {
6
+ buildTypeAcquisitionPath,
7
+ PackageTypeClosureSchema,
8
+ pluginMetadata,
9
+ ScriptPackagesApi,
10
+ } from "@checkstack/script-packages-common";
11
+
12
+ /**
13
+ * Provide the lazy Automatic Type Acquisition (ATA) wiring for script
14
+ * editors: an `acquireTypes` resolver that fetches one package's `.d.ts`
15
+ * closure from the cacheable backend route, the current `lockfileHash` as the
16
+ * editor's `acquireResetKey` (so types refresh after a new install), and the
17
+ * importable package names so the import specifier itself autocompletes.
18
+ *
19
+ * The resolver targets `GET /api/script-packages/types/:hash/:specifier`,
20
+ * which sets `Cache-Control: private, max-age=..., immutable` — so once a
21
+ * package's closure is fetched for an install, the browser serves it from
22
+ * cache until a new install changes the hash. The in-editor acquired-set
23
+ * dedupes within a session; the HTTP cache dedupes across reloads.
24
+ *
25
+ * `importablePackages` comes from the installed manifest (what is actually
26
+ * resolvable at runtime), with `@types/*` companions excluded — you import
27
+ * `lodash`, never `@types/lodash` (the `@types` still back the closure types).
28
+ *
29
+ * Returns `acquireTypes: undefined` until the install hash is known, so the
30
+ * editor simply doesn't attempt ATA before packages are ready.
31
+ */
32
+ export function useScriptPackageTypeAcquisition(): {
33
+ acquireTypes: AcquireTypes | undefined;
34
+ acquireResetKey: string | undefined;
35
+ importablePackages: string[];
36
+ } {
37
+ const fetchApi = useApi(fetchApiRef);
38
+ const client = usePluginClient(ScriptPackagesApi);
39
+
40
+ // The current desired install state. Cached generously — it only changes on
41
+ // an install. The hash keys the route + gates/resets acquisition; the
42
+ // manifest is the source of importable names.
43
+ const installStateQuery = client.getInstallState.useQuery(
44
+ {},
45
+ { staleTime: 5 * 60 * 1000 },
46
+ );
47
+ const lockfileHash = installStateQuery.data?.lockfileHash ?? undefined;
48
+ const manifest = installStateQuery.data?.manifest;
49
+
50
+ // Importable package names from the manifest, `@types/*`-free, deduped +
51
+ // sorted. The manifest includes transitive deps too, all of which resolve
52
+ // from the flat `node_modules` at runtime, so they are all importable.
53
+ const importablePackages = React.useMemo(
54
+ () => importablePackageNames((manifest ?? []).map((entry) => entry.name)),
55
+ [manifest],
56
+ );
57
+
58
+ const pluginFetch = React.useMemo(
59
+ () => fetchApi.forPlugin(pluginMetadata.pluginId),
60
+ [fetchApi],
61
+ );
62
+
63
+ const acquireTypes = React.useMemo<AcquireTypes | undefined>(() => {
64
+ // No install yet -> no resolver; the editor simply doesn't attempt ATA.
65
+ if (!lockfileHash) return;
66
+ const hash = lockfileHash;
67
+ return async (specifier: string): Promise<AcquiredTypeFile[]> => {
68
+ const path = buildTypeAcquisitionPath({ lockfileHash: hash, specifier });
69
+ const response = await pluginFetch.fetch(path);
70
+ if (!response.ok) {
71
+ // A stale-hash 409 or any error: no types this round. The editor
72
+ // treats an empty result as "acquired" so it won't hammer the route;
73
+ // a later install bumps the hash and resets the acquired-set.
74
+ return [];
75
+ }
76
+ const json: unknown = await response.json();
77
+ const parsed = PackageTypeClosureSchema.safeParse(json);
78
+ if (!parsed.success) return [];
79
+ return parsed.data.files;
80
+ };
81
+ }, [lockfileHash, pluginFetch]);
82
+
83
+ return { acquireTypes, acquireResetKey: lockfileHash, importablePackages };
84
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "extends": "@checkstack/tsconfig/frontend.json",
3
+ "include": [
4
+ "src"
5
+ ],
6
+ "references": [
7
+ {
8
+ "path": "../common"
9
+ },
10
+ {
11
+ "path": "../frontend-api"
12
+ },
13
+ {
14
+ "path": "../script-packages-common"
15
+ },
16
+ {
17
+ "path": "../ui"
18
+ }
19
+ ]
20
+ }