emulate 0.1.0 → 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.
- package/README.md +416 -0
- package/dist/api.d.ts +33 -0
- package/dist/api.js +17337 -0
- package/dist/api.js.map +1 -0
- package/dist/fonts/GeistPixel-Square.woff2 +0 -0
- package/dist/fonts/geist-sans.woff2 +0 -0
- package/dist/index.js +129 -82
- package/dist/index.js.map +1 -1
- package/package.json +25 -12
- package/LICENSE +0 -202
|
Binary file
|
|
Binary file
|
package/dist/index.js
CHANGED
|
@@ -3621,6 +3621,9 @@ var SignJWT = class {
|
|
|
3621
3621
|
};
|
|
3622
3622
|
|
|
3623
3623
|
// ../@internal/core/dist/index.js
|
|
3624
|
+
import { readFileSync } from "fs";
|
|
3625
|
+
import { fileURLToPath } from "url";
|
|
3626
|
+
import { dirname, join } from "path";
|
|
3624
3627
|
import { timingSafeEqual } from "crypto";
|
|
3625
3628
|
var Collection = class {
|
|
3626
3629
|
constructor(indexFields = []) {
|
|
@@ -3994,6 +3997,25 @@ function authMiddleware(tokens, appKeyResolver, fallbackUser) {
|
|
|
3994
3997
|
await next();
|
|
3995
3998
|
};
|
|
3996
3999
|
}
|
|
4000
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
4001
|
+
var FONTS = {
|
|
4002
|
+
"geist-sans.woff2": readFileSync(join(__dirname, "fonts", "geist-sans.woff2")),
|
|
4003
|
+
"GeistPixel-Square.woff2": readFileSync(join(__dirname, "fonts", "GeistPixel-Square.woff2"))
|
|
4004
|
+
};
|
|
4005
|
+
function registerFontRoutes(app) {
|
|
4006
|
+
app.get("/_emulate/fonts/:name", (c) => {
|
|
4007
|
+
const name = c.req.param("name");
|
|
4008
|
+
const buf = FONTS[name];
|
|
4009
|
+
if (!buf) return c.notFound();
|
|
4010
|
+
return new Response(buf, {
|
|
4011
|
+
headers: {
|
|
4012
|
+
"Content-Type": "font/woff2",
|
|
4013
|
+
"Cache-Control": "public, max-age=31536000, immutable",
|
|
4014
|
+
"Access-Control-Allow-Origin": "*"
|
|
4015
|
+
}
|
|
4016
|
+
});
|
|
4017
|
+
});
|
|
4018
|
+
}
|
|
3997
4019
|
function createServer(plugin, options = {}) {
|
|
3998
4020
|
const port = options.port ?? 4e3;
|
|
3999
4021
|
const baseUrl = options.baseUrl ?? `http://localhost:${port}`;
|
|
@@ -4011,6 +4033,7 @@ function createServer(plugin, options = {}) {
|
|
|
4011
4033
|
}
|
|
4012
4034
|
}
|
|
4013
4035
|
const docsUrl = options.docsUrl ?? `https://emulate.dev/${plugin.name}`;
|
|
4036
|
+
registerFontRoutes(app);
|
|
4014
4037
|
app.onError(createApiErrorHandler(docsUrl));
|
|
4015
4038
|
app.use("*", cors());
|
|
4016
4039
|
app.use("*", createErrorHandler(docsUrl));
|
|
@@ -4092,137 +4115,161 @@ function escapeAttr(s) {
|
|
|
4092
4115
|
return escapeHtml(s).replace(/'/g, "'");
|
|
4093
4116
|
}
|
|
4094
4117
|
var CSS = `
|
|
4118
|
+
@font-face{
|
|
4119
|
+
font-family:'Geist';font-style:normal;font-weight:100 900;font-display:swap;
|
|
4120
|
+
src:url('/_emulate/fonts/geist-sans.woff2') format('woff2');
|
|
4121
|
+
}
|
|
4122
|
+
@font-face{
|
|
4123
|
+
font-family:'Geist Pixel';font-style:normal;font-weight:400;font-display:swap;
|
|
4124
|
+
src:url('/_emulate/fonts/GeistPixel-Square.woff2') format('woff2');
|
|
4125
|
+
}
|
|
4095
4126
|
*{box-sizing:border-box;margin:0;padding:0}
|
|
4096
4127
|
body{
|
|
4097
|
-
font-family
|
|
4098
|
-
background:#
|
|
4128
|
+
font-family:'Geist',-apple-system,BlinkMacSystemFont,sans-serif;
|
|
4129
|
+
background:#000;color:#33ff00;min-height:100vh;
|
|
4130
|
+
-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
|
|
4099
4131
|
}
|
|
4100
4132
|
.emu-bar{
|
|
4101
|
-
|
|
4102
|
-
display:flex;align-items:center;gap:10px;font-size:.8125rem;color:#
|
|
4133
|
+
border-bottom:1px solid #0a3300;padding:10px 20px;
|
|
4134
|
+
display:flex;align-items:center;gap:10px;font-size:.8125rem;color:#1a8c00;
|
|
4135
|
+
}
|
|
4136
|
+
.emu-bar-title{font-weight:600;color:#33ff00;font-family:'Geist Pixel',monospace;}
|
|
4137
|
+
.emu-bar-links{margin-left:auto;display:flex;gap:16px;}
|
|
4138
|
+
.emu-bar-links a{
|
|
4139
|
+
color:#1a8c00;font-size:.75rem;text-decoration:none;transition:color .15s;
|
|
4103
4140
|
}
|
|
4104
|
-
.emu-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4141
|
+
.emu-bar-links a:hover{color:#33ff00;}
|
|
4142
|
+
.emu-bar-links a .full{display:inline;}
|
|
4143
|
+
.emu-bar-links a .short{display:none;}
|
|
4144
|
+
@media(max-width:600px){
|
|
4145
|
+
.emu-bar-links a .full{display:none;}
|
|
4146
|
+
.emu-bar-links a .short{display:inline;}
|
|
4108
4147
|
}
|
|
4109
|
-
.emu-bar-title{font-weight:600;color:#e4e4e7;}
|
|
4110
|
-
.emu-bar-service{color:#71717a;margin-left:auto;font-size:.75rem;}
|
|
4111
4148
|
|
|
4112
|
-
|
|
4113
|
-
.card-wrap{
|
|
4149
|
+
.content{
|
|
4114
4150
|
display:flex;align-items:center;justify-content:center;
|
|
4115
4151
|
min-height:calc(100vh - 42px);padding:24px 16px;
|
|
4116
4152
|
}
|
|
4117
|
-
.
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4153
|
+
.content-inner{width:100%;max-width:420px;}
|
|
4154
|
+
.card-title{
|
|
4155
|
+
font-family:'Geist Pixel',monospace;
|
|
4156
|
+
font-size:1.125rem;font-weight:600;margin-bottom:4px;color:#33ff00;
|
|
4121
4157
|
}
|
|
4122
|
-
.card-
|
|
4123
|
-
.
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4158
|
+
.card-subtitle{color:#1a8c00;font-size:.8125rem;margin-bottom:18px;line-height:1.45;}
|
|
4159
|
+
.powered-by{
|
|
4160
|
+
position:fixed;bottom:0;left:0;right:0;
|
|
4161
|
+
text-align:center;padding:12px;font-size:.6875rem;color:#0a3300;
|
|
4162
|
+
font-family:'Geist Pixel',monospace;
|
|
4127
4163
|
}
|
|
4164
|
+
.powered-by a{color:#1a8c00;text-decoration:none;transition:color .15s;}
|
|
4165
|
+
.powered-by a:hover{color:#33ff00;}
|
|
4128
4166
|
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4167
|
+
.error-title{
|
|
4168
|
+
font-family:'Geist Pixel',monospace;
|
|
4169
|
+
color:#ff4444;font-size:1.125rem;font-weight:600;margin-bottom:8px;
|
|
4170
|
+
}
|
|
4171
|
+
.error-msg{color:#1a8c00;font-size:.875rem;line-height:1.5;}
|
|
4132
4172
|
.error-card{text-align:center;}
|
|
4133
4173
|
|
|
4134
|
-
/* User button */
|
|
4135
4174
|
.user-form{margin-bottom:8px;}
|
|
4136
4175
|
.user-form:last-of-type{margin-bottom:0;}
|
|
4137
4176
|
.user-btn{
|
|
4138
4177
|
width:100%;display:flex;align-items:center;gap:12px;
|
|
4139
|
-
padding:10px 12px;border:1px solid #
|
|
4140
|
-
background:#
|
|
4141
|
-
font:inherit;transition:
|
|
4178
|
+
padding:10px 12px;border:1px solid #0a3300;border-radius:8px;
|
|
4179
|
+
background:#000;color:inherit;cursor:pointer;text-align:left;
|
|
4180
|
+
font:inherit;transition:border-color .15s;
|
|
4142
4181
|
}
|
|
4143
|
-
.user-btn:hover{
|
|
4182
|
+
.user-btn:hover{border-color:#33ff00;}
|
|
4144
4183
|
.avatar{
|
|
4145
4184
|
width:36px;height:36px;border-radius:50%;
|
|
4146
|
-
background:#
|
|
4185
|
+
background:#0a3300;color:#33ff00;font-weight:600;font-size:.875rem;
|
|
4147
4186
|
display:flex;align-items:center;justify-content:center;flex-shrink:0;
|
|
4187
|
+
font-family:'Geist Pixel',monospace;
|
|
4148
4188
|
}
|
|
4149
4189
|
.user-text{min-width:0;}
|
|
4150
|
-
.user-login{font-weight:600;font-size:.875rem;display:block;color:#
|
|
4151
|
-
.user-meta{color:#
|
|
4152
|
-
.user-email{font-size:.6875rem;color:#
|
|
4190
|
+
.user-login{font-weight:600;font-size:.875rem;display:block;color:#33ff00;}
|
|
4191
|
+
.user-meta{color:#1a8c00;font-size:.75rem;margin-top:1px;}
|
|
4192
|
+
.user-email{font-size:.6875rem;color:#116600;word-break:break-all;margin-top:1px;}
|
|
4153
4193
|
|
|
4154
|
-
/* Settings layout */
|
|
4155
4194
|
.settings-layout{
|
|
4156
4195
|
max-width:920px;margin:0 auto;padding:28px 20px;
|
|
4157
4196
|
display:flex;gap:28px;
|
|
4158
4197
|
}
|
|
4159
4198
|
.settings-sidebar{width:200px;flex-shrink:0;}
|
|
4160
4199
|
.settings-sidebar a{
|
|
4161
|
-
display:block;padding:6px 10px;border-radius:6px;color:#
|
|
4162
|
-
text-decoration:none;font-size:.8125rem;transition:
|
|
4200
|
+
display:block;padding:6px 10px;border-radius:6px;color:#1a8c00;
|
|
4201
|
+
text-decoration:none;font-size:.8125rem;transition:color .15s;
|
|
4163
4202
|
}
|
|
4164
|
-
.settings-sidebar a:hover{
|
|
4165
|
-
.settings-sidebar a.active{
|
|
4203
|
+
.settings-sidebar a:hover{color:#33ff00;}
|
|
4204
|
+
.settings-sidebar a.active{color:#33ff00;font-weight:600;}
|
|
4166
4205
|
.settings-main{flex:1;min-width:0;}
|
|
4167
4206
|
|
|
4168
|
-
/* Settings cards */
|
|
4169
4207
|
.s-card{
|
|
4170
|
-
|
|
4171
|
-
padding:18px;margin-bottom:14px;
|
|
4208
|
+
padding:18px 0;margin-bottom:14px;border-bottom:1px solid #0a3300;
|
|
4172
4209
|
}
|
|
4210
|
+
.s-card:last-child{border-bottom:none;}
|
|
4173
4211
|
.s-card-header{display:flex;align-items:center;gap:14px;margin-bottom:14px;}
|
|
4174
4212
|
.s-icon{
|
|
4175
4213
|
width:42px;height:42px;border-radius:8px;
|
|
4176
|
-
background:#
|
|
4177
|
-
font-size:1.125rem;font-weight:700;color:#
|
|
4214
|
+
background:#0a3300;display:flex;align-items:center;justify-content:center;
|
|
4215
|
+
font-size:1.125rem;font-weight:700;color:#116600;flex-shrink:0;
|
|
4216
|
+
font-family:'Geist Pixel',monospace;
|
|
4178
4217
|
}
|
|
4179
|
-
.s-title{
|
|
4180
|
-
|
|
4218
|
+
.s-title{
|
|
4219
|
+
font-family:'Geist Pixel',monospace;
|
|
4220
|
+
font-size:1.25rem;font-weight:600;color:#33ff00;
|
|
4221
|
+
}
|
|
4222
|
+
.s-subtitle{font-size:.75rem;color:#1a8c00;margin-top:2px;}
|
|
4181
4223
|
.section-heading{
|
|
4182
|
-
font-size:.9375rem;font-weight:600;margin-bottom:10px;color:#
|
|
4224
|
+
font-size:.9375rem;font-weight:600;margin-bottom:10px;color:#33ff00;
|
|
4183
4225
|
display:flex;align-items:center;justify-content:space-between;
|
|
4184
4226
|
}
|
|
4185
4227
|
.perm-list{list-style:none;}
|
|
4186
|
-
.perm-list li{padding:5px 0;font-size:.8125rem;display:flex;align-items:center;gap:6px;color:#
|
|
4187
|
-
.check{color:#
|
|
4228
|
+
.perm-list li{padding:5px 0;font-size:.8125rem;display:flex;align-items:center;gap:6px;color:#1a8c00;}
|
|
4229
|
+
.check{color:#33ff00;}
|
|
4188
4230
|
.org-row{
|
|
4189
4231
|
display:flex;align-items:center;gap:8px;padding:7px 0;
|
|
4190
|
-
border-bottom:1px solid #
|
|
4232
|
+
border-bottom:1px solid #0a3300;font-size:.8125rem;
|
|
4191
4233
|
}
|
|
4192
4234
|
.org-row:last-child{border-bottom:none;}
|
|
4193
4235
|
.org-icon{
|
|
4194
|
-
width:22px;height:22px;border-radius:4px;background:#
|
|
4236
|
+
width:22px;height:22px;border-radius:4px;background:#0a3300;
|
|
4195
4237
|
display:flex;align-items:center;justify-content:center;
|
|
4196
|
-
font-size:.625rem;font-weight:700;color:#
|
|
4238
|
+
font-size:.625rem;font-weight:700;color:#116600;flex-shrink:0;
|
|
4239
|
+
font-family:'Geist Pixel',monospace;
|
|
4197
4240
|
}
|
|
4198
|
-
.org-name{font-weight:600;color:#
|
|
4241
|
+
.org-name{font-weight:600;color:#33ff00;}
|
|
4199
4242
|
.badge{font-size:.6875rem;padding:1px 7px;border-radius:999px;font-weight:500;}
|
|
4200
|
-
.badge-granted{background:#
|
|
4201
|
-
.badge-denied{background:#
|
|
4202
|
-
.badge-requested{background:#
|
|
4243
|
+
.badge-granted{background:#0a3300;color:#33ff00;}
|
|
4244
|
+
.badge-denied{background:#1a0a0a;color:#ff4444;}
|
|
4245
|
+
.badge-requested{background:#0a3300;color:#1a8c00;}
|
|
4203
4246
|
.btn-revoke{
|
|
4204
4247
|
display:inline-block;padding:5px 14px;border-radius:6px;
|
|
4205
|
-
border:1px solid #
|
|
4206
|
-
font-size:.75rem;font-weight:600;cursor:pointer;transition:
|
|
4248
|
+
border:1px solid #0a3300;background:transparent;color:#ff4444;
|
|
4249
|
+
font-size:.75rem;font-weight:600;cursor:pointer;transition:border-color .15s;
|
|
4207
4250
|
}
|
|
4208
|
-
.btn-revoke:hover{
|
|
4209
|
-
.info-text{color:#
|
|
4251
|
+
.btn-revoke:hover{border-color:#ff4444;}
|
|
4252
|
+
.info-text{color:#1a8c00;font-size:.75rem;line-height:1.5;margin-top:10px;}
|
|
4210
4253
|
.app-link{
|
|
4211
4254
|
display:flex;align-items:center;gap:12px;padding:12px;
|
|
4212
|
-
border:1px solid #
|
|
4213
|
-
text-decoration:none;color:inherit;margin-bottom:8px;transition:
|
|
4214
|
-
}
|
|
4215
|
-
.app-link:hover{
|
|
4216
|
-
.app-link-name{font-weight:600;font-size:.875rem;color:#
|
|
4217
|
-
.app-link-scopes{font-size:.6875rem;color:#
|
|
4218
|
-
.empty{color:#
|
|
4219
|
-
.page-footer{margin-top:20px;text-align:center;font-size:.6875rem;color:#3f3f46;}
|
|
4255
|
+
border:1px solid #0a3300;border-radius:8px;background:#000;
|
|
4256
|
+
text-decoration:none;color:inherit;margin-bottom:8px;transition:border-color .15s;
|
|
4257
|
+
}
|
|
4258
|
+
.app-link:hover{border-color:#33ff00;}
|
|
4259
|
+
.app-link-name{font-weight:600;font-size:.875rem;color:#33ff00;}
|
|
4260
|
+
.app-link-scopes{font-size:.6875rem;color:#1a8c00;margin-top:1px;}
|
|
4261
|
+
.empty{color:#1a8c00;text-align:center;padding:28px 0;font-size:.875rem;}
|
|
4220
4262
|
`;
|
|
4263
|
+
var POWERED_BY = `<div class="powered-by">Powered by <a href="https://emulate.dev" target="_blank" rel="noopener">emulate</a></div>`;
|
|
4221
4264
|
function emuBar(service) {
|
|
4265
|
+
const title = service ? `${escapeHtml(service)} Emulator` : "Emulator";
|
|
4222
4266
|
return `<div class="emu-bar">
|
|
4223
|
-
<span class="emu-
|
|
4224
|
-
<
|
|
4225
|
-
|
|
4267
|
+
<span class="emu-bar-title">${title}</span>
|
|
4268
|
+
<nav class="emu-bar-links">
|
|
4269
|
+
<a href="https://github.com/vercel-labs/emulate/issues" target="_blank" rel="noopener"><span class="full">Report Issue</span><span class="short">Report</span></a>
|
|
4270
|
+
<a href="https://github.com/vercel-labs/emulate" target="_blank" rel="noopener"><span class="full">Source Code</span><span class="short">Source</span></a>
|
|
4271
|
+
<a href="https://emulate.dev" target="_blank" rel="noopener"><span class="full">Learn More</span><span class="short">Learn</span></a>
|
|
4272
|
+
</nav>
|
|
4226
4273
|
</div>`;
|
|
4227
4274
|
}
|
|
4228
4275
|
function head(title) {
|
|
@@ -4239,27 +4286,27 @@ function renderCardPage(title, subtitle, body, service) {
|
|
|
4239
4286
|
return `${head(title)}
|
|
4240
4287
|
<body>
|
|
4241
4288
|
${emuBar(service)}
|
|
4242
|
-
<div class="
|
|
4243
|
-
<div class="
|
|
4289
|
+
<div class="content">
|
|
4290
|
+
<div class="content-inner">
|
|
4244
4291
|
<div class="card-title">${escapeHtml(title)}</div>
|
|
4245
4292
|
<div class="card-subtitle">${subtitle}</div>
|
|
4246
4293
|
${body}
|
|
4247
|
-
<div class="card-footer">emulate</div>
|
|
4248
4294
|
</div>
|
|
4249
4295
|
</div>
|
|
4296
|
+
${POWERED_BY}
|
|
4250
4297
|
</body></html>`;
|
|
4251
4298
|
}
|
|
4252
4299
|
function renderErrorPage(title, message2, service) {
|
|
4253
4300
|
return `${head(title)}
|
|
4254
4301
|
<body>
|
|
4255
4302
|
${emuBar(service)}
|
|
4256
|
-
<div class="
|
|
4257
|
-
<div class="
|
|
4303
|
+
<div class="content">
|
|
4304
|
+
<div class="content-inner error-card">
|
|
4258
4305
|
<div class="error-title">${escapeHtml(title)}</div>
|
|
4259
4306
|
<div class="error-msg">${escapeHtml(message2)}</div>
|
|
4260
|
-
<div class="card-footer">emulate</div>
|
|
4261
4307
|
</div>
|
|
4262
4308
|
</div>
|
|
4309
|
+
${POWERED_BY}
|
|
4263
4310
|
</body></html>`;
|
|
4264
4311
|
}
|
|
4265
4312
|
function renderSettingsPage(title, sidebarHtml, bodyHtml, service) {
|
|
@@ -4270,7 +4317,7 @@ ${emuBar(service)}
|
|
|
4270
4317
|
<nav class="settings-sidebar">${sidebarHtml}</nav>
|
|
4271
4318
|
<div class="settings-main">${bodyHtml}</div>
|
|
4272
4319
|
</div>
|
|
4273
|
-
|
|
4320
|
+
${POWERED_BY}
|
|
4274
4321
|
</body></html>`;
|
|
4275
4322
|
}
|
|
4276
4323
|
function renderUserButton(opts) {
|
|
@@ -16363,7 +16410,7 @@ function oauthRoutes2({ app, store, baseUrl, tokenMap }) {
|
|
|
16363
16410
|
</form>
|
|
16364
16411
|
</div>
|
|
16365
16412
|
<ul class="perm-list">
|
|
16366
|
-
${permRows || '<li style="color:#
|
|
16413
|
+
${permRows || '<li style="color:#1a8c00">No specific permissions granted.</li>'}
|
|
16367
16414
|
</ul>
|
|
16368
16415
|
</div>
|
|
16369
16416
|
|
|
@@ -17221,7 +17268,7 @@ var googlePlugin = {
|
|
|
17221
17268
|
|
|
17222
17269
|
// src/commands/start.ts
|
|
17223
17270
|
import { serve } from "@hono/node-server";
|
|
17224
|
-
import { readFileSync, existsSync } from "fs";
|
|
17271
|
+
import { readFileSync as readFileSync2, existsSync } from "fs";
|
|
17225
17272
|
import { resolve } from "path";
|
|
17226
17273
|
import { parse as parseYaml } from "yaml";
|
|
17227
17274
|
import { createRequire } from "module";
|
|
@@ -17235,7 +17282,7 @@ function loadSeedConfig(seedPath) {
|
|
|
17235
17282
|
console.error(`Seed file not found: ${fullPath}`);
|
|
17236
17283
|
process.exit(1);
|
|
17237
17284
|
}
|
|
17238
|
-
const content =
|
|
17285
|
+
const content = readFileSync2(fullPath, "utf-8");
|
|
17239
17286
|
try {
|
|
17240
17287
|
const config = fullPath.endsWith(".json") ? JSON.parse(content) : parseYaml(content);
|
|
17241
17288
|
return { config, source: seedPath };
|
|
@@ -17255,7 +17302,7 @@ function loadSeedConfig(seedPath) {
|
|
|
17255
17302
|
for (const file of autoFiles) {
|
|
17256
17303
|
const fullPath = resolve(file);
|
|
17257
17304
|
if (existsSync(fullPath)) {
|
|
17258
|
-
const content =
|
|
17305
|
+
const content = readFileSync2(fullPath, "utf-8");
|
|
17259
17306
|
try {
|
|
17260
17307
|
const config = fullPath.endsWith(".json") ? JSON.parse(content) : parseYaml(content);
|
|
17261
17308
|
return { config, source: file };
|
|
@@ -17274,7 +17321,7 @@ var SERVICE_PLUGINS = {
|
|
|
17274
17321
|
};
|
|
17275
17322
|
var ALL_SERVICES = Object.keys(SERVICE_PLUGINS);
|
|
17276
17323
|
function inferServicesFromConfig(config) {
|
|
17277
|
-
const found =
|
|
17324
|
+
const found = ALL_SERVICES.filter((k) => k in config);
|
|
17278
17325
|
return found.length > 0 ? found : null;
|
|
17279
17326
|
}
|
|
17280
17327
|
function startCommand(options) {
|