ai-evaluate 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.
Files changed (42) hide show
  1. package/.turbo/turbo-build.log +5 -0
  2. package/.turbo/turbo-test.log +51 -0
  3. package/README.md +420 -0
  4. package/coverage/base.css +224 -0
  5. package/coverage/block-navigation.js +87 -0
  6. package/coverage/coverage-final.json +4 -0
  7. package/coverage/evaluate.ts.html +574 -0
  8. package/coverage/favicon.png +0 -0
  9. package/coverage/index.html +146 -0
  10. package/coverage/index.ts.html +145 -0
  11. package/coverage/prettify.css +1 -0
  12. package/coverage/prettify.js +2 -0
  13. package/coverage/sort-arrow-sprite.png +0 -0
  14. package/coverage/sorter.js +210 -0
  15. package/coverage/worker-template.ts.html +1948 -0
  16. package/dist/evaluate.d.ts +62 -0
  17. package/dist/evaluate.d.ts.map +1 -0
  18. package/dist/evaluate.js +188 -0
  19. package/dist/evaluate.js.map +1 -0
  20. package/dist/index.d.ts +12 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +11 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/types.d.ts +165 -0
  25. package/dist/types.d.ts.map +1 -0
  26. package/dist/types.js +5 -0
  27. package/dist/types.js.map +1 -0
  28. package/dist/worker-template.d.ts +40 -0
  29. package/dist/worker-template.d.ts.map +1 -0
  30. package/dist/worker-template.js +3628 -0
  31. package/dist/worker-template.js.map +1 -0
  32. package/package.json +46 -0
  33. package/src/evaluate.ts +217 -0
  34. package/src/index.ts +21 -0
  35. package/src/types.ts +174 -0
  36. package/src/worker-template.ts +3677 -0
  37. package/test/evaluate-extended.test.ts +469 -0
  38. package/test/evaluate.test.ts +253 -0
  39. package/test/index.test.ts +95 -0
  40. package/test/worker-template.test.ts +430 -0
  41. package/tsconfig.json +22 -0
  42. package/vitest.config.ts +16 -0
