@rip-lang/server 1.3.114 → 1.3.116
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 +435 -622
- package/api.rip +4 -4
- package/control/cli.rip +221 -1
- package/control/control.rip +9 -0
- package/control/lifecycle.rip +6 -1
- package/control/watchers.rip +10 -0
- package/control/workers.rip +9 -5
- package/default.rip +3 -1
- package/docs/READ_VALIDATORS.md +656 -0
- package/docs/edge/CONFIG_LIFECYCLE.md +70 -32
- package/docs/edge/CONTRACTS.md +60 -69
- package/docs/edge/EDGEFILE_CONTRACT.md +258 -29
- package/docs/edge/M0B_REVIEW_NOTES.md +5 -5
- package/edge/config.rip +584 -52
- package/edge/forwarding.rip +6 -2
- package/edge/metrics.rip +19 -1
- package/edge/registry.rip +29 -3
- package/edge/router.rip +138 -0
- package/edge/runtime.rip +98 -0
- package/edge/static.rip +69 -0
- package/edge/tls.rip +23 -0
- package/edge/upstream.rip +272 -0
- package/edge/verify.rip +73 -0
- package/middleware.rip +3 -3
- package/package.json +2 -2
- package/server.rip +775 -393
- package/tests/control.rip +18 -0
- package/tests/edgefile.rip +165 -0
- package/tests/metrics.rip +16 -0
- package/tests/proxy.rip +22 -1
- package/tests/registry.rip +27 -0
- package/tests/router.rip +101 -0
- package/tests/runtime_entrypoints.rip +16 -0
- package/tests/servers.rip +262 -0
- package/tests/static.rip +64 -0
- package/tests/streams_clienthello.rip +108 -0
- package/tests/streams_index.rip +53 -0
- package/tests/streams_pipe.rip +70 -0
- package/tests/streams_router.rip +39 -0
- package/tests/streams_runtime.rip +38 -0
- package/tests/streams_upstream.rip +34 -0
- package/tests/upstream.rip +191 -0
- package/tests/verify.rip +148 -0
- package/tests/watchers.rip +15 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# edge/upstream.rip — upstream pools, health checks, and retry helpers
|
|
3
|
+
# ==============================================================================
|
|
4
|
+
|
|
5
|
+
DEFAULT_HEALTH =
|
|
6
|
+
path: '/health'
|
|
7
|
+
intervalMs: 5000
|
|
8
|
+
timeoutMs: 2000
|
|
9
|
+
unhealthyThreshold: 3
|
|
10
|
+
healthyThreshold: 1
|
|
11
|
+
|
|
12
|
+
DEFAULT_RETRY =
|
|
13
|
+
attempts: 2
|
|
14
|
+
retryOn: [502, 503, 504]
|
|
15
|
+
retryMethods: ['GET', 'HEAD', 'OPTIONS']
|
|
16
|
+
backoffMs: 100
|
|
17
|
+
|
|
18
|
+
DEFAULT_CIRCUIT =
|
|
19
|
+
errorThreshold: 0.5
|
|
20
|
+
minRequests: 10
|
|
21
|
+
cooldownMs: 30000
|
|
22
|
+
jitterRatio: 0.3
|
|
23
|
+
|
|
24
|
+
toArray = (value, fallback = []) ->
|
|
25
|
+
if Array.isArray(value) then value else fallback
|
|
26
|
+
|
|
27
|
+
toPositiveInt = (value, fallback) ->
|
|
28
|
+
return fallback unless value?
|
|
29
|
+
n = parseInt(String(value))
|
|
30
|
+
if Number.isFinite(n) and n >= 0 then n else fallback
|
|
31
|
+
|
|
32
|
+
toPositiveFloat = (value, fallback) ->
|
|
33
|
+
return fallback unless value?
|
|
34
|
+
n = parseFloat(String(value))
|
|
35
|
+
if Number.isFinite(n) and n >= 0 then n else fallback
|
|
36
|
+
|
|
37
|
+
mergeHealth = (config = {}) ->
|
|
38
|
+
path: if typeof config.path is 'string' and config.path.startsWith('/') then config.path else DEFAULT_HEALTH.path
|
|
39
|
+
intervalMs: toPositiveInt(config.intervalMs, DEFAULT_HEALTH.intervalMs)
|
|
40
|
+
timeoutMs: toPositiveInt(config.timeoutMs, DEFAULT_HEALTH.timeoutMs)
|
|
41
|
+
unhealthyThreshold: Math.max(1, toPositiveInt(config.unhealthyThreshold, DEFAULT_HEALTH.unhealthyThreshold))
|
|
42
|
+
healthyThreshold: Math.max(1, toPositiveInt(config.healthyThreshold, DEFAULT_HEALTH.healthyThreshold))
|
|
43
|
+
|
|
44
|
+
mergeRetry = (config = {}) ->
|
|
45
|
+
attempts: Math.max(1, toPositiveInt(config.attempts, DEFAULT_RETRY.attempts))
|
|
46
|
+
retryOn: toArray(config.retryOn, DEFAULT_RETRY.retryOn).map((n) -> parseInt(String(n))).filter(Number.isFinite)
|
|
47
|
+
retryMethods: toArray(config.retryMethods, DEFAULT_RETRY.retryMethods).map((m) -> String(m).toUpperCase())
|
|
48
|
+
backoffMs: toPositiveInt(config.backoffMs, DEFAULT_RETRY.backoffMs)
|
|
49
|
+
|
|
50
|
+
mergeCircuit = (config = {}) ->
|
|
51
|
+
errorThreshold: Math.min(1, Math.max(0, toPositiveFloat(config.errorThreshold, DEFAULT_CIRCUIT.errorThreshold)))
|
|
52
|
+
minRequests: Math.max(1, toPositiveInt(config.minRequests, DEFAULT_CIRCUIT.minRequests))
|
|
53
|
+
cooldownMs: Math.max(1, toPositiveInt(config.cooldownMs, DEFAULT_CIRCUIT.cooldownMs))
|
|
54
|
+
jitterRatio: Math.min(1, Math.max(0, toPositiveFloat(config.jitterRatio, DEFAULT_CIRCUIT.jitterRatio)))
|
|
55
|
+
|
|
56
|
+
createHistory = ->
|
|
57
|
+
[]
|
|
58
|
+
|
|
59
|
+
trimHistory = (history, minRequests) ->
|
|
60
|
+
history.shift() while history.length > minRequests
|
|
61
|
+
history
|
|
62
|
+
|
|
63
|
+
recordCircuitResult = (target, success, pool = null) ->
|
|
64
|
+
target.history.push(success)
|
|
65
|
+
trimHistory(target.history, target.circuitConfig.minRequests)
|
|
66
|
+
|
|
67
|
+
return unless target.circuitState is 'closed'
|
|
68
|
+
return unless target.history.length >= target.circuitConfig.minRequests
|
|
69
|
+
failures = target.history.filter((ok) -> not ok).length
|
|
70
|
+
errorRate = failures / target.history.length
|
|
71
|
+
openCircuit(target, pool?.randomFn or Math.random, pool?.nowFn or Date.now) if errorRate >= target.circuitConfig.errorThreshold
|
|
72
|
+
|
|
73
|
+
computeCooldownMs = (target, randomFn = Math.random) ->
|
|
74
|
+
base = target.circuitConfig.cooldownMs
|
|
75
|
+
jitter = base * target.circuitConfig.jitterRatio * randomFn()
|
|
76
|
+
Math.round(base + jitter)
|
|
77
|
+
|
|
78
|
+
openCircuit = (target, randomFn = Math.random, nowFn = Date.now) ->
|
|
79
|
+
target.circuitState = 'open'
|
|
80
|
+
target.circuitOpenedAt = nowFn()
|
|
81
|
+
target.circuitProbeInFlight = false
|
|
82
|
+
target.circuitCooldownMs = computeCooldownMs(target, randomFn)
|
|
83
|
+
|
|
84
|
+
export createUpstreamPool = (options = {}) ->
|
|
85
|
+
upstreams: new Map()
|
|
86
|
+
intervals: new Map()
|
|
87
|
+
fetchFn: options.fetchFn or fetch
|
|
88
|
+
nowFn: options.nowFn or Date.now
|
|
89
|
+
randomFn: options.randomFn or Math.random
|
|
90
|
+
setIntervalFn: options.setIntervalFn or setInterval
|
|
91
|
+
clearIntervalFn: options.clearIntervalFn or clearInterval
|
|
92
|
+
|
|
93
|
+
export normalizeUpstream = (id, config = {}) ->
|
|
94
|
+
timeouts =
|
|
95
|
+
connectMs: toPositiveInt(config.timeouts?.connectMs or config.timeout?.connectMs, 2000)
|
|
96
|
+
readMs: toPositiveInt(config.timeouts?.readMs or config.timeout?.readMs, 30000)
|
|
97
|
+
targets = toArray(config.targets).map (url, idx) ->
|
|
98
|
+
url: url
|
|
99
|
+
targetId: "#{id}:#{idx}"
|
|
100
|
+
healthy: true
|
|
101
|
+
inflight: 0
|
|
102
|
+
lastCheckAt: null
|
|
103
|
+
lastStatus: null
|
|
104
|
+
consecutiveFailures: 0
|
|
105
|
+
consecutiveSuccesses: 0
|
|
106
|
+
latencyEwma: 0
|
|
107
|
+
history: createHistory()
|
|
108
|
+
circuitState: 'closed'
|
|
109
|
+
circuitOpenedAt: null
|
|
110
|
+
circuitCooldownMs: null
|
|
111
|
+
circuitProbeInFlight: false
|
|
112
|
+
health: mergeHealth(config.healthCheck)
|
|
113
|
+
retry: mergeRetry(config.retry)
|
|
114
|
+
timeouts: timeouts
|
|
115
|
+
circuitConfig: mergeCircuit(config.circuit)
|
|
116
|
+
meta: config.meta or {}
|
|
117
|
+
{
|
|
118
|
+
id
|
|
119
|
+
healthCheck: mergeHealth(config.healthCheck)
|
|
120
|
+
retry: mergeRetry(config.retry)
|
|
121
|
+
timeouts: timeouts
|
|
122
|
+
circuit: mergeCircuit(config.circuit)
|
|
123
|
+
targets
|
|
124
|
+
strategy: config.strategy or 'least-inflight'
|
|
125
|
+
meta: config.meta or {}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export addUpstream = (pool, id, config = {}) ->
|
|
129
|
+
upstream = normalizeUpstream(id, config)
|
|
130
|
+
pool.upstreams.set(id, upstream)
|
|
131
|
+
upstream
|
|
132
|
+
|
|
133
|
+
export getUpstream = (pool, id) ->
|
|
134
|
+
pool.upstreams.get(id) ?? null
|
|
135
|
+
|
|
136
|
+
export removeUpstream = (pool, id) ->
|
|
137
|
+
stopHealthCheck(pool, id)
|
|
138
|
+
pool.upstreams.delete(id)
|
|
139
|
+
|
|
140
|
+
export listUpstreams = (pool) ->
|
|
141
|
+
Array.from(pool.upstreams.values())
|
|
142
|
+
|
|
143
|
+
export selectTarget = (upstream, nowFn = Date.now) ->
|
|
144
|
+
return null unless upstream?.targets?.length
|
|
145
|
+
candidates = upstream.targets.filter (target) ->
|
|
146
|
+
return false unless target.healthy
|
|
147
|
+
if target.circuitState is 'open'
|
|
148
|
+
cooldown = target.circuitCooldownMs or target.circuitConfig.cooldownMs
|
|
149
|
+
if target.circuitOpenedAt? and nowFn() - target.circuitOpenedAt >= cooldown
|
|
150
|
+
target.circuitState = 'half-open'
|
|
151
|
+
target.circuitProbeInFlight = false
|
|
152
|
+
else
|
|
153
|
+
return false
|
|
154
|
+
if target.circuitState is 'half-open'
|
|
155
|
+
return false if target.circuitProbeInFlight
|
|
156
|
+
true
|
|
157
|
+
return null unless candidates.length
|
|
158
|
+
best = candidates[0]
|
|
159
|
+
for candidate in candidates[1..]
|
|
160
|
+
better = false
|
|
161
|
+
if candidate.inflight < best.inflight
|
|
162
|
+
better = true
|
|
163
|
+
else if candidate.inflight is best.inflight and candidate.latencyEwma < best.latencyEwma
|
|
164
|
+
better = true
|
|
165
|
+
else if candidate.inflight is best.inflight and candidate.latencyEwma is best.latencyEwma and candidate.targetId < best.targetId
|
|
166
|
+
better = true
|
|
167
|
+
best = candidate if better
|
|
168
|
+
best
|
|
169
|
+
|
|
170
|
+
export markTargetBusy = (target) ->
|
|
171
|
+
target.inflight++
|
|
172
|
+
target.circuitProbeInFlight = true if target.circuitState is 'half-open'
|
|
173
|
+
target
|
|
174
|
+
|
|
175
|
+
export releaseTarget = (target, latencyMs = 0, success = true, pool = null) ->
|
|
176
|
+
target.inflight = Math.max(0, target.inflight - 1)
|
|
177
|
+
target.latencyEwma = if target.latencyEwma > 0 then (target.latencyEwma * 0.8) + (latencyMs * 0.2) else latencyMs
|
|
178
|
+
target.circuitProbeInFlight = false if target.circuitState is 'half-open'
|
|
179
|
+
|
|
180
|
+
if success
|
|
181
|
+
if target.circuitState is 'half-open'
|
|
182
|
+
target.circuitState = 'closed'
|
|
183
|
+
target.circuitOpenedAt = null
|
|
184
|
+
target.circuitCooldownMs = null
|
|
185
|
+
target.history.length = 0
|
|
186
|
+
recordCircuitResult(target, true, pool)
|
|
187
|
+
else
|
|
188
|
+
if target.circuitState is 'half-open'
|
|
189
|
+
openCircuit(target, pool?.randomFn or Math.random, pool?.nowFn or Date.now)
|
|
190
|
+
else
|
|
191
|
+
recordCircuitResult(target, false, pool)
|
|
192
|
+
target
|
|
193
|
+
|
|
194
|
+
export shouldRetry = (retryConfig, method, status, bodyStarted = false) ->
|
|
195
|
+
return false if bodyStarted
|
|
196
|
+
cfg = mergeRetry(retryConfig)
|
|
197
|
+
return false unless cfg.retryMethods.includes(String(method or 'GET').toUpperCase())
|
|
198
|
+
cfg.retryOn.includes(status)
|
|
199
|
+
|
|
200
|
+
export computeRetryDelayMs = (retryConfig, attempt, randomFn = Math.random) ->
|
|
201
|
+
cfg = mergeRetry(retryConfig)
|
|
202
|
+
base = cfg.backoffMs * Math.max(1, attempt)
|
|
203
|
+
jitter = cfg.backoffMs * 0.3 * randomFn()
|
|
204
|
+
Math.round(base + jitter)
|
|
205
|
+
|
|
206
|
+
export updateTargetHealth = (target, healthy, pool = null) ->
|
|
207
|
+
target.lastCheckAt = (pool?.nowFn or Date.now)()
|
|
208
|
+
if healthy
|
|
209
|
+
target.consecutiveFailures = 0
|
|
210
|
+
target.consecutiveSuccesses++
|
|
211
|
+
if not target.healthy and target.consecutiveSuccesses >= target.health.healthyThreshold
|
|
212
|
+
target.healthy = true
|
|
213
|
+
if target.circuitState in ['open', 'half-open']
|
|
214
|
+
target.circuitState = 'closed'
|
|
215
|
+
target.circuitOpenedAt = null
|
|
216
|
+
target.circuitCooldownMs = null
|
|
217
|
+
target.circuitProbeInFlight = false
|
|
218
|
+
target.history.length = 0
|
|
219
|
+
else
|
|
220
|
+
target.consecutiveSuccesses = 0
|
|
221
|
+
target.consecutiveFailures++
|
|
222
|
+
if target.healthy and target.consecutiveFailures >= target.health.unhealthyThreshold
|
|
223
|
+
target.healthy = false
|
|
224
|
+
openCircuit(target, pool?.randomFn or Math.random, pool?.nowFn or Date.now) if target.circuitState is 'half-open'
|
|
225
|
+
target
|
|
226
|
+
|
|
227
|
+
export checkTargetHealth = (pool, target, fetchFn = null) ->
|
|
228
|
+
runFetch = fetchFn or pool.fetchFn or fetch
|
|
229
|
+
url = target.url + target.health.path
|
|
230
|
+
try
|
|
231
|
+
res = runFetch(url)
|
|
232
|
+
res = res! if res?.then
|
|
233
|
+
target.lastStatus = res.status
|
|
234
|
+
updateTargetHealth(target, res.ok, pool)
|
|
235
|
+
res.ok
|
|
236
|
+
catch
|
|
237
|
+
target.lastStatus = null
|
|
238
|
+
updateTargetHealth(target, false, pool)
|
|
239
|
+
false
|
|
240
|
+
|
|
241
|
+
export startHealthCheck = (pool, upstreamId) ->
|
|
242
|
+
upstream = getUpstream(pool, upstreamId)
|
|
243
|
+
return null unless upstream
|
|
244
|
+
stopHealthCheck(pool, upstreamId)
|
|
245
|
+
|
|
246
|
+
targets = upstream.targets
|
|
247
|
+
return null unless targets.length > 0
|
|
248
|
+
intervalMs = upstream.healthCheck.intervalMs
|
|
249
|
+
offsetMs = if targets.length > 0 then Math.floor(intervalMs / targets.length) else 0
|
|
250
|
+
|
|
251
|
+
timers = targets.map (target, idx) ->
|
|
252
|
+
checkTargetHealth(pool, target)
|
|
253
|
+
pool.setIntervalFn((-> checkTargetHealth(pool, target)), intervalMs + (idx * offsetMs))
|
|
254
|
+
|
|
255
|
+
pool.intervals.set(upstreamId, timers)
|
|
256
|
+
timers
|
|
257
|
+
|
|
258
|
+
export startHealthChecks = (pool) ->
|
|
259
|
+
for upstream in listUpstreams(pool)
|
|
260
|
+
startHealthCheck(pool, upstream.id)
|
|
261
|
+
pool
|
|
262
|
+
|
|
263
|
+
export stopHealthCheck = (pool, upstreamId) ->
|
|
264
|
+
timers = pool.intervals.get(upstreamId)
|
|
265
|
+
if timers
|
|
266
|
+
pool.clearIntervalFn(timer) for timer in timers
|
|
267
|
+
pool.intervals.delete(upstreamId)
|
|
268
|
+
|
|
269
|
+
export stopHealthChecks = (pool) ->
|
|
270
|
+
for upstreamId in pool.intervals.keys()
|
|
271
|
+
stopHealthCheck(pool, upstreamId)
|
|
272
|
+
pool
|
package/edge/verify.rip
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# edge/verify.rip — edge runtime verification helpers
|
|
3
|
+
# ==============================================================================
|
|
4
|
+
|
|
5
|
+
export buildVerificationResult = (ok, code = null, message = null, details = null) ->
|
|
6
|
+
{ ok, code, message, details }
|
|
7
|
+
|
|
8
|
+
defaultPolicy =
|
|
9
|
+
requireHealthyUpstreams: true
|
|
10
|
+
requireReadyApps: true
|
|
11
|
+
includeUnroutedManagedApps: true
|
|
12
|
+
minHealthyTargetsPerUpstream: 1
|
|
13
|
+
|
|
14
|
+
mergePolicy = (policy = {}) ->
|
|
15
|
+
requireHealthyUpstreams: if policy.requireHealthyUpstreams? then policy.requireHealthyUpstreams is true else defaultPolicy.requireHealthyUpstreams
|
|
16
|
+
requireReadyApps: if policy.requireReadyApps? then policy.requireReadyApps is true else defaultPolicy.requireReadyApps
|
|
17
|
+
includeUnroutedManagedApps: if policy.includeUnroutedManagedApps? then policy.includeUnroutedManagedApps is true else defaultPolicy.includeUnroutedManagedApps
|
|
18
|
+
minHealthyTargetsPerUpstream: Math.max(1, parseInt(String(policy.minHealthyTargetsPerUpstream or defaultPolicy.minHealthyTargetsPerUpstream)) or 1)
|
|
19
|
+
|
|
20
|
+
export collectRouteRequirements = (routeTable, appRegistry = null, defaultAppId = null, policy = {}) ->
|
|
21
|
+
policy = mergePolicy(policy)
|
|
22
|
+
upstreamIds = new Set()
|
|
23
|
+
appIds = new Set()
|
|
24
|
+
|
|
25
|
+
for route in (routeTable?.routes or [])
|
|
26
|
+
upstreamIds.add(route.upstream) if route.upstream
|
|
27
|
+
appIds.add(route.app) if route.app
|
|
28
|
+
|
|
29
|
+
if appRegistry and policy.includeUnroutedManagedApps
|
|
30
|
+
for [appId, app] as appRegistry.apps
|
|
31
|
+
continue if appId is defaultAppId
|
|
32
|
+
continue unless app?.config?.entry
|
|
33
|
+
appIds.add(appId)
|
|
34
|
+
|
|
35
|
+
{
|
|
36
|
+
upstreamIds: Array.from(upstreamIds)
|
|
37
|
+
appIds: Array.from(appIds)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export verifyRouteRuntime = (runtime, appRegistry, defaultAppId, getUpstreamFn, checkTargetHealthFn, getAppStateFn, policy = {}) ->
|
|
41
|
+
policy = mergePolicy(policy)
|
|
42
|
+
requirements = collectRouteRequirements(runtime?.routeTable, appRegistry, defaultAppId, policy)
|
|
43
|
+
|
|
44
|
+
if policy.requireHealthyUpstreams
|
|
45
|
+
for upstreamId in requirements.upstreamIds
|
|
46
|
+
upstream = getUpstreamFn(runtime.upstreamPool, upstreamId)
|
|
47
|
+
return buildVerificationResult(false, 'upstream_missing', "Referenced upstream #{upstreamId} is missing", { upstreamId }) unless upstream
|
|
48
|
+
|
|
49
|
+
healthyTargets = []
|
|
50
|
+
for target in upstream.targets
|
|
51
|
+
healthyTargets.push(target.targetId) if checkTargetHealthFn(runtime.upstreamPool, target)
|
|
52
|
+
|
|
53
|
+
unless healthyTargets.length >= policy.minHealthyTargetsPerUpstream
|
|
54
|
+
return buildVerificationResult(
|
|
55
|
+
false
|
|
56
|
+
'upstream_no_healthy_targets'
|
|
57
|
+
"Upstream #{upstreamId} has too few healthy targets after activation"
|
|
58
|
+
{ upstreamId, targetCount: upstream.targets.length, healthyTargets: healthyTargets.length, requiredHealthyTargets: policy.minHealthyTargetsPerUpstream }
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
if policy.requireReadyApps
|
|
62
|
+
for appId in requirements.appIds
|
|
63
|
+
app = getAppStateFn(appRegistry, appId)
|
|
64
|
+
return buildVerificationResult(false, 'app_missing', "Referenced app #{appId} is missing", { appId }) unless app
|
|
65
|
+
unless app.sockets?.length > 0
|
|
66
|
+
return buildVerificationResult(
|
|
67
|
+
false
|
|
68
|
+
'app_no_ready_workers'
|
|
69
|
+
"App #{appId} has no ready workers after activation"
|
|
70
|
+
{ appId, workers: app.sockets?.length or 0 }
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
buildVerificationResult(true)
|
package/middleware.rip
CHANGED
|
@@ -217,7 +217,7 @@ export compress = (opts = {}) ->
|
|
|
217
217
|
# before ->
|
|
218
218
|
# session.userId = 123
|
|
219
219
|
#
|
|
220
|
-
# get '/profile'
|
|
220
|
+
# get '/profile' ->
|
|
221
221
|
# { userId: session.userId }
|
|
222
222
|
#
|
|
223
223
|
# Security:
|
|
@@ -488,8 +488,8 @@ export serve = (opts = {}) ->
|
|
|
488
488
|
prefix = opts.app or ''
|
|
489
489
|
appDir = opts.dir or '.'
|
|
490
490
|
routesDir = "#{appDir}/#{opts.routes or 'routes'}"
|
|
491
|
-
rawBundle
|
|
492
|
-
bundles
|
|
491
|
+
rawBundle = opts.bundle or opts.components or ['components']
|
|
492
|
+
bundles = new Map()
|
|
493
493
|
if Array.isArray(rawBundle)
|
|
494
494
|
bundles.set 'bundle', rawBundle.map (d) -> "#{appDir}/#{d}"
|
|
495
495
|
else if typeof rawBundle is 'object'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rip-lang/server",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.116",
|
|
4
4
|
"description": "Pure Rip web framework and application server",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "api.rip",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"author": "Steve Shreeve <steve.shreeve@gmail.com>",
|
|
46
46
|
"license": "MIT",
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"rip-lang": ">=3.13.
|
|
48
|
+
"rip-lang": ">=3.13.121"
|
|
49
49
|
},
|
|
50
50
|
"files": [
|
|
51
51
|
"api.rip",
|