mustardscript 0.1.0 → 0.1.2

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 (96) hide show
  1. package/README.md +65 -22
  2. package/SECURITY.md +1 -1
  3. package/dist/index.js +2 -0
  4. package/dist/lib/executor.js +16 -1
  5. package/dist/lib/policy.js +301 -22
  6. package/dist/lib/progress.js +499 -113
  7. package/dist/lib/runtime.js +109 -40
  8. package/dist/lib/structured.js +327 -11
  9. package/dist/native-loader.js +11 -12
  10. package/index.d.ts +54 -6
  11. package/mustard.d.ts +23 -1
  12. package/package.json +34 -25
  13. package/Cargo.lock +0 -1579
  14. package/Cargo.toml +0 -40
  15. package/crates/mustard/Cargo.toml +0 -31
  16. package/crates/mustard/src/cancellation.rs +0 -28
  17. package/crates/mustard/src/diagnostic.rs +0 -145
  18. package/crates/mustard/src/ir.rs +0 -435
  19. package/crates/mustard/src/lib.rs +0 -21
  20. package/crates/mustard/src/limits.rs +0 -22
  21. package/crates/mustard/src/parser/expressions.rs +0 -723
  22. package/crates/mustard/src/parser/mod.rs +0 -115
  23. package/crates/mustard/src/parser/operators.rs +0 -105
  24. package/crates/mustard/src/parser/patterns.rs +0 -123
  25. package/crates/mustard/src/parser/scope.rs +0 -107
  26. package/crates/mustard/src/parser/statements.rs +0 -298
  27. package/crates/mustard/src/parser/tests/acceptance.rs +0 -339
  28. package/crates/mustard/src/parser/tests/mod.rs +0 -2
  29. package/crates/mustard/src/parser/tests/rejections.rs +0 -107
  30. package/crates/mustard/src/runtime/accounting.rs +0 -613
  31. package/crates/mustard/src/runtime/api.rs +0 -192
  32. package/crates/mustard/src/runtime/async_runtime/mod.rs +0 -5
  33. package/crates/mustard/src/runtime/async_runtime/promises.rs +0 -246
  34. package/crates/mustard/src/runtime/async_runtime/reactions.rs +0 -400
  35. package/crates/mustard/src/runtime/async_runtime/scheduler.rs +0 -224
  36. package/crates/mustard/src/runtime/builtins/arrays.rs +0 -1205
  37. package/crates/mustard/src/runtime/builtins/collections.rs +0 -573
  38. package/crates/mustard/src/runtime/builtins/install.rs +0 -501
  39. package/crates/mustard/src/runtime/builtins/intl.rs +0 -553
  40. package/crates/mustard/src/runtime/builtins/mod.rs +0 -25
  41. package/crates/mustard/src/runtime/builtins/objects.rs +0 -405
  42. package/crates/mustard/src/runtime/builtins/primitives.rs +0 -859
  43. package/crates/mustard/src/runtime/builtins/promises.rs +0 -335
  44. package/crates/mustard/src/runtime/builtins/regexp.rs +0 -356
  45. package/crates/mustard/src/runtime/builtins/strings.rs +0 -803
  46. package/crates/mustard/src/runtime/builtins/support.rs +0 -561
  47. package/crates/mustard/src/runtime/bytecode.rs +0 -123
  48. package/crates/mustard/src/runtime/compiler/assignments.rs +0 -690
  49. package/crates/mustard/src/runtime/compiler/bindings.rs +0 -92
  50. package/crates/mustard/src/runtime/compiler/context.rs +0 -46
  51. package/crates/mustard/src/runtime/compiler/control.rs +0 -342
  52. package/crates/mustard/src/runtime/compiler/expressions.rs +0 -372
  53. package/crates/mustard/src/runtime/compiler/mod.rs +0 -173
  54. package/crates/mustard/src/runtime/compiler/statements.rs +0 -459
  55. package/crates/mustard/src/runtime/conversions/boundary.rs +0 -293
  56. package/crates/mustard/src/runtime/conversions/coercions.rs +0 -217
  57. package/crates/mustard/src/runtime/conversions/errors.rs +0 -118
  58. package/crates/mustard/src/runtime/conversions/mod.rs +0 -14
  59. package/crates/mustard/src/runtime/conversions/operators.rs +0 -334
  60. package/crates/mustard/src/runtime/env.rs +0 -355
  61. package/crates/mustard/src/runtime/exceptions.rs +0 -377
  62. package/crates/mustard/src/runtime/gc.rs +0 -595
  63. package/crates/mustard/src/runtime/mod.rs +0 -318
  64. package/crates/mustard/src/runtime/properties.rs +0 -1762
  65. package/crates/mustard/src/runtime/serialization.rs +0 -127
  66. package/crates/mustard/src/runtime/shared.rs +0 -108
  67. package/crates/mustard/src/runtime/snapshot_validation_tests.rs +0 -93
  68. package/crates/mustard/src/runtime/state.rs +0 -652
  69. package/crates/mustard/src/runtime/tests/async_host.rs +0 -104
  70. package/crates/mustard/src/runtime/tests/collections.rs +0 -50
  71. package/crates/mustard/src/runtime/tests/diagnostics.rs +0 -36
  72. package/crates/mustard/src/runtime/tests/exceptions.rs +0 -122
  73. package/crates/mustard/src/runtime/tests/execution.rs +0 -553
  74. package/crates/mustard/src/runtime/tests/gc.rs +0 -533
  75. package/crates/mustard/src/runtime/tests/mod.rs +0 -56
  76. package/crates/mustard/src/runtime/tests/serialization.rs +0 -170
  77. package/crates/mustard/src/runtime/validation/bytecode.rs +0 -484
  78. package/crates/mustard/src/runtime/validation/mod.rs +0 -14
  79. package/crates/mustard/src/runtime/validation/policy.rs +0 -94
  80. package/crates/mustard/src/runtime/validation/snapshot.rs +0 -406
  81. package/crates/mustard/src/runtime/validation/walk.rs +0 -206
  82. package/crates/mustard/src/runtime/vm.rs +0 -1016
  83. package/crates/mustard/src/span.rs +0 -22
  84. package/crates/mustard/src/structured.rs +0 -107
  85. package/crates/mustard-bridge/Cargo.toml +0 -17
  86. package/crates/mustard-bridge/src/codec.rs +0 -46
  87. package/crates/mustard-bridge/src/dto.rs +0 -99
  88. package/crates/mustard-bridge/src/lib.rs +0 -12
  89. package/crates/mustard-bridge/src/operations.rs +0 -142
  90. package/crates/mustard-node/Cargo.toml +0 -24
  91. package/crates/mustard-node/build.rs +0 -3
  92. package/crates/mustard-node/src/lib.rs +0 -236
  93. package/crates/mustard-sidecar/Cargo.toml +0 -21
  94. package/crates/mustard-sidecar/src/lib.rs +0 -134
  95. package/crates/mustard-sidecar/src/main.rs +0 -36
  96. package/dist/install.js +0 -117