@@ -0,0 +1,5 @@
1
+
2
+ 
3
+ > ai-sandbox@0.1.0 build /Users/nathanclevenger/projects/mdx.org.ai/primitives/packages/ai-sandbox
4
+ > tsc -p tsconfig.json
5
+
@@ -0,0 +1,51 @@
1
+
2
+ > ai-sandbox@0.0.1 test /Users/nathanclevenger/projects/mdx.org.ai/primitives/packages/ai-sandbox
3
+ > vitest
4
+
5
+
6
+ DEV v2.1.9 /Users/nathanclevenger/projects/mdx.org.ai/primitives/packages/ai-sandbox
7
+
8
+ ✓ test/worker-template.test.ts (65 tests) 8ms
9
+ stdout | test/evaluate.test.ts > evaluate > script execution > captures console output
10
+ hello
11
+
12
+ stderr | test/evaluate.test.ts > evaluate > script execution > captures console output
13
+ warning
14
+ error
15
+
16
+ stdout | test/index.test.ts > types > LogEntry has correct shape
17
+ test
18
+
19
+ stderr | test/evaluate.test.ts > evaluate > script execution > handles script errors
20
+ Script error: test error
21
+
22
+ ✓ test/index.test.ts (8 tests) 421ms
23
+ stderr | test/evaluate-extended.test.ts > evaluate - extended tests > module errors > captures module runtime errors
24
+ Module error: module error
25
+
26
+ stdout | test/evaluate-extended.test.ts > evaluate - extended tests > console methods > captures console.info
27
+ info message
28
+
29
+ stdout | test/evaluate-extended.test.ts > evaluate - extended tests > console methods > captures console.debug
30
+ debug message
31
+
32
+ stdout | test/evaluate-extended.test.ts > evaluate - extended tests > console methods > stringifies objects in console output
33
+ { a: 1 }
34
+
35
+ stdout | test/evaluate-extended.test.ts > evaluate - extended tests > console methods > joins multiple console arguments
36
+ a b c
37
+
38
+ stderr | test/evaluate-extended.test.ts > evaluate - extended tests > async scripts > handles Promise rejection in script
39
+ Script error: async error
40
+
41
+ ✓ test/evaluate.test.ts (18 tests) 765ms
42
+ ✓ test/evaluate-extended.test.ts (40 tests) 1352ms
43
+
44
+ Test Files 4 passed (4)
45
+ Tests 131 passed (131)
46
+ Start at 14:14:37
47
+ Duration 1.87s (transform 186ms, setup 0ms, collect 405ms, tests 2.55s, environment 0ms, prepare 339ms)
48
+
49
+ PASS Waiting for file changes...
50
+ press h to show help, press q to quit
51
+  ELIFECYCLE  Test failed. See above for more details.
package/README.md ADDED
@@ -0,0 +1,420 @@
1
+ # ai-evaluate
2
+
3
+ Secure code execution in sandboxed environments. Run untrusted code safely using Cloudflare Workers or Miniflare.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add ai-evaluate
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { evaluate } from 'ai-evaluate'
15
+
16
+ // Run a simple script
17
+ const result = await evaluate({
18
+ script: '1 + 1'
19
+ })
20
+ // { success: true, value: 2, logs: [], duration: 5 }
21
+
22
+ // With a module and tests
23
+ const result = await evaluate({
24
+ module: `
25
+ export const add = (a, b) => a + b
26
+ export const multiply = (a, b) => a * b
27
+ `,
28
+ tests: `
29
+ describe('math', () => {
30
+ it('adds numbers', () => {
31
+ expect(add(2, 3)).toBe(5);
32
+ })
33
+ it('multiplies numbers', () => {
34
+ expect(multiply(2, 3)).toBe(6);
35
+ })
36
+ })
37
+ `,
38
+ script: 'add(10, 20)'
39
+ })
40
+ ```
41
+
42
+ ## Features
43
+
44
+ - **Secure isolation** - Code runs in a sandboxed V8 isolate
45
+ - **Vitest-compatible tests** - `describe`, `it`, `expect` in global scope
46
+ - **Module exports** - Define modules and use exports in scripts/tests
47
+ - **Cloudflare Workers** - Uses worker_loaders in production
48
+ - **Miniflare** - Uses Miniflare for local development and Node.js
49
+ - **Network isolation** - External network access blocked by default
50
+
51
+ ## API
52
+
53
+ ### evaluate(options)
54
+
55
+ Execute code in a sandboxed environment.
56
+
57
+ ```typescript
58
+ interface EvaluateOptions {
59
+ /** Module code with exports */
60
+ module?: string
61
+ /** Test code using vitest-style API */
62
+ tests?: string
63
+ /** Script code to run (module exports in scope) */
64
+ script?: string
65
+ /** Timeout in milliseconds (default: 5000) */
66
+ timeout?: number
67
+ /** Environment variables */
68
+ env?: Record<string, string>
69
+ }
70
+
71
+ interface EvaluateResult {
72
+ /** Whether execution succeeded */
73
+ success: boolean
74
+ /** Return value from script */
75
+ value?: unknown
76
+ /** Console output */
77
+ logs: LogEntry[]
78
+ /** Test results (if tests provided) */
79
+ testResults?: TestResults
80
+ /** Error message if failed */
81
+ error?: string
82
+ /** Execution time in ms */
83
+ duration: number
84
+ }
85
+ ```
86
+
87
+ ### createEvaluator(env)
88
+
89
+ Create an evaluate function bound to a specific environment. Useful for Cloudflare Workers.
90
+
91
+ ```typescript
92
+ import { createEvaluator } from 'ai-evaluate'
93
+
94
+ export default {
95
+ async fetch(request, env) {
96
+ const sandbox = createEvaluator(env)
97
+ const result = await sandbox({
98
+ script: '1 + 1'
99
+ })
100
+ return Response.json(result)
101
+ }
102
+ }
103
+ ```
104
+
105
+ ## Usage Patterns
106
+
107
+ ### Simple Script Execution
108
+
109
+ ```typescript
110
+ const result = await evaluate({
111
+ script: `
112
+ const x = 10;
113
+ const y = 20;
114
+ return x + y;
115
+ `
116
+ })
117
+ // result.value === 30
118
+ ```
119
+
120
+ ### Module with Exports
121
+
122
+ ```typescript
123
+ const result = await evaluate({
124
+ module: `
125
+ exports.greet = (name) => \`Hello, \${name}!\`;
126
+ exports.sum = (...nums) => nums.reduce((a, b) => a + b, 0);
127
+ `,
128
+ script: `
129
+ console.log(greet('World'));
130
+ return sum(1, 2, 3, 4, 5);
131
+ `
132
+ })
133
+ // result.value === 15
134
+ // result.logs[0].message === 'Hello, World!'
135
+ ```
136
+
137
+ ### Running Tests
138
+
139
+ ```typescript
140
+ const result = await evaluate({
141
+ module: `
142
+ exports.isPrime = (n) => {
143
+ if (n < 2) return false;
144
+ for (let i = 2; i <= Math.sqrt(n); i++) {
145
+ if (n % i === 0) return false;
146
+ }
147
+ return true;
148
+ };
149
+ `,
150
+ tests: `
151
+ describe('isPrime', () => {
152
+ it('returns false for numbers less than 2', () => {
153
+ expect(isPrime(0)).toBe(false);
154
+ expect(isPrime(1)).toBe(false);
155
+ });
156
+
157
+ it('returns true for prime numbers', () => {
158
+ expect(isPrime(2)).toBe(true);
159
+ expect(isPrime(3)).toBe(true);
160
+ expect(isPrime(17)).toBe(true);
161
+ });
162
+
163
+ it('returns false for composite numbers', () => {
164
+ expect(isPrime(4)).toBe(false);
165
+ expect(isPrime(9)).toBe(false);
166
+ expect(isPrime(100)).toBe(false);
167
+ });
168
+ });
169
+ `
170
+ })
171
+
172
+ console.log(result.testResults)
173
+ // {
174
+ // total: 3,
175
+ // passed: 3,
176
+ // failed: 0,
177
+ // skipped: 0,
178
+ // tests: [...]
179
+ // }
180
+ ```
181
+
182
+ ## Test Framework
183
+
184
+ The sandbox provides a vitest-compatible test API with async support.
185
+
186
+ ### describe / it / test
187
+
188
+ ```typescript
189
+ describe('group name', () => {
190
+ it('test name', () => {
191
+ // test code
192
+ });
193
+
194
+ test('another test', () => {
195
+ // test code
196
+ });
197
+
198
+ it.skip('skipped test', () => {
199
+ // won't run
200
+ });
201
+
202
+ it.only('only this test', () => {
203
+ // when .only is used, only these tests run
204
+ });
205
+ });
206
+ ```
207
+
208
+ ### Async Tests
209
+
210
+ ```typescript
211
+ describe('async operations', () => {
212
+ it('supports async/await', async () => {
213
+ const result = await someAsyncFunction();
214
+ expect(result).toBe('expected');
215
+ });
216
+
217
+ it('supports promises', () => {
218
+ return fetchData().then(data => {
219
+ expect(data).toBeDefined();
220
+ });
221
+ });
222
+ });
223
+ ```
224
+
225
+ ### Hooks
226
+
227
+ ```typescript
228
+ describe('with setup', () => {
229
+ let data;
230
+
231
+ beforeEach(() => {
232
+ data = { count: 0 };
233
+ });
234
+
235
+ afterEach(() => {
236
+ data = null;
237
+ });
238
+
239
+ it('uses setup data', () => {
240
+ data.count++;
241
+ expect(data.count).toBe(1);
242
+ });
243
+ });
244
+ ```
245
+
246
+ ### expect matchers
247
+
248
+ ```typescript
249
+ // Equality
250
+ expect(value).toBe(expected) // Strict equality (===)
251
+ expect(value).toEqual(expected) // Deep equality
252
+ expect(value).toStrictEqual(expected) // Strict deep equality
253
+
254
+ // Truthiness
255
+ expect(value).toBeTruthy() // Truthy check
256
+ expect(value).toBeFalsy() // Falsy check
257
+ expect(value).toBeNull() // null check
258
+ expect(value).toBeUndefined() // undefined check
259
+ expect(value).toBeDefined() // not undefined
260
+ expect(value).toBeNaN() // NaN check
261
+
262
+ // Numbers
263
+ expect(value).toBeGreaterThan(n) // > comparison
264
+ expect(value).toBeLessThan(n) // < comparison
265
+ expect(value).toBeGreaterThanOrEqual(n)// >= comparison
266
+ expect(value).toBeLessThanOrEqual(n) // <= comparison
267
+ expect(value).toBeCloseTo(n, digits) // Floating point comparison
268
+
269
+ // Strings
270
+ expect(value).toMatch(/pattern/) // Regex match
271
+ expect(value).toMatch('substring') // Contains substring
272
+
273
+ // Arrays & Strings
274
+ expect(value).toContain(item) // Array/string contains
275
+ expect(value).toContainEqual(item) // Array contains (deep equality)
276
+ expect(value).toHaveLength(n) // Length check
277
+
278
+ // Objects
279
+ expect(value).toHaveProperty('path') // Has property
280
+ expect(value).toHaveProperty('path', v)// Has property with value
281
+ expect(value).toMatchObject(partial) // Partial object match
282
+
283
+ // Types
284
+ expect(value).toBeInstanceOf(Class) // instanceof check
285
+ expect(value).toBeTypeOf('string') // typeof check
286
+
287
+ // Errors
288
+ expect(fn).toThrow() // Throws any error
289
+ expect(fn).toThrow('message') // Throws with message
290
+ expect(fn).toThrow(/pattern/) // Throws matching pattern
291
+ expect(fn).toThrow(ErrorClass) // Throws specific error type
292
+
293
+ // Negated matchers
294
+ expect(value).not.toBe(expected)
295
+ expect(value).not.toEqual(expected)
296
+ expect(value).not.toContain(item)
297
+ expect(fn).not.toThrow()
298
+
299
+ // Promise matchers
300
+ await expect(promise).resolves.toBe(value)
301
+ await expect(promise).rejects.toThrow('error')
302
+ ```
303
+
304
+ ## Cloudflare Workers Setup
305
+
306
+ To use in Cloudflare Workers with worker_loaders:
307
+
308
+ ### wrangler.toml
309
+
310
+ ```toml
311
+ name = "my-worker"
312
+ main = "src/index.ts"
313
+
314
+ [[worker_loaders]]
315
+ binding = "LOADER"
316
+ ```
317
+
318
+ ### Worker Code
319
+
320
+ ```typescript
321
+ import { createEvaluator } from 'ai-evaluate'
322
+
323
+ export interface Env {
324
+ LOADER: unknown
325
+ }
326
+
327
+ export default {
328
+ async fetch(request: Request, env: Env): Promise<Response> {
329
+ const sandbox = createEvaluator(env)
330
+
331
+ const { code, tests } = await request.json()
332
+
333
+ const result = await sandbox({
334
+ module: code,
335
+ tests: tests
336
+ })
337
+
338
+ return Response.json(result)
339
+ }
340
+ }
341
+ ```
342
+
343
+ ## Node.js / Development
344
+
345
+ In Node.js or during development, the evaluate function automatically uses Miniflare:
346
+
347
+ ```typescript
348
+ import { evaluate } from 'ai-evaluate'
349
+
350
+ // Miniflare is used automatically when LOADER binding is not present
351
+ const result = await evaluate({
352
+ script: 'return "Hello from Node!"'
353
+ })
354
+ ```
355
+
356
+ Make sure `miniflare` is installed:
357
+
358
+ ```bash
359
+ pnpm add miniflare
360
+ ```
361
+
362
+ ## Security
363
+
364
+ The sandbox provides several security features:
365
+
366
+ 1. **V8 Isolate** - Code runs in an isolated V8 context
367
+ 2. **No Network** - External network access is blocked (`globalOutbound: null`)
368
+ 3. **No File System** - No access to the file system
369
+ 4. **Memory Limits** - Standard Worker memory limits apply
370
+ 5. **CPU Limits** - Execution time is limited
371
+
372
+ ## Example: Code Evaluation API
373
+
374
+ ```typescript
375
+ import { evaluate } from 'ai-evaluate'
376
+ import { Hono } from 'hono'
377
+
378
+ const app = new Hono()
379
+
380
+ app.post('/evaluate', async (c) => {
381
+ const { module, tests, script } = await c.req.json()
382
+
383
+ const result = await evaluate({
384
+ module,
385
+ tests,
386
+ script,
387
+ timeout: 5000
388
+ })
389
+
390
+ return c.json(result)
391
+ })
392
+
393
+ export default app
394
+ ```
395
+
396
+ ## Types
397
+
398
+ ```typescript
399
+ interface LogEntry {
400
+ level: 'log' | 'warn' | 'error' | 'info' | 'debug'
401
+ message: string
402
+ timestamp: number
403
+ }
404
+
405
+ interface TestResults {
406
+ total: number
407
+ passed: number
408
+ failed: number
409
+ skipped: number
410
+ tests: TestResult[]
411
+ duration: number
412
+ }
413
+
414
+ interface TestResult {
415
+ name: string
416
+ passed: boolean
417
+ error?: string
418
+ duration: number
419
+ }
420
+ ```
@@ -0,0 +1,224 @@
1
+ body, html {
2
+ margin:0; padding: 0;
3
+ height: 100%;
4
+ }
5
+ body {
6
+ font-family: Helvetica Neue, Helvetica, Arial;
7
+ font-size: 14px;
8
+ color:#333;
9
+ }
10
+ .small { font-size: 12px; }
11
+ *, *:after, *:before {
12
+ -webkit-box-sizing:border-box;
13
+ -moz-box-sizing:border-box;
14
+ box-sizing:border-box;
15
+ }
16
+ h1 { font-size: 20px; margin: 0;}
17
+ h2 { font-size: 14px; }
18
+ pre {
19
+ font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
20
+ margin: 0;
21
+ padding: 0;
22
+ -moz-tab-size: 2;
23
+ -o-tab-size: 2;
24
+ tab-size: 2;
25
+ }
26
+ a { color:#0074D9; text-decoration:none; }
27
+ a:hover { text-decoration:underline; }
28
+ .strong { font-weight: bold; }
29
+ .space-top1 { padding: 10px 0 0 0; }
30
+ .pad2y { padding: 20px 0; }
31
+ .pad1y { padding: 10px 0; }
32
+ .pad2x { padding: 0 20px; }
33
+ .pad2 { padding: 20px; }
34
+ .pad1 { padding: 10px; }
35
+ .space-left2 { padding-left:55px; }
36
+ .space-right2 { padding-right:20px; }
37
+ .center { text-align:center; }
38
+ .clearfix { display:block; }
39
+ .clearfix:after {
40
+ content:'';
41
+ display:block;
42
+ height:0;
43
+ clear:both;
44
+ visibility:hidden;
45
+ }
46
+ .fl { float: left; }
47
+ @media only screen and (max-width:640px) {
48
+ .col3 { width:100%; max-width:100%; }
49
+ .hide-mobile { display:none!important; }
50
+ }
51
+
52
+ .quiet {
53
+ color: #7f7f7f;
54
+ color: rgba(0,0,0,0.5);
55
+ }
56
+ .quiet a { opacity: 0.7; }
57
+
58
+ .fraction {
59
+ font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
60
+ font-size: 10px;
61
+ color: #555;
62
+ background: #E8E8E8;
63
+ padding: 4px 5px;
64
+ border-radius: 3px;
65
+ vertical-align: middle;
66
+ }
67
+
68
+ div.path a:link, div.path a:visited { color: #333; }
69
+ table.coverage {
70
+ border-collapse: collapse;
71
+ margin: 10px 0 0 0;
72
+ padding: 0;
73
+ }
74
+
75
+ table.coverage td {
76
+ margin: 0;
77
+ padding: 0;
78
+ vertical-align: top;
79
+ }
80
+ table.coverage td.line-count {
81
+ text-align: right;
82
+ padding: 0 5px 0 20px;
83
+ }
84
+ table.coverage td.line-coverage {
85
+ text-align: right;
86
+ padding-right: 10px;
87
+ min-width:20px;
88
+ }
89
+
90
+ table.coverage td span.cline-any {
91
+ display: inline-block;
92
+ padding: 0 5px;
93
+ width: 100%;
94
+ }
95
+ .missing-if-branch {
96
+ display: inline-block;
97
+ margin-right: 5px;
98
+ border-radius: 3px;
99
+ position: relative;
100
+ padding: 0 4px;
101
+ background: #333;
102
+ color: yellow;
103
+ }
104
+
105
+ .skip-if-branch {
106
+ display: none;
107
+ margin-right: 10px;
108
+ position: relative;
109
+ padding: 0 4px;
110
+ background: #ccc;
111
+ color: white;
112
+ }
113
+ .missing-if-branch .typ, .skip-if-branch .typ {
114
+ color: inherit !important;
115
+ }
116
+ .coverage-summary {
117
+ border-collapse: collapse;
118
+ width: 100%;
119
+ }
120
+ .coverage-summary tr { border-bottom: 1px solid #bbb; }
121
+ .keyline-all { border: 1px solid #ddd; }
122
+ .coverage-summary td, .coverage-summary th { padding: 10px; }
123
+ .coverage-summary tbody { border: 1px solid #bbb; }
124
+ .coverage-summary td { border-right: 1px solid #bbb; }
125
+ .coverage-summary td:last-child { border-right: none; }
126
+ .coverage-summary th {
127
+ text-align: left;
128
+ font-weight: normal;
129
+ white-space: nowrap;
130
+ }
131
+ .coverage-summary th.file { border-right: none !important; }
132
+ .coverage-summary th.pct { }
133
+ .coverage-summary th.pic,
134
+ .coverage-summary th.abs,
135
+ .coverage-summary td.pct,
136
+ .coverage-summary td.abs { text-align: right; }
137
+ .coverage-summary td.file { white-space: nowrap; }
138
+ .coverage-summary td.pic { min-width: 120px !important; }
139
+ .coverage-summary tfoot td { }
140
+
141
+ .coverage-summary .sorter {
142
+ height: 10px;
143
+ width: 7px;
144
+ display: inline-block;
145
+ margin-left: 0.5em;
146
+ background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
147
+ }
148
+ .coverage-summary .sorted .sorter {
149
+ background-position: 0 -20px;
150
+ }
151
+ .coverage-summary .sorted-desc .sorter {
152
+ background-position: 0 -10px;
153
+ }
154
+ .status-line { height: 10px; }
155
+ /* yellow */
156
+ .cbranch-no { background: yellow !important; color: #111; }
157
+ /* dark red */
158
+ .red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
159
+ .low .chart { border:1px solid #C21F39 }
160
+ .highlighted,
161
+ .highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{
162
+ background: #C21F39 !important;
163
+ }
164
+ /* medium red */
165
+ .cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
166
+ /* light red */
167
+ .low, .cline-no { background:#FCE1E5 }
168
+ /* light green */
169
+ .high, .cline-yes { background:rgb(230,245,208) }
170
+ /* medium green */
171
+ .cstat-yes { background:rgb(161,215,106) }
172
+ /* dark green */
173
+ .status-line.high, .high .cover-fill { background:rgb(77,146,33) }
174
+ .high .chart { border:1px solid rgb(77,146,33) }
175
+ /* dark yellow (gold) */
176
+ .status-line.medium, .medium .cover-fill { background: #f9cd0b; }
177
+ .medium .chart { border:1px solid #f9cd0b; }
178
+ /* light yellow */
179
+ .medium { background: #fff4c2; }
180
+
181
+ .cstat-skip { background: #ddd; color: #111; }
182
+ .fstat-skip { background: #ddd; color: #111 !important; }
183
+ .cbranch-skip { background: #ddd !important; color: #111; }
184
+
185
+ span.cline-neutral { background: #eaeaea; }
186
+
187
+ .coverage-summary td.empty {
188
+ opacity: .5;
189
+ padding-top: 4px;
190
+ padding-bottom: 4px;
191
+ line-height: 1;
192
+ color: #888;
193
+ }
194
+
195
+ .cover-fill, .cover-empty {
196
+ display:inline-block;
197
+ height: 12px;
198
+ }
199
+ .chart {
200
+ line-height: 0;
201
+ }
202
+ .cover-empty {
203
+ background: white;
204
+ }
205
+ .cover-full {
206
+ border-right: none !important;
207
+ }
208
+ pre.prettyprint {
209
+ border: none !important;
210
+ padding: 0 !important;
211
+ margin: 0 !important;
212
+ }
213
+ .com { color: #999 !important; }
214
+ .ignore-none { color: #999; font-weight: normal; }
215
+
216
+ .wrapper {
217
+ min-height: 100%;
218
+ height: auto !important;
219
+ height: 100%;
220
+ margin: 0 auto -48px;
221
+ }
222
+ .footer, .push {
223
+ height: 48px;
224
+ }