buildonce 0.1.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.
- package/README.md +20 -0
- package/lib/index.d.ts +29 -0
- package/lib/index.js +219 -0
- package/package.json +26 -0
- package/prebuilds/darwin-arm64/libonce.dylib +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Once JavaScript SDK
|
|
2
|
+
|
|
3
|
+
`buildonce` exposes Once primitives to Node.js. It loads the same
|
|
4
|
+
native Once library used by the other SDKs and does not expose script
|
|
5
|
+
execution.
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
const { Cache } = require("buildonce");
|
|
9
|
+
|
|
10
|
+
async function example() {
|
|
11
|
+
const cache = new Cache();
|
|
12
|
+
const digest = await cache.putBlob(Buffer.from("hello"));
|
|
13
|
+
const bytes = await cache.getBlob(digest);
|
|
14
|
+
|
|
15
|
+
console.assert(bytes.equals(Buffer.from("hello")));
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The package looks for a bundled native library under `prebuilds/`. Set
|
|
20
|
+
`ONCE_LIBRARY_PATH` to load a custom `libonce` build.
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface ActionResult {
|
|
2
|
+
exitCode: number;
|
|
3
|
+
stdout?: string | null;
|
|
4
|
+
stderr?: string | null;
|
|
5
|
+
outputs?: Record<string, string>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface CacheStats {
|
|
9
|
+
blobCount: number;
|
|
10
|
+
blobBytes: number;
|
|
11
|
+
actionCount: number;
|
|
12
|
+
actionBytes: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class OnceError extends Error {}
|
|
16
|
+
|
|
17
|
+
export class Cache {
|
|
18
|
+
version(): string;
|
|
19
|
+
digest(bytes: Buffer | Uint8Array | string): string;
|
|
20
|
+
putBlob(bytes: Buffer | Uint8Array | string): Promise<string>;
|
|
21
|
+
getBlob(digest: string): Promise<Buffer>;
|
|
22
|
+
hasBlob(digest: string): Promise<boolean>;
|
|
23
|
+
putActionResult(result: ActionResult, actionDigest: string): Promise<boolean>;
|
|
24
|
+
getActionResult(actionDigest: string): Promise<ActionResult | null>;
|
|
25
|
+
forgetAction(actionDigest: string): Promise<boolean>;
|
|
26
|
+
stats(): Promise<CacheStats>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function digest(bytes: Buffer | Uint8Array | string): string;
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("node:fs");
|
|
4
|
+
const path = require("node:path");
|
|
5
|
+
const koffi = require("koffi");
|
|
6
|
+
|
|
7
|
+
class OnceError extends Error {
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "OnceError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const native = loadNative();
|
|
15
|
+
|
|
16
|
+
class Cache {
|
|
17
|
+
version() {
|
|
18
|
+
return native.once_version() || "";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
digest(bytes) {
|
|
22
|
+
const buffer = toBuffer(bytes);
|
|
23
|
+
const pointer = buffer.length === 0 ? null : buffer;
|
|
24
|
+
return decodeStringResponse(native.once_digest_bytes(pointer, buffer.length));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async putBlob(bytes) {
|
|
28
|
+
const buffer = toBuffer(bytes);
|
|
29
|
+
return decodeRequest(
|
|
30
|
+
native.once_cache_put_blob_json,
|
|
31
|
+
{ bytes: Array.from(buffer) },
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async getBlob(digest) {
|
|
36
|
+
const response = decodeRequest(native.once_cache_get_blob_json, { digest });
|
|
37
|
+
return Buffer.from(response.bytes);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async hasBlob(digest) {
|
|
41
|
+
return decodeRequest(native.once_cache_has_blob_json, { digest });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async putActionResult(result, actionDigest) {
|
|
45
|
+
return decodeRequest(
|
|
46
|
+
native.once_cache_put_action_result_json,
|
|
47
|
+
{
|
|
48
|
+
action_digest: actionDigest,
|
|
49
|
+
result: actionResultToNative(result),
|
|
50
|
+
},
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async getActionResult(actionDigest) {
|
|
55
|
+
const result = decodeRequest(
|
|
56
|
+
native.once_cache_get_action_result_json,
|
|
57
|
+
{ action_digest: actionDigest },
|
|
58
|
+
);
|
|
59
|
+
return result === null ? null : actionResultFromNative(result);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async forgetAction(actionDigest) {
|
|
63
|
+
return decodeRequest(
|
|
64
|
+
native.once_cache_forget_action_json,
|
|
65
|
+
{ action_digest: actionDigest },
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async stats() {
|
|
70
|
+
const stats = decodeRequest(native.once_cache_stats_json, {});
|
|
71
|
+
return {
|
|
72
|
+
blobCount: stats.blob_count,
|
|
73
|
+
blobBytes: stats.blob_bytes,
|
|
74
|
+
actionCount: stats.action_count,
|
|
75
|
+
actionBytes: stats.action_bytes,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function digest(bytes) {
|
|
81
|
+
return new Cache().digest(bytes);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function decodeStringResponse(pointer) {
|
|
85
|
+
return decodeResponse(pointer);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function decodeRequest(fn, request) {
|
|
89
|
+
const pointer = fn(JSON.stringify(request));
|
|
90
|
+
return decodeResponse(pointer);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function decodeResponse(pointer) {
|
|
94
|
+
if (pointer == null) {
|
|
95
|
+
throw new OnceError("native Once function returned null");
|
|
96
|
+
}
|
|
97
|
+
const response = JSON.parse(pointer);
|
|
98
|
+
if (response.status === "ok") {
|
|
99
|
+
return response.value;
|
|
100
|
+
}
|
|
101
|
+
throw new OnceError(response.message || "Once native call failed");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function actionResultToNative(result) {
|
|
105
|
+
return {
|
|
106
|
+
exit_code: result.exitCode,
|
|
107
|
+
stdout: result.stdout ?? null,
|
|
108
|
+
stderr: result.stderr ?? null,
|
|
109
|
+
outputs: result.outputs ?? {},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function actionResultFromNative(result) {
|
|
114
|
+
return {
|
|
115
|
+
exitCode: result.exit_code,
|
|
116
|
+
stdout: result.stdout ?? null,
|
|
117
|
+
stderr: result.stderr ?? null,
|
|
118
|
+
outputs: result.outputs ?? {},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function toBuffer(bytes) {
|
|
123
|
+
if (Buffer.isBuffer(bytes)) {
|
|
124
|
+
return bytes;
|
|
125
|
+
}
|
|
126
|
+
if (bytes instanceof Uint8Array) {
|
|
127
|
+
return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
128
|
+
}
|
|
129
|
+
if (typeof bytes === "string") {
|
|
130
|
+
return Buffer.from(bytes);
|
|
131
|
+
}
|
|
132
|
+
throw new TypeError("bytes must be a Buffer, Uint8Array, or string");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function loadNative() {
|
|
136
|
+
const library = koffi.load(resolveLibraryPath());
|
|
137
|
+
const onceStringFree = library.func("void once_string_free(void *value)");
|
|
138
|
+
const onceString = koffi.disposable("OnceString", "str", onceStringFree);
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
once_version: library.func("once_version", onceString, []),
|
|
142
|
+
once_digest_bytes: library.func("once_digest_bytes", onceString, [
|
|
143
|
+
"const void *",
|
|
144
|
+
"size_t",
|
|
145
|
+
]),
|
|
146
|
+
once_cache_put_blob_json: library.func(
|
|
147
|
+
"once_cache_put_blob_json",
|
|
148
|
+
onceString,
|
|
149
|
+
["str"],
|
|
150
|
+
),
|
|
151
|
+
once_cache_get_blob_json: library.func(
|
|
152
|
+
"once_cache_get_blob_json",
|
|
153
|
+
onceString,
|
|
154
|
+
["str"],
|
|
155
|
+
),
|
|
156
|
+
once_cache_has_blob_json: library.func(
|
|
157
|
+
"once_cache_has_blob_json",
|
|
158
|
+
onceString,
|
|
159
|
+
["str"],
|
|
160
|
+
),
|
|
161
|
+
once_cache_put_action_result_json: library.func(
|
|
162
|
+
"once_cache_put_action_result_json",
|
|
163
|
+
onceString,
|
|
164
|
+
["str"],
|
|
165
|
+
),
|
|
166
|
+
once_cache_get_action_result_json: library.func(
|
|
167
|
+
"once_cache_get_action_result_json",
|
|
168
|
+
onceString,
|
|
169
|
+
["str"],
|
|
170
|
+
),
|
|
171
|
+
once_cache_forget_action_json: library.func(
|
|
172
|
+
"once_cache_forget_action_json",
|
|
173
|
+
onceString,
|
|
174
|
+
["str"],
|
|
175
|
+
),
|
|
176
|
+
once_cache_stats_json: library.func("once_cache_stats_json", onceString, [
|
|
177
|
+
"str",
|
|
178
|
+
]),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function resolveLibraryPath() {
|
|
183
|
+
if (process.env.ONCE_LIBRARY_PATH) {
|
|
184
|
+
return process.env.ONCE_LIBRARY_PATH;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const candidate = path.join(
|
|
188
|
+
__dirname,
|
|
189
|
+
"..",
|
|
190
|
+
"prebuilds",
|
|
191
|
+
`${process.platform}-${process.arch}`,
|
|
192
|
+
libraryName(),
|
|
193
|
+
);
|
|
194
|
+
if (fs.existsSync(candidate)) {
|
|
195
|
+
return candidate;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
throw new OnceError(
|
|
199
|
+
`missing native Once library for ${process.platform}-${process.arch}; ` +
|
|
200
|
+
"set ONCE_LIBRARY_PATH or install a package that includes this platform",
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function libraryName() {
|
|
205
|
+
switch (process.platform) {
|
|
206
|
+
case "darwin":
|
|
207
|
+
return "libonce.dylib";
|
|
208
|
+
case "win32":
|
|
209
|
+
return "once.dll";
|
|
210
|
+
default:
|
|
211
|
+
return "libonce.so";
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
module.exports = {
|
|
216
|
+
Cache,
|
|
217
|
+
OnceError,
|
|
218
|
+
digest,
|
|
219
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "buildonce",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "JavaScript SDK for Once cache access",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/tuist/once.git",
|
|
9
|
+
"directory": "packages/js"
|
|
10
|
+
},
|
|
11
|
+
"main": "lib/index.js",
|
|
12
|
+
"types": "lib/index.d.ts",
|
|
13
|
+
"files": [
|
|
14
|
+
"lib",
|
|
15
|
+
"prebuilds"
|
|
16
|
+
],
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=18"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"koffi": "^2.14.0"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"test": "node test.js"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
Binary file
|