@@ -1,236 +0,0 @@
1
- use base64::{Engine as _, engine::general_purpose::STANDARD};
2
- use hmac::{Hmac, Mac};
3
- use rand::random;
4
- use sha2::{Digest, Sha256};
5
- use std::collections::{HashMap, HashSet};
6
- use std::sync::{
7
- Arc, Mutex, OnceLock,
8
- atomic::{AtomicBool, Ordering},
9
- };
10
-
11
- use mustard::CancellationToken;
12
- use mustard_bridge::{
13
- ResumeDto, SnapshotPolicyDto, StartOptionsDto, compile_program_bytes, decode_program,
14
- encode_json, inspect_snapshot_bytes, parse_json, resume_program as bridge_resume_program,
15
- start_program as bridge_start_program,
16
- };
17
- use napi::bindgen_prelude::Buffer;
18
- use napi::{Error, Result};
19
- use napi_derive::napi;
20
-
21
- type HmacSha256 = Hmac<Sha256>;
22
-
23
- fn cancellation_tokens() -> &'static Mutex<HashMap<String, Arc<AtomicBool>>> {
24
- static TOKENS: OnceLock<Mutex<HashMap<String, Arc<AtomicBool>>>> = OnceLock::new();
25
- TOKENS.get_or_init(|| Mutex::new(HashMap::new()))
26
- }
27
-
28
- fn used_progress_snapshots() -> &'static Mutex<HashSet<String>> {
29
- static TOKENS: OnceLock<Mutex<HashSet<String>>> = OnceLock::new();
30
- TOKENS.get_or_init(|| Mutex::new(HashSet::new()))
31
- }
32
-
33
- fn next_cancellation_token_id(tokens: &HashMap<String, Arc<AtomicBool>>) -> String {
34
- loop {
35
- let candidate = format!("cancel-{:032x}", random::<u128>());
36
- if !tokens.contains_key(&candidate) {
37
- return candidate;
38
- }
39
- }
40
- }
41
-
42
- fn lookup_cancellation_token(token_id: Option<String>) -> Result<Option<CancellationToken>> {
43
- let Some(token_id) = token_id else {
44
- return Ok(None);
45
- };
46
- let tokens = cancellation_tokens()
47
- .lock()
48
- .map_err(|_| to_napi_error("cancellation token registry is poisoned"))?;
49
- let shared = tokens
50
- .get(&token_id)
51
- .cloned()
52
- .ok_or_else(|| to_napi_error(format!("unknown cancellation token `{token_id}`")))?;
53
- Ok(Some(CancellationToken::from_shared(shared)))
54
- }
55
-
56
- fn to_napi_error(error: impl std::fmt::Display) -> Error {
57
- Error::from_reason(error.to_string())
58
- }
59
-
60
- fn snapshot_identity_hex(snapshot: &[u8]) -> String {
61
- let digest = Sha256::digest(snapshot);
62
- let mut encoded = String::with_capacity(digest.len() * 2);
63
- for byte in digest {
64
- use std::fmt::Write as _;
65
- let _ = write!(&mut encoded, "{byte:02x}");
66
- }
67
- encoded
68
- }
69
-
70
- fn snapshot_key_digest_hex(snapshot_key: &[u8]) -> String {
71
- let digest = Sha256::digest(snapshot_key);
72
- let mut encoded = String::with_capacity(digest.len() * 2);
73
- for byte in digest {
74
- use std::fmt::Write as _;
75
- let _ = write!(&mut encoded, "{byte:02x}");
76
- }
77
- encoded
78
- }
79
-
80
- fn encode_snapshot_token(snapshot_id: &str, snapshot_key: &[u8]) -> Result<String> {
81
- let mut mac = HmacSha256::new_from_slice(snapshot_key)
82
- .map_err(|_| to_napi_error("invalid snapshot key"))?;
83
- mac.update(snapshot_id.as_bytes());
84
- let digest = mac.finalize().into_bytes();
85
- let mut token = String::with_capacity(digest.len() * 2);
86
- for byte in digest {
87
- use std::fmt::Write as _;
88
- let _ = write!(&mut token, "{byte:02x}");
89
- }
90
- Ok(token)
91
- }
92
-
93
- fn assert_authenticated_snapshot(snapshot: &[u8], policy: &SnapshotPolicyDto) -> Result<()> {
94
- let snapshot_id = policy
95
- .snapshot_id
96
- .as_deref()
97
- .ok_or_else(|| to_napi_error("raw snapshot restore requires snapshot_id"))?;
98
- let snapshot_key_base64 = policy
99
- .snapshot_key_base64
100
- .as_deref()
101
- .ok_or_else(|| to_napi_error("raw snapshot restore requires snapshot_key_base64"))?;
102
- let snapshot_token = policy
103
- .snapshot_token
104
- .as_deref()
105
- .ok_or_else(|| to_napi_error("raw snapshot restore requires snapshot_token"))?;
106
- let snapshot_key_digest = policy
107
- .snapshot_key_digest
108
- .as_deref()
109
- .ok_or_else(|| to_napi_error("raw snapshot restore requires snapshot_key_digest"))?;
110
- let snapshot_key = STANDARD
111
- .decode(snapshot_key_base64)
112
- .map_err(|_| to_napi_error("snapshot_key_base64 must be valid base64"))?;
113
- let expected_snapshot_id = snapshot_identity_hex(snapshot);
114
- if expected_snapshot_id != snapshot_id {
115
- return Err(to_napi_error(
116
- "raw snapshot restore rejected a tampered or unauthenticated snapshot",
117
- ));
118
- }
119
- if snapshot_key_digest_hex(&snapshot_key) != snapshot_key_digest {
120
- return Err(to_napi_error(
121
- "raw snapshot restore rejected a mismatched snapshot key digest",
122
- ));
123
- }
124
- let expected = encode_snapshot_token(snapshot_id, &snapshot_key)?;
125
- if expected != snapshot_token {
126
- return Err(to_napi_error(
127
- "raw snapshot restore rejected a tampered or unauthenticated snapshot",
128
- ));
129
- }
130
- Ok(())
131
- }
132
-
133
- #[napi]
134
- pub fn compile_program(source: String) -> Result<Buffer> {
135
- let bytes = compile_program_bytes(&source).map_err(to_napi_error)?;
136
- Ok(Buffer::from(bytes))
137
- }
138
-
139
- #[napi]
140
- pub fn create_cancellation_token() -> Result<String> {
141
- let mut tokens = cancellation_tokens()
142
- .lock()
143
- .map_err(|_| to_napi_error("cancellation token registry is poisoned"))?;
144
- let token_id = next_cancellation_token_id(&tokens);
145
- tokens.insert(token_id.clone(), Arc::new(AtomicBool::new(false)));
146
- Ok(token_id)
147
- }
148
-
149
- #[napi]
150
- pub fn cancel_cancellation_token(token_id: String) -> Result<()> {
151
- let tokens = cancellation_tokens()
152
- .lock()
153
- .map_err(|_| to_napi_error("cancellation token registry is poisoned"))?;
154
- let token = tokens
155
- .get(&token_id)
156
- .ok_or_else(|| to_napi_error(format!("unknown cancellation token `{token_id}`")))?;
157
- token.store(true, Ordering::SeqCst);
158
- Ok(())
159
- }
160
-
161
- #[napi]
162
- pub fn release_cancellation_token(token_id: String) -> Result<()> {
163
- let mut tokens = cancellation_tokens()
164
- .lock()
165
- .map_err(|_| to_napi_error("cancellation token registry is poisoned"))?;
166
- tokens.remove(&token_id);
167
- Ok(())
168
- }
169
-
170
- #[napi]
171
- pub fn snapshot_identity(snapshot: Buffer) -> Result<String> {
172
- Ok(snapshot_identity_hex(snapshot.as_ref()))
173
- }
174
-
175
- #[napi]
176
- pub fn is_progress_snapshot_used(snapshot_identity: String) -> Result<bool> {
177
- let tokens = used_progress_snapshots()
178
- .lock()
179
- .map_err(|_| to_napi_error("progress snapshot registry is poisoned"))?;
180
- Ok(tokens.contains(&snapshot_identity))
181
- }
182
-
183
- #[napi]
184
- pub fn claim_progress_snapshot(snapshot_identity: String) -> Result<bool> {
185
- let mut tokens = used_progress_snapshots()
186
- .lock()
187
- .map_err(|_| to_napi_error("progress snapshot registry is poisoned"))?;
188
- Ok(tokens.insert(snapshot_identity))
189
- }
190
-
191
- #[napi]
192
- pub fn release_progress_snapshot(snapshot_identity: String) -> Result<()> {
193
- let mut tokens = used_progress_snapshots()
194
- .lock()
195
- .map_err(|_| to_napi_error("progress snapshot registry is poisoned"))?;
196
- tokens.remove(&snapshot_identity);
197
- Ok(())
198
- }
199
-
200
- #[napi]
201
- pub fn start_program(
202
- program: Buffer,
203
- options_json: String,
204
- cancellation_token_id: Option<String>,
205
- ) -> Result<String> {
206
- let program = decode_program(program.as_ref()).map_err(to_napi_error)?;
207
- let options: StartOptionsDto = parse_json(&options_json).map_err(to_napi_error)?;
208
- let cancellation_token = lookup_cancellation_token(cancellation_token_id)?;
209
- let step =
210
- bridge_start_program(&program, options, cancellation_token).map_err(to_napi_error)?;
211
- encode_json(&step).map_err(to_napi_error)
212
- }
213
-
214
- #[napi]
215
- pub fn inspect_snapshot(snapshot: Buffer, policy_json: String) -> Result<String> {
216
- let policy: SnapshotPolicyDto = parse_json(&policy_json).map_err(to_napi_error)?;
217
- assert_authenticated_snapshot(snapshot.as_ref(), &policy)?;
218
- let inspection = inspect_snapshot_bytes(snapshot.as_ref(), policy).map_err(to_napi_error)?;
219
- encode_json(&inspection).map_err(to_napi_error)
220
- }
221
-
222
- #[napi]
223
- pub fn resume_program(
224
- snapshot: Buffer,
225
- payload_json: String,
226
- policy_json: String,
227
- cancellation_token_id: Option<String>,
228
- ) -> Result<String> {
229
- let payload: ResumeDto = parse_json(&payload_json).map_err(to_napi_error)?;
230
- let policy: SnapshotPolicyDto = parse_json(&policy_json).map_err(to_napi_error)?;
231
- assert_authenticated_snapshot(snapshot.as_ref(), &policy)?;
232
- let cancellation_token = lookup_cancellation_token(cancellation_token_id)?;
233
- let step = bridge_resume_program(snapshot.as_ref(), payload, policy, cancellation_token)
234
- .map_err(to_napi_error)?;
235
- encode_json(&step).map_err(to_napi_error)
236
- }
@@ -1,21 +0,0 @@
1
- [package]
2
- name = "mustard-sidecar"
3
- version.workspace = true
4
- edition.workspace = true
5
- license.workspace = true
6
- authors.workspace = true
7
- repository.workspace = true
8
- description = "Sidecar process for the MustardScript runtime"
9
-
10
- [dependencies]
11
- anyhow.workspace = true
12
- mustard-bridge = { path = "../mustard-bridge" }
13
- serde.workspace = true
14
- serde_json.workspace = true
15
- tokio.workspace = true
16
-
17
- [dev-dependencies]
18
- base64 = "0.22.1"
19
- hmac.workspace = true
20
- mustard = { path = "../mustard" }
21
- sha2.workspace = true
@@ -1,134 +0,0 @@
1
- use anyhow::{Context, Result};
2
- use mustard_bridge::{
3
- ResumeDto, SnapshotPolicyDto, StartOptionsDto, StepDto, compile_program_bytes, decode_base64,
4
- decode_program_base64, encode_bytes_base64, resume_program, start_program,
5
- };
6
- use serde::{Deserialize, Serialize};
7
-
8
- pub const PROTOCOL_VERSION: u32 = 1;
9
- pub const MAX_REQUEST_LINE_BYTES: usize = 1024 * 1024;
10
-
11
- #[derive(Debug, Serialize, Deserialize)]
12
- #[serde(tag = "method", rename_all = "snake_case")]
13
- enum Request {
14
- Compile {
15
- protocol_version: u32,
16
- id: u64,
17
- source: String,
18
- },
19
- Start {
20
- protocol_version: u32,
21
- id: u64,
22
- program_base64: String,
23
- options: StartOptionsDto,
24
- },
25
- Resume {
26
- protocol_version: u32,
27
- id: u64,
28
- snapshot_base64: String,
29
- policy: Box<SnapshotPolicyDto>,
30
- payload: Box<ResumeDto>,
31
- },
32
- }
33
-
34
- #[derive(Debug, Serialize, Deserialize)]
35
- struct Response {
36
- protocol_version: u32,
37
- id: u64,
38
- ok: bool,
39
- #[serde(skip_serializing_if = "Option::is_none")]
40
- result: Option<ResponsePayload>,
41
- #[serde(skip_serializing_if = "Option::is_none")]
42
- error: Option<String>,
43
- }
44
-
45
- #[derive(Debug, Serialize, Deserialize)]
46
- #[serde(tag = "kind", rename_all = "snake_case")]
47
- enum ResponsePayload {
48
- Program { program_base64: String },
49
- Step { step: StepDto },
50
- }
51
-
52
- fn handle(request: Request) -> Response {
53
- let id = match &request {
54
- Request::Compile { id, .. } | Request::Start { id, .. } | Request::Resume { id, .. } => *id,
55
- };
56
- let protocol_version = match &request {
57
- Request::Compile {
58
- protocol_version, ..
59
- }
60
- | Request::Start {
61
- protocol_version, ..
62
- }
63
- | Request::Resume {
64
- protocol_version, ..
65
- } => *protocol_version,
66
- };
67
-
68
- if protocol_version != PROTOCOL_VERSION {
69
- return Response {
70
- protocol_version: PROTOCOL_VERSION,
71
- id,
72
- ok: false,
73
- result: None,
74
- error: Some(format!(
75
- "unsupported sidecar protocol version {protocol_version}; expected {PROTOCOL_VERSION}"
76
- )),
77
- };
78
- }
79
-
80
- let result: Result<ResponsePayload> = match request {
81
- Request::Compile { source, .. } => (|| {
82
- let bytes = compile_program_bytes(&source)?;
83
- Ok(ResponsePayload::Program {
84
- program_base64: encode_bytes_base64(&bytes),
85
- })
86
- })(),
87
- Request::Start {
88
- program_base64,
89
- options,
90
- ..
91
- } => (|| {
92
- let program = decode_program_base64(&program_base64)?;
93
- let step = start_program(&program, options, None)?;
94
- Ok(ResponsePayload::Step { step })
95
- })(),
96
- Request::Resume {
97
- snapshot_base64,
98
- policy,
99
- payload,
100
- ..
101
- } => (|| {
102
- let snapshot_bytes = decode_base64(&snapshot_base64)?;
103
- let step = resume_program(&snapshot_bytes, *payload, *policy, None)?;
104
- Ok(ResponsePayload::Step { step })
105
- })(),
106
- };
107
-
108
- match result {
109
- Ok(result) => Response {
110
- protocol_version: PROTOCOL_VERSION,
111
- id,
112
- ok: true,
113
- result: Some(result),
114
- error: None,
115
- },
116
- Err(error) => Response {
117
- protocol_version: PROTOCOL_VERSION,
118
- id,
119
- ok: false,
120
- result: None,
121
- error: Some(error.to_string()),
122
- },
123
- }
124
- }
125
-
126
- pub fn handle_request_line(line: &str) -> Result<Option<String>> {
127
- if line.trim().is_empty() {
128
- return Ok(None);
129
- }
130
- let request: Request = serde_json::from_str(line).context("invalid request")?;
131
- let response = handle(request);
132
- let encoded = serde_json::to_string(&response).context("failed to encode response")?;
133
- Ok(Some(encoded))
134
- }
@@ -1,36 +0,0 @@
1
- use std::io::{self, BufRead, Write};
2
-
3
- use anyhow::{Context, Result};
4
- use mustard_sidecar::{MAX_REQUEST_LINE_BYTES, handle_request_line};
5
-
6
- fn main() -> Result<()> {
7
- let stdin = io::stdin();
8
- let mut stdin = stdin.lock();
9
- let mut stdout = io::stdout().lock();
10
- let mut line = Vec::new();
11
- loop {
12
- line.clear();
13
- let read = stdin
14
- .read_until(b'\n', &mut line)
15
- .context("failed to read request line")?;
16
- if read == 0 {
17
- break;
18
- }
19
- if line.len() > MAX_REQUEST_LINE_BYTES + 1 {
20
- anyhow::bail!("request line exceeds maximum size of {MAX_REQUEST_LINE_BYTES} bytes");
21
- }
22
- if line.ends_with(b"\n") {
23
- line.pop();
24
- if line.ends_with(b"\r") {
25
- line.pop();
26
- }
27
- }
28
- let line = String::from_utf8(line.clone()).context("request line must be valid utf-8")?;
29
- let Some(response) = handle_request_line(&line)? else {
30
- continue;
31
- };
32
- writeln!(&mut stdout, "{response}").context("failed to terminate response line")?;
33
- stdout.flush().context("failed to flush response")?;
34
- }
35
- Ok(())
36
- }
package/dist/install.js DELETED
@@ -1,117 +0,0 @@
1
- 'use strict';
2
-
3
- const { execFileSync } = require('node:child_process');
4
- const fs = require('node:fs');
5
- const path = require('node:path');
6
- const {
7
- resolvePrebuiltPackage,
8
- getCurrentPrebuiltTarget,
9
- getLocalBuildOutputFile,
10
- } = require('./native-loader.js');
11
-
12
- const packageRoot = path.basename(__dirname) === 'dist' ? path.dirname(__dirname) : __dirname;
13
-
14
- let prebuilt = null;
15
- let prebuiltError = null;
16
- try {
17
- prebuilt = resolvePrebuiltPackage();
18
- } catch (error) {
19
- prebuiltError = error;
20
- }
21
-
22
- if (prebuilt) {
23
- process.stdout.write(
24
- `MustardScript: using optional prebuilt addon from ${prebuilt.packageName}\n`,
25
- );
26
- process.exit(0);
27
- }
28
-
29
- const target = getCurrentPrebuiltTarget();
30
- if (prebuiltError) {
31
- process.stdout.write(
32
- `MustardScript: ignoring invalid optional prebuilt for ${target?.platformArchABI ?? `${process.platform}-${process.arch}`}: ${prebuiltError.message}\n`,
33
- );
34
- }
35
- if (target) {
36
- process.stdout.write(
37
- `MustardScript: no installed prebuilt for ${target.platformArchABI}; building from source\n`,
38
- );
39
- } else {
40
- process.stdout.write(
41
- `MustardScript: no configured prebuilt for ${process.platform}-${process.arch}; building from source\n`,
42
- );
43
- }
44
-
45
- function nativeLibraryExtension(platform) {
46
- switch (platform) {
47
- case 'win32':
48
- return '.dll';
49
- case 'darwin':
50
- return '.dylib';
51
- default:
52
- return '.so';
53
- }
54
- }
55
-
56
- function parseCargoArtifactPath(output) {
57
- const expectedExtension = nativeLibraryExtension(process.platform);
58
- let artifactPath = null;
59
- for (const line of output.split(/\r?\n/u)) {
60
- if (line.trim() === '') {
61
- continue;
62
- }
63
- let event;
64
- try {
65
- event = JSON.parse(line);
66
- } catch {
67
- continue;
68
- }
69
- if (event.reason !== 'compiler-artifact') {
70
- continue;
71
- }
72
- if (event.target?.name !== 'mustard_node') {
73
- continue;
74
- }
75
- if (
76
- !Array.isArray(event.target?.crate_types) ||
77
- !event.target.crate_types.includes('cdylib')
78
- ) {
79
- continue;
80
- }
81
- if (!Array.isArray(event.filenames)) {
82
- continue;
83
- }
84
- const filename = event.filenames.find((entry) => entry.endsWith(expectedExtension));
85
- if (filename) {
86
- artifactPath = filename;
87
- }
88
- }
89
- if (!artifactPath) {
90
- throw new Error('MustardScript: cargo did not report a native cdylib artifact');
91
- }
92
- return artifactPath;
93
- }
94
-
95
- const cargo = process.env.CARGO || 'cargo';
96
- const cargoOutput = execFileSync(
97
- cargo,
98
- [
99
- 'build',
100
- '--release',
101
- '--manifest-path',
102
- 'crates/mustard-node/Cargo.toml',
103
- '--message-format',
104
- 'json-render-diagnostics',
105
- ],
106
- {
107
- cwd: packageRoot,
108
- encoding: 'utf8',
109
- stdio: ['ignore', 'pipe', 'inherit'],
110
- env: process.env,
111
- },
112
- );
113
-
114
- const artifactPath = parseCargoArtifactPath(cargoOutput);
115
- const outputPath = path.join(packageRoot, getLocalBuildOutputFile());
116
- fs.copyFileSync(artifactPath, outputPath);
117
- process.stdout.write(`MustardScript: built local addon at ${path.basename(outputPath)}\n`);