@atproto/dev-env 0.6.7 → 0.6.9

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/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # @atproto/dev-env
2
2
 
3
+ ## 0.6.9
4
+
5
+ ### Patch Changes
6
+
7
+ - [#5562](https://github.com/bluesky-social/atproto/pull/5562) [`f632a35`](https://github.com/bluesky-social/atproto/commit/f632a35e8641644022bf430c6e3b34b1382cab55) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Remove the need to provide a `plcRotationKey` secret when and override is provided
8
+
9
+ - [#5562](https://github.com/bluesky-social/atproto/pull/5562) [`f632a35`](https://github.com/bluesky-social/atproto/commit/f632a35e8641644022bf430c6e3b34b1382cab55) Thanks [@matthieusieben](https://github.com/matthieusieben)! - `TestPds.create` accepts optional `PDS.create` overrides as a second argument.
10
+ - Updated dependencies [[`4c725e6`](https://github.com/bluesky-social/atproto/commit/4c725e670ae67adf0711d08b53108be054e9dcf3), [`f632a35`](https://github.com/bluesky-social/atproto/commit/f632a35e8641644022bf430c6e3b34b1382cab55), [`af8c350`](https://github.com/bluesky-social/atproto/commit/af8c3501cd32e82cf8128683445656aa6708608d), [`5019f55`](https://github.com/bluesky-social/atproto/commit/5019f5537317ee75ad4527db212bc27c3085fe67), [`f632a35`](https://github.com/bluesky-social/atproto/commit/f632a35e8641644022bf430c6e3b34b1382cab55), [`af8c350`](https://github.com/bluesky-social/atproto/commit/af8c3501cd32e82cf8128683445656aa6708608d), [`f5f0aab`](https://github.com/bluesky-social/atproto/commit/f5f0aab4cf3046de3dddd9251a07003d29e3c0be), [`b3d7c33`](https://github.com/bluesky-social/atproto/commit/b3d7c3391e50e4ae9e1b1cbfc9d561c8693507e7), [`136e214`](https://github.com/bluesky-social/atproto/commit/136e2145b893773a94e9620df37be5215cdb57a3), [`7a85798`](https://github.com/bluesky-social/atproto/commit/7a857989751ae31518509d69ab7194a922064f3d), [`f5f0aab`](https://github.com/bluesky-social/atproto/commit/f5f0aab4cf3046de3dddd9251a07003d29e3c0be), [`f632a35`](https://github.com/bluesky-social/atproto/commit/f632a35e8641644022bf430c6e3b34b1382cab55), [`b3d7c33`](https://github.com/bluesky-social/atproto/commit/b3d7c3391e50e4ae9e1b1cbfc9d561c8693507e7), [`136e214`](https://github.com/bluesky-social/atproto/commit/136e2145b893773a94e9620df37be5215cdb57a3)]:
11
+ - @atproto/api@0.22.0
12
+ - @atproto/bsky@0.0.282
13
+ - @atproto/pds@0.5.36
14
+ - @atproto/bsync@0.1.1
15
+ - @atproto/common-web@0.5.13
16
+ - @atproto/crypto@0.5.6
17
+ - @atproto/identity@0.5.15
18
+ - @atproto/lexicon@0.7.15
19
+ - @atproto/ozone@0.5.0
20
+ - @atproto/sync@0.4.12
21
+ - @atproto/xrpc-server@0.13.3
22
+ - @atproto/lex@0.3.13
23
+
24
+ ## 0.6.8
25
+
26
+ ### Patch Changes
27
+
28
+ - [#5517](https://github.com/bluesky-social/atproto/pull/5517) [`4dba111`](https://github.com/bluesky-social/atproto/commit/4dba11136a4a04a76dbb678135aacfe944f8d15b) Thanks [@foysalit](https://github.com/foysalit)! - Add a video invalidator for purging transcoded video content after blob takedowns.
29
+
30
+ - [#5510](https://github.com/bluesky-social/atproto/pull/5510) [`78b0162`](https://github.com/bluesky-social/atproto/commit/78b0162cf38090bb9bffeb6c9dbd5218fefb46e3) Thanks [@danielroe](https://github.com/danielroe)! - Write the test port allocator's owner file atomically so a concurrent sweep cannot delete it while it is still empty.
31
+
32
+ - [#5391](https://github.com/bluesky-social/atproto/pull/5391) [`30f8149`](https://github.com/bluesky-social/atproto/commit/30f8149e629f9afc3fe41cf66bd8482954d7c052) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Remove `dotenv` dependency
33
+ - Updated dependencies [[`7870a59`](https://github.com/bluesky-social/atproto/commit/7870a59c0b0aa5955ba3b663f9e095350f50e194), [`4dba111`](https://github.com/bluesky-social/atproto/commit/4dba11136a4a04a76dbb678135aacfe944f8d15b), [`f210723`](https://github.com/bluesky-social/atproto/commit/f21072353415b4362c90f1dd013f014d6cdadca5), [`421111e`](https://github.com/bluesky-social/atproto/commit/421111e98031d2f8780cc37887d66c5f6659cda8), [`d442d03`](https://github.com/bluesky-social/atproto/commit/d442d037d33d68061df6b44d4269b5ee7309c66f), [`e801c39`](https://github.com/bluesky-social/atproto/commit/e801c39fea8903c6381fd72abd65add426ddcf93), [`2480187`](https://github.com/bluesky-social/atproto/commit/2480187c9554ff161227480dd6c0bcaf5e7e3921), [`210907c`](https://github.com/bluesky-social/atproto/commit/210907c801d281c6b83c92bf94c888b2dc3d531f), [`1eafad7`](https://github.com/bluesky-social/atproto/commit/1eafad71c4f5f8ffc2b6c35ad9f704994d1cd3c4), [`2e583a4`](https://github.com/bluesky-social/atproto/commit/2e583a4ed26659923b2a1effd952fce937f0feeb), [`1884e15`](https://github.com/bluesky-social/atproto/commit/1884e1589f3507c4316f46174f151b3eff3c4b63), [`f86aaa6`](https://github.com/bluesky-social/atproto/commit/f86aaa615b4372c1634011337e42247690d77c5a), [`88f32da`](https://github.com/bluesky-social/atproto/commit/88f32dac103908d1fff0461815afe57b72cb3338), [`d3ceb6b`](https://github.com/bluesky-social/atproto/commit/d3ceb6bbcad0f1a5c9b8daa2c81a4b4259c96e1d), [`9c76c34`](https://github.com/bluesky-social/atproto/commit/9c76c3422ed0c5369871633d2c4ed67a2f61ae33), [`3e58d0b`](https://github.com/bluesky-social/atproto/commit/3e58d0bafdb873b86df25ebf2cf941e256dcf697), [`30f8149`](https://github.com/bluesky-social/atproto/commit/30f8149e629f9afc3fe41cf66bd8482954d7c052)]:
34
+ - @atproto/pds@0.5.35
35
+ - @atproto/ozone@0.4.3
36
+ - @atproto/api@0.21.0
37
+ - @atproto/bsky@0.0.281
38
+ - @atproto/xrpc-server@0.13.2
39
+ - @atproto/bsync@0.1.0
40
+ - @atproto/identity@0.5.14
41
+ - @atproto/sync@0.4.11
42
+ - @atproto/lex@0.3.12
43
+
3
44
  ## 0.6.7
4
45
 
5
46
  ### Patch Changes
package/dist/bin.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import './env.js';
1
+ export {};
2
2
  //# sourceMappingURL=bin.d.ts.map
package/dist/bin.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,CAAA"}
1
+ {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":""}
package/dist/bin.js CHANGED
@@ -1,4 +1,3 @@
1
- import './env.js';
2
1
  import { generateMockSetup } from './mock/index.js';
3
2
  import { TestNetwork } from './network.js';
4
3
  import { mockMailer } from './util.js';
package/dist/bin.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,CAAA;AACjB,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAEtC,MAAM,GAAG,GAAG,KAAK,IAAI,EAAE;IACrB,OAAO,CAAC,GAAG,CAAC;;;;;;;;uBAQS,CAAC,CAAA;IAEtB,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC;QACvC,GAAG,EAAE;YACH,IAAI,EAAE,IAAI;YACV,QAAQ,EAAE,WAAW;YACrB,uBAAuB,EAAE,IAAI;SAC9B;QACD,IAAI,EAAE;YACJ,gBAAgB,EAAE,MAAM;YACxB,IAAI,EAAE,IAAI;YACV,SAAS,EAAE,uBAAuB;SACnC;QACD,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;QACnB,KAAK,EAAE;YACL,IAAI,EAAE,IAAI;YACV,OAAO,EAAE,uBAAuB,EAAE,iCAAiC;YACnE,OAAO,EAAE,kBAAkB;YAC3B,mCAAmC,EAAE,MAAM;SAC5C;QACD,UAAU,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;KAC3B,CAAC,CAAA;IACF,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAEvB,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CACT,oDAAoD,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,CAC9E,CAAA;IACH,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,8CAA8C,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAA;IAC7E,OAAO,CAAC,GAAG,CAAC,gCAAgC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAA;IAC/D,OAAO,CAAC,GAAG,CAAC,wBAAwB,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,YAAY,CAAC,CAAA;IAC1E,OAAO,CAAC,GAAG,CACT,4BAA4B,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,CACvE,CAAA;IACD,OAAO,CAAC,GAAG,CAAC,oCAAoC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;IACrE,OAAO,CAAC,GAAG,CAAC,wBAAwB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;IACxE,OAAO,CAAC,GAAG,CAAC,oCAAoC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;IACrE,OAAO,CAAC,GAAG,CAAC,oCAAoC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;IACpE,OAAO,CAAC,GAAG,CAAC,uBAAuB,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAA;IAC5D,KAAK,MAAM,EAAE,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,sBAAsB,EAAE,CAAC,GAAG,sBAAsB,EAAE,CAAC,IAAI,EAAE,CAAC,CAAA;IAC1E,CAAC;IAED,OAAO,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAA;IAC1C,MAAM,iBAAiB,CAAC,OAAO,CAAC,CAAA;IAChC,OAAO,CAAC,OAAO,CAAC,4BAA4B,CAAC,CAAA;AAC/C,CAAC,CAAA;AAED,GAAG,EAAE,CAAA","sourcesContent":["import './env.js'\nimport { generateMockSetup } from './mock/index.js'\nimport { TestNetwork } from './network.js'\nimport { mockMailer } from './util.js'\n\nconst run = async () => {\n console.log(`\n██████╗\n██╔═══██╗\n██║██╗██║\n██║██║██║\n╚█║████╔╝\n ╚╝╚═══╝ protocol\n\n[ created by Bluesky ]`)\n\n const network = await TestNetwork.create({\n pds: {\n port: 2583,\n hostname: 'localhost',\n enableDidDocWithSession: true,\n },\n bsky: {\n dbPostgresSchema: 'bsky',\n port: 2584,\n publicUrl: 'http://localhost:2584',\n },\n plc: { port: 2582 },\n ozone: {\n port: 2587,\n chatUrl: 'http://localhost:2590', // must run separate chat service\n chatDid: 'did:example:chat',\n dbMaterializedViewRefreshIntervalMs: 30_000,\n },\n introspect: { port: 2581 },\n })\n mockMailer(network.pds)\n\n if (network.introspect) {\n console.log(\n `🔍 Dev-env introspection server http://localhost:${network.introspect.port}`,\n )\n }\n console.log(`👤 DID Placeholder server http://localhost:${network.plc.port}`)\n console.log(`🌞 Main PDS http://localhost:${network.pds.port}`)\n console.log(`🌞 Main PDS account DB`, network.pds.ctx.cfg.db.accountDbLoc)\n console.log(\n `🔨 Lexicon authority DID ${network.pds.ctx.cfg.lexicon.didAuthority}`,\n )\n console.log(`🗼 Ozone server http://localhost:${network.ozone.port}`)\n console.log(`🗼 Ozone service DID ${network.ozone.ctx.cfg.service.did}`)\n console.log(`🔄 Bsync server http://localhost:${network.bsync.port}`)\n console.log(`🌅 Bsky Appview http://localhost:${network.bsky.port}`)\n console.log(`🌅 Bsky Appview DID ${network.bsky.serverDid}`)\n for (const fg of network.feedGens) {\n console.log(`🤖 Feed Generator (${fg.did}) http://localhost:${fg.port}`)\n }\n\n console.time('✅ Dev environment is ready')\n await generateMockSetup(network)\n console.timeEnd('✅ Dev environment is ready')\n}\n\nrun()\n"]}
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAEtC,MAAM,GAAG,GAAG,KAAK,IAAI,EAAE;IACrB,OAAO,CAAC,GAAG,CAAC;;;;;;;;uBAQS,CAAC,CAAA;IAEtB,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC;QACvC,GAAG,EAAE;YACH,IAAI,EAAE,IAAI;YACV,QAAQ,EAAE,WAAW;YACrB,uBAAuB,EAAE,IAAI;SAC9B;QACD,IAAI,EAAE;YACJ,gBAAgB,EAAE,MAAM;YACxB,IAAI,EAAE,IAAI;YACV,SAAS,EAAE,uBAAuB;SACnC;QACD,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;QACnB,KAAK,EAAE;YACL,IAAI,EAAE,IAAI;YACV,OAAO,EAAE,uBAAuB,EAAE,iCAAiC;YACnE,OAAO,EAAE,kBAAkB;YAC3B,mCAAmC,EAAE,MAAM;SAC5C;QACD,UAAU,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;KAC3B,CAAC,CAAA;IACF,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAEvB,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CACT,oDAAoD,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,CAC9E,CAAA;IACH,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,8CAA8C,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAA;IAC7E,OAAO,CAAC,GAAG,CAAC,gCAAgC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAA;IAC/D,OAAO,CAAC,GAAG,CAAC,wBAAwB,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,YAAY,CAAC,CAAA;IAC1E,OAAO,CAAC,GAAG,CACT,4BAA4B,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,CACvE,CAAA;IACD,OAAO,CAAC,GAAG,CAAC,oCAAoC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;IACrE,OAAO,CAAC,GAAG,CAAC,wBAAwB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;IACxE,OAAO,CAAC,GAAG,CAAC,oCAAoC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;IACrE,OAAO,CAAC,GAAG,CAAC,oCAAoC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;IACpE,OAAO,CAAC,GAAG,CAAC,uBAAuB,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAA;IAC5D,KAAK,MAAM,EAAE,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,sBAAsB,EAAE,CAAC,GAAG,sBAAsB,EAAE,CAAC,IAAI,EAAE,CAAC,CAAA;IAC1E,CAAC;IAED,OAAO,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAA;IAC1C,MAAM,iBAAiB,CAAC,OAAO,CAAC,CAAA;IAChC,OAAO,CAAC,OAAO,CAAC,4BAA4B,CAAC,CAAA;AAC/C,CAAC,CAAA;AAED,GAAG,EAAE,CAAA","sourcesContent":["import { generateMockSetup } from './mock/index.js'\nimport { TestNetwork } from './network.js'\nimport { mockMailer } from './util.js'\n\nconst run = async () => {\n console.log(`\n██████╗\n██╔═══██╗\n██║██╗██║\n██║██║██║\n╚█║████╔╝\n ╚╝╚═══╝ protocol\n\n[ created by Bluesky ]`)\n\n const network = await TestNetwork.create({\n pds: {\n port: 2583,\n hostname: 'localhost',\n enableDidDocWithSession: true,\n },\n bsky: {\n dbPostgresSchema: 'bsky',\n port: 2584,\n publicUrl: 'http://localhost:2584',\n },\n plc: { port: 2582 },\n ozone: {\n port: 2587,\n chatUrl: 'http://localhost:2590', // must run separate chat service\n chatDid: 'did:example:chat',\n dbMaterializedViewRefreshIntervalMs: 30_000,\n },\n introspect: { port: 2581 },\n })\n mockMailer(network.pds)\n\n if (network.introspect) {\n console.log(\n `🔍 Dev-env introspection server http://localhost:${network.introspect.port}`,\n )\n }\n console.log(`👤 DID Placeholder server http://localhost:${network.plc.port}`)\n console.log(`🌞 Main PDS http://localhost:${network.pds.port}`)\n console.log(`🌞 Main PDS account DB`, network.pds.ctx.cfg.db.accountDbLoc)\n console.log(\n `🔨 Lexicon authority DID ${network.pds.ctx.cfg.lexicon.didAuthority}`,\n )\n console.log(`🗼 Ozone server http://localhost:${network.ozone.port}`)\n console.log(`🗼 Ozone service DID ${network.ozone.ctx.cfg.service.did}`)\n console.log(`🔄 Bsync server http://localhost:${network.bsync.port}`)\n console.log(`🌅 Bsky Appview http://localhost:${network.bsky.port}`)\n console.log(`🌅 Bsky Appview DID ${network.bsky.serverDid}`)\n for (const fg of network.feedGens) {\n console.log(`🤖 Feed Generator (${fg.did}) http://localhost:${fg.port}`)\n }\n\n console.time('✅ Dev environment is ready')\n await generateMockSetup(network)\n console.timeEnd('✅ Dev environment is ready')\n}\n\nrun()\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"get-port.d.ts","sourceRoot":"","sources":["../src/get-port.ts"],"names":[],"mappings":"AAkCA,eAAO,MAAM,gBAAgB,YAClB,MAAM,aACJ,MAAM,0BAEhB,OAAO,CAAC,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CA8C3C,CAAA;AAED,eAAO,MAAM,sBAAsB,YACxB,MAAM,aACJ,MAAM,kBAsBlB,CAAA;AA+BD,wBAA8B,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,CAgBvD"}
1
+ {"version":3,"file":"get-port.d.ts","sourceRoot":"","sources":["../src/get-port.ts"],"names":[],"mappings":"AAqCA,eAAO,MAAM,gBAAgB,YAClB,MAAM,aACJ,MAAM,0BAEhB,OAAO,CAAC,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CA8C3C,CAAA;AAED,eAAO,MAAM,sBAAsB,YACxB,MAAM,aACJ,MAAM,kBAsBlB,CAAA;AAgCD,wBAA8B,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,CAgBvD"}
package/dist/get-port.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { randomUUID } from 'node:crypto';
2
2
  import { unlinkSync } from 'node:fs';
3
- import { link, mkdir, readFile, readdir, unlink, writeFile, } from 'node:fs/promises';
3
+ import { link, mkdir, readFile, readdir, rename, unlink, writeFile, } from 'node:fs/promises';
4
4
  import { tmpdir } from 'node:os';
5
5
  import { basename, join } from 'node:path';
6
6
  import getAvailablePort from 'get-port';
@@ -8,7 +8,9 @@ const SWEEP_INTERVAL_MS = 1_000;
8
8
  const SWEEP_LOCK_PATTERN = /^\.sweep-(\d+)$/;
9
9
  const lockDir = join(tmpdir(), `atproto-dev-env-ports-${process.getuid?.() ?? 'unknown'}`);
10
10
  const claimedPorts = new Set();
11
- const ownerPath = join(lockDir, `.owner-${process.pid}-${randomUUID()}`);
11
+ const ownerId = `${process.pid}-${randomUUID()}`;
12
+ const ownerPath = join(lockDir, `.owner-${ownerId}`);
13
+ const pendingOwnerPath = join(lockDir, `.pending-${ownerId}`);
12
14
  let initialize;
13
15
  const isProcessRunning = (pid) => {
14
16
  try {
@@ -93,7 +95,8 @@ export const sweepStaleReservations = async (lockDir, ownerPath) => {
93
95
  };
94
96
  const initializeLockDir = async () => {
95
97
  await mkdir(lockDir, { recursive: true });
96
- await writeFile(ownerPath, String(process.pid), { flag: 'wx' });
98
+ await writeFile(pendingOwnerPath, String(process.pid), { flag: 'wx' });
99
+ await rename(pendingOwnerPath, ownerPath);
97
100
  const releaseSweepLock = await acquireSweepLock(lockDir, ownerPath);
98
101
  if (!releaseSweepLock)
99
102
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"get-port.js","sourceRoot":"","sources":["../src/get-port.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from 'node:crypto'\nimport { unlinkSync } from 'node:fs'\nimport {\n link,\n mkdir,\n readFile,\n readdir,\n unlink,\n writeFile,\n} from 'node:fs/promises'\nimport { tmpdir } from 'node:os'\nimport { basename, join } from 'node:path'\nimport getAvailablePort from 'get-port'\n\nconst SWEEP_INTERVAL_MS = 1_000\nconst SWEEP_LOCK_PATTERN = /^\\.sweep-(\\d+)$/\nconst lockDir = join(\n tmpdir(),\n `atproto-dev-env-ports-${process.getuid?.() ?? 'unknown'}`,\n)\nconst claimedPorts = new Set<string>()\nconst ownerPath = join(lockDir, `.owner-${process.pid}-${randomUUID()}`)\n\nlet initialize: Promise<void> | undefined\n\nconst isProcessRunning = (pid: number) => {\n try {\n process.kill(pid, 0)\n return true\n } catch (err) {\n return err?.['code'] !== 'ESRCH'\n }\n}\n\nexport const acquireSweepLock = async (\n lockDir: string,\n ownerPath: string,\n generation = Math.floor(Date.now() / SWEEP_INTERVAL_MS),\n): Promise<(() => Promise<void>) | undefined> => {\n const sweepPath = join(lockDir, `.sweep-${generation}`)\n\n try {\n await link(ownerPath, sweepPath)\n } catch (err) {\n if (err?.['code'] === 'EEXIST') return\n throw err\n }\n\n try {\n const entries = await readdir(lockDir)\n for (const entry of entries) {\n const match = SWEEP_LOCK_PATTERN.exec(entry)\n if (!match || entry === basename(sweepPath)) continue\n\n const otherSweepPath = join(lockDir, entry)\n try {\n const pid = Number.parseInt(await readFile(otherSweepPath, 'utf8'), 10)\n if (Number.isSafeInteger(pid) && isProcessRunning(pid)) {\n await unlink(sweepPath)\n return\n }\n // Sweep generations are never reclaimed, so an inactive marker can\n // be removed without racing a new owner of the same path.\n await unlink(otherSweepPath)\n } catch (err) {\n if (err?.['code'] !== 'ENOENT') throw err\n }\n }\n\n return async () => {\n try {\n await unlink(sweepPath)\n } catch (err) {\n if (err?.['code'] !== 'ENOENT') throw err\n }\n }\n } catch (err) {\n try {\n await unlink(sweepPath)\n } catch (cleanupErr) {\n if (cleanupErr?.['code'] !== 'ENOENT') throw cleanupErr\n }\n throw err\n }\n}\n\nexport const sweepStaleReservations = async (\n lockDir: string,\n ownerPath: string,\n) => {\n const entries = await readdir(lockDir)\n await Promise.all(\n entries\n .filter(\n (entry) =>\n entry !== basename(ownerPath) &&\n (/^\\d+$/.test(entry) || entry.startsWith('.owner-')),\n )\n .map(async (entry) => {\n const lockPath = join(lockDir, entry)\n try {\n const pid = Number.parseInt(await readFile(lockPath, 'utf8'), 10)\n if (!Number.isSafeInteger(pid) || !isProcessRunning(pid)) {\n await unlink(lockPath)\n }\n } catch (err) {\n if (err?.['code'] !== 'ENOENT') throw err\n }\n }),\n )\n}\n\nconst initializeLockDir = async () => {\n await mkdir(lockDir, { recursive: true })\n await writeFile(ownerPath, String(process.pid), { flag: 'wx' })\n\n const releaseSweepLock = await acquireSweepLock(lockDir, ownerPath)\n if (!releaseSweepLock) return\n\n try {\n await sweepStaleReservations(lockDir, ownerPath)\n } finally {\n await releaseSweepLock()\n }\n}\n\nprocess.once('exit', () => {\n for (const lockPath of claimedPorts) {\n try {\n unlinkSync(lockPath)\n } catch {\n // The process is already exiting, so cleanup is best effort.\n }\n }\n try {\n unlinkSync(ownerPath)\n } catch {\n // The process is already exiting, so cleanup is best effort.\n }\n})\n\nexport default async function getPort(): Promise<number> {\n initialize ??= initializeLockDir()\n await initialize\n\n while (true) {\n const port = await getAvailablePort()\n const lockPath = join(lockDir, String(port))\n\n try {\n await link(ownerPath, lockPath)\n claimedPorts.add(lockPath)\n return port\n } catch (err) {\n if (err?.['code'] !== 'EEXIST') throw err\n }\n }\n}\n"]}
1
+ {"version":3,"file":"get-port.js","sourceRoot":"","sources":["../src/get-port.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from 'node:crypto'\nimport { unlinkSync } from 'node:fs'\nimport {\n link,\n mkdir,\n readFile,\n readdir,\n rename,\n unlink,\n writeFile,\n} from 'node:fs/promises'\nimport { tmpdir } from 'node:os'\nimport { basename, join } from 'node:path'\nimport getAvailablePort from 'get-port'\n\nconst SWEEP_INTERVAL_MS = 1_000\nconst SWEEP_LOCK_PATTERN = /^\\.sweep-(\\d+)$/\nconst lockDir = join(\n tmpdir(),\n `atproto-dev-env-ports-${process.getuid?.() ?? 'unknown'}`,\n)\nconst claimedPorts = new Set<string>()\nconst ownerId = `${process.pid}-${randomUUID()}`\nconst ownerPath = join(lockDir, `.owner-${ownerId}`)\nconst pendingOwnerPath = join(lockDir, `.pending-${ownerId}`)\n\nlet initialize: Promise<void> | undefined\n\nconst isProcessRunning = (pid: number) => {\n try {\n process.kill(pid, 0)\n return true\n } catch (err) {\n return err?.['code'] !== 'ESRCH'\n }\n}\n\nexport const acquireSweepLock = async (\n lockDir: string,\n ownerPath: string,\n generation = Math.floor(Date.now() / SWEEP_INTERVAL_MS),\n): Promise<(() => Promise<void>) | undefined> => {\n const sweepPath = join(lockDir, `.sweep-${generation}`)\n\n try {\n await link(ownerPath, sweepPath)\n } catch (err) {\n if (err?.['code'] === 'EEXIST') return\n throw err\n }\n\n try {\n const entries = await readdir(lockDir)\n for (const entry of entries) {\n const match = SWEEP_LOCK_PATTERN.exec(entry)\n if (!match || entry === basename(sweepPath)) continue\n\n const otherSweepPath = join(lockDir, entry)\n try {\n const pid = Number.parseInt(await readFile(otherSweepPath, 'utf8'), 10)\n if (Number.isSafeInteger(pid) && isProcessRunning(pid)) {\n await unlink(sweepPath)\n return\n }\n // Sweep generations are never reclaimed, so an inactive marker can\n // be removed without racing a new owner of the same path.\n await unlink(otherSweepPath)\n } catch (err) {\n if (err?.['code'] !== 'ENOENT') throw err\n }\n }\n\n return async () => {\n try {\n await unlink(sweepPath)\n } catch (err) {\n if (err?.['code'] !== 'ENOENT') throw err\n }\n }\n } catch (err) {\n try {\n await unlink(sweepPath)\n } catch (cleanupErr) {\n if (cleanupErr?.['code'] !== 'ENOENT') throw cleanupErr\n }\n throw err\n }\n}\n\nexport const sweepStaleReservations = async (\n lockDir: string,\n ownerPath: string,\n) => {\n const entries = await readdir(lockDir)\n await Promise.all(\n entries\n .filter(\n (entry) =>\n entry !== basename(ownerPath) &&\n (/^\\d+$/.test(entry) || entry.startsWith('.owner-')),\n )\n .map(async (entry) => {\n const lockPath = join(lockDir, entry)\n try {\n const pid = Number.parseInt(await readFile(lockPath, 'utf8'), 10)\n if (!Number.isSafeInteger(pid) || !isProcessRunning(pid)) {\n await unlink(lockPath)\n }\n } catch (err) {\n if (err?.['code'] !== 'ENOENT') throw err\n }\n }),\n )\n}\n\nconst initializeLockDir = async () => {\n await mkdir(lockDir, { recursive: true })\n await writeFile(pendingOwnerPath, String(process.pid), { flag: 'wx' })\n await rename(pendingOwnerPath, ownerPath)\n\n const releaseSweepLock = await acquireSweepLock(lockDir, ownerPath)\n if (!releaseSweepLock) return\n\n try {\n await sweepStaleReservations(lockDir, ownerPath)\n } finally {\n await releaseSweepLock()\n }\n}\n\nprocess.once('exit', () => {\n for (const lockPath of claimedPorts) {\n try {\n unlinkSync(lockPath)\n } catch {\n // The process is already exiting, so cleanup is best effort.\n }\n }\n try {\n unlinkSync(ownerPath)\n } catch {\n // The process is already exiting, so cleanup is best effort.\n }\n})\n\nexport default async function getPort(): Promise<number> {\n initialize ??= initializeLockDir()\n await initialize\n\n while (true) {\n const port = await getAvailablePort()\n const lockPath = join(lockDir, String(port))\n\n try {\n await link(ownerPath, lockPath)\n claimedPorts.add(lockPath)\n return port\n } catch (err) {\n if (err?.['code'] !== 'EEXIST') throw err\n }\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"ozone.d.ts","sourceRoot":"","sources":["../src/ozone.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,KAAK,OAAO,EAAoB,MAAM,iBAAiB,CAAA;AAChE,OAAO,EAAE,MAAM,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAA;AACrD,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAA;AAIvC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAGxD,qBAAa,SAAS;IAEX,GAAG,EAAE,MAAM;IACX,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,KAAK,CAAC,YAAY;IAC1B,MAAM,EAAE,KAAK,CAAC,WAAW;IACzB,UAAU,EAAE,SAAS;IACrB,cAAc,EAAE,SAAS;IACzB,WAAW,EAAE,SAAS;IAP/B,YACS,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,KAAK,CAAC,YAAY,EAC1B,MAAM,EAAE,KAAK,CAAC,WAAW,EACzB,UAAU,EAAE,SAAS,EACrB,cAAc,EAAE,SAAS,EACzB,WAAW,EAAE,SAAS,EAC3B;IAEJ,OAAa,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC,CA4E3D;IAED,IAAI,GAAG,IAAI,KAAK,CAAC,UAAU,CAE1B;IAED,gDAAgD;IAChD,QAAQ,IAAI,QAAQ,CAInB;IAED,SAAS,IAAI,MAAM,CAIlB;IAED,YAAY,oBAEX;IAEK,WAAW,CAAC,GAAG,EAAE,SAAS,iBAU/B;IAEK,eAAe,CAAC,GAAG,EAAE,SAAS,iBAUnC;IAEK,YAAY,CAAC,GAAG,EAAE,SAAS,iBAUhC;IAEK,cAAc,kBAsEnB;IAEK,UAAU,CACd,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,OAAO,GAAG,WAAW,GAAG,QAAsB;;OAerD;IAEK,UAAU,kBAGf;IAEK,KAAK,kBAMV;IAEK,CAAC,MAAM,CAAC,YAAY,CAAC,kBAE1B;CACF;AAED,eAAO,MAAM,cAAc,WACjB,MAAM,WACL,OAAO,KACf,OAAO,CAAC,SAAS,CAuBnB,CAAA"}
1
+ {"version":3,"file":"ozone.d.ts","sourceRoot":"","sources":["../src/ozone.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,KAAK,OAAO,EAAoB,MAAM,iBAAiB,CAAA;AAChE,OAAO,EAAE,MAAM,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAA;AACrD,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAA;AAIvC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAGxD,qBAAa,SAAS;IAEX,GAAG,EAAE,MAAM;IACX,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,KAAK,CAAC,YAAY;IAC1B,MAAM,EAAE,KAAK,CAAC,WAAW;IACzB,UAAU,EAAE,SAAS;IACrB,cAAc,EAAE,SAAS;IACzB,WAAW,EAAE,SAAS;IAP/B,YACS,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,KAAK,CAAC,YAAY,EAC1B,MAAM,EAAE,KAAK,CAAC,WAAW,EACzB,UAAU,EAAE,SAAS,EACrB,cAAc,EAAE,SAAS,EACzB,WAAW,EAAE,SAAS,EAC3B;IAEJ,OAAa,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC,CA6E3D;IAED,IAAI,GAAG,IAAI,KAAK,CAAC,UAAU,CAE1B;IAED,gDAAgD;IAChD,QAAQ,IAAI,QAAQ,CAInB;IAED,SAAS,IAAI,MAAM,CAIlB;IAED,YAAY,oBAEX;IAEK,WAAW,CAAC,GAAG,EAAE,SAAS,iBAU/B;IAEK,eAAe,CAAC,GAAG,EAAE,SAAS,iBAUnC;IAEK,YAAY,CAAC,GAAG,EAAE,SAAS,iBAUhC;IAEK,cAAc,kBAsEnB;IAEK,UAAU,CACd,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,OAAO,GAAG,WAAW,GAAG,QAAsB;;OAerD;IAEK,UAAU,kBAGf;IAEK,KAAK,kBAMV;IAEK,CAAC,MAAM,CAAC,YAAY,CAAC,kBAE1B;CACF;AAED,eAAO,MAAM,cAAc,WACjB,MAAM,WACL,OAAO,KACf,OAAO,CAAC,SAAS,CAuBnB,CAAA"}
package/dist/ozone.js CHANGED
@@ -78,6 +78,7 @@ export class TestOzone {
78
78
  // api server
79
79
  const server = await ozone.OzoneService.create(cfg, secrets, {
80
80
  imgInvalidator: config.imgInvalidator,
81
+ videoInvalidator: config.videoInvalidator,
81
82
  });
82
83
  await server.start();
83
84
  const daemon = await ozone.OzoneDaemon.create(cfg, secrets);
package/dist/ozone.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ozone.js","sourceRoot":"","sources":["../src/ozone.ts"],"names":[],"mappings":"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","sourcesContent":["import * as plc from '@did-plc/lib'\nimport * as ui8 from 'uint8arrays'\nimport { AtpAgent } from '@atproto/api'\nimport { type Keypair, Secp256k1Keypair } from '@atproto/crypto'\nimport { Client, type DidString } from '@atproto/lex'\nimport * as ozone from '@atproto/ozone'\nimport { createServiceJwt } from '@atproto/xrpc-server'\nimport { ADMIN_PASSWORD, EXAMPLE_LABELER } from './const.js'\nimport getPort from './get-port.js'\nimport { ModeratorClient } from './moderator-client.js'\nimport type { DidAndKey, OzoneConfig } from './types.js'\nimport { createDidAndKey } from './util.js'\n\nexport class TestOzone {\n constructor(\n public url: string,\n public port: number,\n public server: ozone.OzoneService,\n public daemon: ozone.OzoneDaemon,\n public adminAccnt: DidAndKey,\n public moderatorAccnt: DidAndKey,\n public triageAccnt: DidAndKey,\n ) {}\n\n static async create(config: OzoneConfig): Promise<TestOzone> {\n const serviceKeypair =\n config.signingKey ?? (await Secp256k1Keypair.create({ exportable: true }))\n const signingKeyHex = ui8.toString(await serviceKeypair.export(), 'hex')\n let serverDid = config.serverDid\n if (!serverDid) {\n serverDid = await createOzoneDid(config.plcUrl, serviceKeypair)\n }\n\n const admin = await createDidAndKey({\n plcUrl: config.plcUrl,\n handle: 'admin.ozone',\n pds: 'https://pds.invalid',\n })\n\n const moderator = await createDidAndKey({\n plcUrl: config.plcUrl,\n handle: 'moderator.ozone',\n pds: 'https://pds.invalid',\n })\n\n const triage = await createDidAndKey({\n plcUrl: config.plcUrl,\n handle: 'triage.ozone',\n pds: 'https://pds.invalid',\n })\n\n const port = config.port || (await getPort())\n const url = `http://localhost:${port}`\n\n const env: ozone.OzoneEnvironment = {\n devMode: true,\n version: '0.0.0',\n port,\n didPlcUrl: config.plcUrl,\n publicUrl: url,\n serverDid,\n signingKeyHex,\n ...config,\n adminPassword: ADMIN_PASSWORD,\n adminDids: [...(config.adminDids ?? []), admin.did],\n moderatorDids: [\n ...(config.moderatorDids ?? []),\n config.appviewDid,\n moderator.did,\n ],\n triageDids: [...(config.triageDids ?? []), triage.did],\n }\n\n // Separate migration db in case migration changes some connection state that we need in the tests, e.g. \"alter database ... set ...\"\n const migrationDb = new ozone.Database({\n schema: config.dbPostgresSchema,\n url: config.dbPostgresUrl,\n })\n if (config.migration) {\n await migrationDb.migrateToOrThrow(config.migration)\n } else {\n await migrationDb.migrateToLatestOrThrow()\n }\n await migrationDb.close()\n\n const cfg = ozone.envToCfg(env)\n const secrets = ozone.envToSecrets(env)\n\n // api server\n const server = await ozone.OzoneService.create(cfg, secrets, {\n imgInvalidator: config.imgInvalidator,\n })\n await server.start()\n\n const daemon = await ozone.OzoneDaemon.create(cfg, secrets)\n await daemon.start()\n // don't do event reversal in dev-env\n await daemon.ctx.eventReverser.destroy()\n\n return new TestOzone(url, port, server, daemon, admin, moderator, triage)\n }\n\n get ctx(): ozone.AppContext {\n return this.server.ctx\n }\n\n /** @deprecated use {@link getClient} instead */\n getAgent(): AtpAgent {\n const agent = new AtpAgent({ service: this.url })\n agent.configureLabelers([EXAMPLE_LABELER])\n return agent\n }\n\n getClient(): Client {\n return new Client(this.url, {\n labelers: [EXAMPLE_LABELER],\n })\n }\n\n getModClient() {\n return new ModeratorClient(this)\n }\n\n async addAdminDid(did: DidString) {\n await this.ctx.teamService(this.ctx.db).create({\n did,\n disabled: false,\n handle: null,\n displayName: null,\n lastUpdatedBy: this.ctx.cfg.service.did,\n role: 'tools.ozone.team.defs#roleAdmin',\n })\n this.ctx.cfg.access.admins.push(did)\n }\n\n async addModeratorDid(did: DidString) {\n await this.ctx.teamService(this.ctx.db).create({\n did,\n disabled: false,\n handle: null,\n displayName: null,\n lastUpdatedBy: this.ctx.cfg.service.did,\n role: 'tools.ozone.team.defs#roleModerator',\n })\n this.ctx.cfg.access.moderators.push(did)\n }\n\n async addTriageDid(did: DidString) {\n await this.ctx.teamService(this.ctx.db).create({\n did,\n disabled: false,\n handle: null,\n displayName: null,\n lastUpdatedBy: this.ctx.cfg.service.did,\n role: 'tools.ozone.team.defs#roleTriage',\n })\n this.ctx.cfg.access.triage.push(did)\n }\n\n async createPolicies() {\n const now = new Date()\n const defaultOptions = {\n managerRole: 'tools.ozone.team.defs#roleAdmin' as const,\n scope: 'instance' as const,\n did: this.ctx.cfg.service.did,\n lastUpdatedBy: this.ctx.cfg.service.did,\n createdBy: this.ctx.cfg.service.did,\n createdAt: now,\n updatedAt: now,\n }\n await this.ctx.settingService(this.ctx.db).upsert({\n ...defaultOptions,\n key: 'tools.ozone.setting.severityLevels',\n value: {\n 'sev-2': {\n strikeCount: 2,\n expiryInDays: 90,\n },\n 'sev-4': {\n strikeCount: 4,\n expiryInDays: 365,\n },\n 'sev-7': {\n needsTakedown: true,\n description: 'Sever violation, immedate account takedown',\n },\n 'custom-sev': {\n strikeCount: 4,\n firstOccurrenceStrikeCount: 8,\n description: 'First offense harsher penalty, on subsequent less',\n },\n },\n description: 'Severity levels and strike count mapping for policies',\n })\n await this.ctx.settingService(this.ctx.db).upsert({\n ...defaultOptions,\n key: 'tools.ozone.setting.policyList',\n value: {\n 'policy-one': {\n name: 'Policy One',\n description: 'Policy for handling user behavior',\n severityLevels: {\n 'sev-1': {\n description: 'Minor infraction',\n isDefault: true,\n },\n 'sev-2': {\n description: 'Moderate infraction',\n isDefault: false,\n },\n },\n },\n 'policy-two': {\n name: 'Policy Two',\n description: 'Policy for handling user action',\n severityLevels: {\n 'sev-4': {\n description: 'Moderate infraction',\n isDefault: false,\n },\n 'sev-5': {\n description: 'Severe infraction',\n isDefault: false,\n },\n },\n },\n },\n description: 'Moderation policies to be associated with actions',\n })\n }\n\n async modHeaders(\n lxm: string,\n role: 'admin' | 'moderator' | 'triage' = 'moderator',\n ) {\n const account =\n role === 'admin'\n ? this.adminAccnt\n : role === 'moderator'\n ? this.moderatorAccnt\n : this.triageAccnt\n const jwt = await createServiceJwt({\n iss: account.did,\n aud: this.ctx.cfg.service.did,\n lxm,\n keypair: account.key,\n })\n return { authorization: `Bearer ${jwt}` }\n }\n\n async processAll() {\n await this.ctx.backgroundQueue.processAll()\n await this.daemon.processAll()\n }\n\n async close() {\n try {\n await this.server.destroy()\n } finally {\n await this.daemon.destroy()\n }\n }\n\n async [Symbol.asyncDispose]() {\n await this.close()\n }\n}\n\nexport const createOzoneDid = async (\n plcUrl: string,\n keypair: Keypair,\n): Promise<DidString> => {\n const plcClient = new plc.Client(plcUrl)\n const plcOp = await plc.signOperation(\n {\n type: 'plc_operation',\n alsoKnownAs: [],\n rotationKeys: [keypair.did()],\n verificationMethods: {\n atproto_label: keypair.did(),\n },\n services: {\n atproto_labeler: {\n type: 'AtprotoLabeler',\n endpoint: 'https://ozone.public.url',\n },\n },\n prev: null,\n },\n keypair,\n )\n const did = await plc.didForCreateOp(plcOp)\n await plcClient.sendOperation(did, plcOp)\n return did as DidString\n}\n"]}
1
+ {"version":3,"file":"ozone.js","sourceRoot":"","sources":["../src/ozone.ts"],"names":[],"mappings":"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","sourcesContent":["import * as plc from '@did-plc/lib'\nimport * as ui8 from 'uint8arrays'\nimport { AtpAgent } from '@atproto/api'\nimport { type Keypair, Secp256k1Keypair } from '@atproto/crypto'\nimport { Client, type DidString } from '@atproto/lex'\nimport * as ozone from '@atproto/ozone'\nimport { createServiceJwt } from '@atproto/xrpc-server'\nimport { ADMIN_PASSWORD, EXAMPLE_LABELER } from './const.js'\nimport getPort from './get-port.js'\nimport { ModeratorClient } from './moderator-client.js'\nimport type { DidAndKey, OzoneConfig } from './types.js'\nimport { createDidAndKey } from './util.js'\n\nexport class TestOzone {\n constructor(\n public url: string,\n public port: number,\n public server: ozone.OzoneService,\n public daemon: ozone.OzoneDaemon,\n public adminAccnt: DidAndKey,\n public moderatorAccnt: DidAndKey,\n public triageAccnt: DidAndKey,\n ) {}\n\n static async create(config: OzoneConfig): Promise<TestOzone> {\n const serviceKeypair =\n config.signingKey ?? (await Secp256k1Keypair.create({ exportable: true }))\n const signingKeyHex = ui8.toString(await serviceKeypair.export(), 'hex')\n let serverDid = config.serverDid\n if (!serverDid) {\n serverDid = await createOzoneDid(config.plcUrl, serviceKeypair)\n }\n\n const admin = await createDidAndKey({\n plcUrl: config.plcUrl,\n handle: 'admin.ozone',\n pds: 'https://pds.invalid',\n })\n\n const moderator = await createDidAndKey({\n plcUrl: config.plcUrl,\n handle: 'moderator.ozone',\n pds: 'https://pds.invalid',\n })\n\n const triage = await createDidAndKey({\n plcUrl: config.plcUrl,\n handle: 'triage.ozone',\n pds: 'https://pds.invalid',\n })\n\n const port = config.port || (await getPort())\n const url = `http://localhost:${port}`\n\n const env: ozone.OzoneEnvironment = {\n devMode: true,\n version: '0.0.0',\n port,\n didPlcUrl: config.plcUrl,\n publicUrl: url,\n serverDid,\n signingKeyHex,\n ...config,\n adminPassword: ADMIN_PASSWORD,\n adminDids: [...(config.adminDids ?? []), admin.did],\n moderatorDids: [\n ...(config.moderatorDids ?? []),\n config.appviewDid,\n moderator.did,\n ],\n triageDids: [...(config.triageDids ?? []), triage.did],\n }\n\n // Separate migration db in case migration changes some connection state that we need in the tests, e.g. \"alter database ... set ...\"\n const migrationDb = new ozone.Database({\n schema: config.dbPostgresSchema,\n url: config.dbPostgresUrl,\n })\n if (config.migration) {\n await migrationDb.migrateToOrThrow(config.migration)\n } else {\n await migrationDb.migrateToLatestOrThrow()\n }\n await migrationDb.close()\n\n const cfg = ozone.envToCfg(env)\n const secrets = ozone.envToSecrets(env)\n\n // api server\n const server = await ozone.OzoneService.create(cfg, secrets, {\n imgInvalidator: config.imgInvalidator,\n videoInvalidator: config.videoInvalidator,\n })\n await server.start()\n\n const daemon = await ozone.OzoneDaemon.create(cfg, secrets)\n await daemon.start()\n // don't do event reversal in dev-env\n await daemon.ctx.eventReverser.destroy()\n\n return new TestOzone(url, port, server, daemon, admin, moderator, triage)\n }\n\n get ctx(): ozone.AppContext {\n return this.server.ctx\n }\n\n /** @deprecated use {@link getClient} instead */\n getAgent(): AtpAgent {\n const agent = new AtpAgent({ service: this.url })\n agent.configureLabelers([EXAMPLE_LABELER])\n return agent\n }\n\n getClient(): Client {\n return new Client(this.url, {\n labelers: [EXAMPLE_LABELER],\n })\n }\n\n getModClient() {\n return new ModeratorClient(this)\n }\n\n async addAdminDid(did: DidString) {\n await this.ctx.teamService(this.ctx.db).create({\n did,\n disabled: false,\n handle: null,\n displayName: null,\n lastUpdatedBy: this.ctx.cfg.service.did,\n role: 'tools.ozone.team.defs#roleAdmin',\n })\n this.ctx.cfg.access.admins.push(did)\n }\n\n async addModeratorDid(did: DidString) {\n await this.ctx.teamService(this.ctx.db).create({\n did,\n disabled: false,\n handle: null,\n displayName: null,\n lastUpdatedBy: this.ctx.cfg.service.did,\n role: 'tools.ozone.team.defs#roleModerator',\n })\n this.ctx.cfg.access.moderators.push(did)\n }\n\n async addTriageDid(did: DidString) {\n await this.ctx.teamService(this.ctx.db).create({\n did,\n disabled: false,\n handle: null,\n displayName: null,\n lastUpdatedBy: this.ctx.cfg.service.did,\n role: 'tools.ozone.team.defs#roleTriage',\n })\n this.ctx.cfg.access.triage.push(did)\n }\n\n async createPolicies() {\n const now = new Date()\n const defaultOptions = {\n managerRole: 'tools.ozone.team.defs#roleAdmin' as const,\n scope: 'instance' as const,\n did: this.ctx.cfg.service.did,\n lastUpdatedBy: this.ctx.cfg.service.did,\n createdBy: this.ctx.cfg.service.did,\n createdAt: now,\n updatedAt: now,\n }\n await this.ctx.settingService(this.ctx.db).upsert({\n ...defaultOptions,\n key: 'tools.ozone.setting.severityLevels',\n value: {\n 'sev-2': {\n strikeCount: 2,\n expiryInDays: 90,\n },\n 'sev-4': {\n strikeCount: 4,\n expiryInDays: 365,\n },\n 'sev-7': {\n needsTakedown: true,\n description: 'Sever violation, immedate account takedown',\n },\n 'custom-sev': {\n strikeCount: 4,\n firstOccurrenceStrikeCount: 8,\n description: 'First offense harsher penalty, on subsequent less',\n },\n },\n description: 'Severity levels and strike count mapping for policies',\n })\n await this.ctx.settingService(this.ctx.db).upsert({\n ...defaultOptions,\n key: 'tools.ozone.setting.policyList',\n value: {\n 'policy-one': {\n name: 'Policy One',\n description: 'Policy for handling user behavior',\n severityLevels: {\n 'sev-1': {\n description: 'Minor infraction',\n isDefault: true,\n },\n 'sev-2': {\n description: 'Moderate infraction',\n isDefault: false,\n },\n },\n },\n 'policy-two': {\n name: 'Policy Two',\n description: 'Policy for handling user action',\n severityLevels: {\n 'sev-4': {\n description: 'Moderate infraction',\n isDefault: false,\n },\n 'sev-5': {\n description: 'Severe infraction',\n isDefault: false,\n },\n },\n },\n },\n description: 'Moderation policies to be associated with actions',\n })\n }\n\n async modHeaders(\n lxm: string,\n role: 'admin' | 'moderator' | 'triage' = 'moderator',\n ) {\n const account =\n role === 'admin'\n ? this.adminAccnt\n : role === 'moderator'\n ? this.moderatorAccnt\n : this.triageAccnt\n const jwt = await createServiceJwt({\n iss: account.did,\n aud: this.ctx.cfg.service.did,\n lxm,\n keypair: account.key,\n })\n return { authorization: `Bearer ${jwt}` }\n }\n\n async processAll() {\n await this.ctx.backgroundQueue.processAll()\n await this.daemon.processAll()\n }\n\n async close() {\n try {\n await this.server.destroy()\n } finally {\n await this.daemon.destroy()\n }\n }\n\n async [Symbol.asyncDispose]() {\n await this.close()\n }\n}\n\nexport const createOzoneDid = async (\n plcUrl: string,\n keypair: Keypair,\n): Promise<DidString> => {\n const plcClient = new plc.Client(plcUrl)\n const plcOp = await plc.signOperation(\n {\n type: 'plc_operation',\n alsoKnownAs: [],\n rotationKeys: [keypair.did()],\n verificationMethods: {\n atproto_label: keypair.did(),\n },\n services: {\n atproto_labeler: {\n type: 'AtprotoLabeler',\n endpoint: 'https://ozone.public.url',\n },\n },\n prev: null,\n },\n keypair,\n )\n const did = await plc.didForCreateOp(plcOp)\n await plcClient.sendOperation(did, plcOp)\n return did as DidString\n}\n"]}
package/dist/pds.d.ts CHANGED
@@ -1,13 +1,15 @@
1
1
  import { AtpAgent } from '@atproto/api';
2
2
  import { Client, type UriString } from '@atproto/lex';
3
- import { type AppContext, PDS } from '@atproto/pds';
3
+ import { type AppContext, type AppContextOptions, type KeyObject, PDS } from '@atproto/pds';
4
4
  import type { PdsConfig } from './types.js';
5
+ export { AtpAgent, Client, PDS };
6
+ export type { AppContextOptions, KeyObject, PdsConfig, UriString };
5
7
  export declare class TestPds {
6
8
  url: UriString;
7
9
  port: number;
8
10
  server: PDS;
9
11
  constructor(url: UriString, port: number, server: PDS);
10
- static create(config: PdsConfig): Promise<TestPds>;
12
+ static create(env: PdsConfig, overrides?: Partial<AppContextOptions>): Promise<TestPds>;
11
13
  get ctx(): AppContext;
12
14
  getAgent(): AtpAgent;
13
15
  getClient(): Client;
@@ -15,7 +17,7 @@ export declare class TestPds {
15
17
  adminAuthHeaders(): {
16
18
  authorization: string;
17
19
  };
18
- jwtSecretKey(): import("crypto").KeyObject;
20
+ jwtSecretKey(): KeyObject;
19
21
  processAll(): Promise<void>;
20
22
  close(): Promise<void>;
21
23
  [Symbol.asyncDispose](): Promise<void>;
package/dist/pds.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"pds.d.ts","sourceRoot":"","sources":["../src/pds.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAEvC,OAAO,EAAE,MAAM,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAA;AACrD,OAAO,EAAE,KAAK,UAAU,EAAE,GAAG,EAAyB,MAAM,cAAc,CAAA;AAG1E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAE3C,qBAAa,OAAO;IAET,GAAG,EAAE,SAAS;IACd,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,GAAG;IAHpB,YACS,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,GAAG,EAChB;IAEJ,OAAa,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAiDvD;IAED,IAAI,GAAG,IAAI,UAAU,CAEpB;IAED,QAAQ,IAAI,QAAQ,CAInB;IAED,SAAS,IAAI,MAAM,CAIlB;IAED,SAAS,IAAI,MAAM,CAQlB;IAED,gBAAgB;QAEZ,aAAa;MAEhB;IAED,YAAY,+BAEX;IAEK,UAAU,kBAEf;IAEK,KAAK,kBAEV;IAEK,CAAC,MAAM,CAAC,YAAY,CAAC,kBAE1B;CACF"}
1
+ {"version":3,"file":"pds.d.ts","sourceRoot":"","sources":["../src/pds.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAEvC,OAAO,EAAE,MAAM,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAA;AACrD,OAAO,EACL,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,SAAS,EACd,GAAG,EAEJ,MAAM,cAAc,CAAA;AAGrB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAG3C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAA;AAChC,YAAY,EAAE,iBAAiB,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,CAAA;AAElE,qBAAa,OAAO;IAET,GAAG,EAAE,SAAS;IACd,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,GAAG;IAHpB,YACS,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,GAAG,EAChB;IAEJ,OAAa,MAAM,CACjB,GAAG,EAAE,SAAS,EACd,SAAS,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,GACrC,OAAO,CAAC,OAAO,CAAC,CAgDlB;IAED,IAAI,GAAG,IAAI,UAAU,CAEpB;IAED,QAAQ,IAAI,QAAQ,CAInB;IAED,SAAS,IAAI,MAAM,CAIlB;IAED,SAAS,IAAI,MAAM,CAQlB;IAED,gBAAgB;QAEZ,aAAa;MAEhB;IAED,YAAY,cAEX;IAEK,UAAU,kBAEf;IAEK,KAAK,kBAEV;IAEK,CAAC,MAAM,CAAC,YAAY,CAAC,kBAE1B;CACF"}
package/dist/pds.js CHANGED
@@ -5,29 +5,26 @@ import * as ui8 from 'uint8arrays';
5
5
  import { AtpAgent } from '@atproto/api';
6
6
  import { Secp256k1Keypair, randomStr } from '@atproto/crypto';
7
7
  import { Client } from '@atproto/lex';
8
- import { PDS, createSecretKeyObject } from '@atproto/pds';
8
+ import { PDS, createSecretKeyObject, } from '@atproto/pds';
9
9
  import { ADMIN_PASSWORD, EXAMPLE_LABELER, JWT_SECRET } from './const.js';
10
10
  import getPort from './get-port.js';
11
+ // Re-export types that are part of the public API
12
+ export { AtpAgent, Client, PDS };
11
13
  export class TestPds {
12
14
  constructor(url, port, server) {
13
15
  this.url = url;
14
16
  this.port = port;
15
17
  this.server = server;
16
18
  }
17
- static async create(config) {
18
- const plcRotationKey = await Secp256k1Keypair.create({ exportable: true });
19
- const plcRotationPriv = ui8.toString(await plcRotationKey.export(), 'hex');
19
+ static async create(env, overrides) {
20
20
  const recoveryKey = (await Secp256k1Keypair.create()).did();
21
- const port = config.port || (await getPort());
21
+ const port = env.port || (await getPort());
22
22
  const url = `http://localhost:${port}`;
23
- const blobstoreLoc = path.join(os.tmpdir(), randomStr(8, 'base32'));
24
- const dataDirectory = path.join(os.tmpdir(), randomStr(8, 'base32'));
25
- await fs.mkdir(dataDirectory, { recursive: true });
26
23
  const server = await PDS.fromEnv({
27
24
  devMode: true,
28
25
  port,
29
- dataDirectory: dataDirectory,
30
- blobstoreDiskLocation: blobstoreLoc,
26
+ dataDirectory: await mkTmpdir(),
27
+ blobstoreDiskLocation: await mkTmpdir(),
31
28
  recoveryDidKey: recoveryKey,
32
29
  adminPassword: ADMIN_PASSWORD,
33
30
  jwtSecret: JWT_SECRET,
@@ -39,7 +36,9 @@ export class TestPds {
39
36
  bskyAppViewCdnUrlPattern: 'http://cdn.appview.com/%s/%s/%s',
40
37
  modServiceUrl: 'https://moderator.invalid',
41
38
  modServiceDid: 'did:example:invalid',
42
- plcRotationKeyK256PrivateKeyHex: plcRotationPriv,
39
+ plcRotationKeyK256PrivateKeyHex: overrides?.plcRotationKey
40
+ ? undefined // @NOTE the plcRotationKey might not be exportable
41
+ : await createRotationKey(),
43
42
  inviteRequired: false,
44
43
  disableSsrfProtection: true,
45
44
  serviceName: 'Development PDS',
@@ -54,8 +53,8 @@ export class TestPds {
54
53
  termsOfServiceUrl: 'https://bsky.social/about/support/tos',
55
54
  privacyPolicyUrl: 'https://bsky.social/about/support/privacy-policy',
56
55
  supportUrl: 'https://blueskyweb.zendesk.com/hc/en-us',
57
- ...config,
58
- });
56
+ ...env,
57
+ }, overrides);
59
58
  await server.start();
60
59
  return new TestPds(url, port, server);
61
60
  }
@@ -94,4 +93,14 @@ export class TestPds {
94
93
  await this.close();
95
94
  }
96
95
  }
96
+ async function createRotationKey() {
97
+ const plcRotationKey = await Secp256k1Keypair.create({ exportable: true });
98
+ const plcRotationPriv = await plcRotationKey.export();
99
+ return Buffer.from(plcRotationPriv).toString('hex');
100
+ }
101
+ async function mkTmpdir() {
102
+ const location = path.join(os.tmpdir(), randomStr(8, 'base32'));
103
+ await fs.mkdir(location, { recursive: true });
104
+ return location;
105
+ }
97
106
  //# sourceMappingURL=pds.js.map
package/dist/pds.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"pds.js","sourceRoot":"","sources":["../src/pds.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'node:fs/promises'\nimport os from 'node:os'\nimport path from 'node:path'\nimport * as ui8 from 'uint8arrays'\nimport { AtpAgent } from '@atproto/api'\nimport { Secp256k1Keypair, randomStr } from '@atproto/crypto'\nimport { Client, type UriString } from '@atproto/lex'\nimport { type AppContext, PDS, createSecretKeyObject } from '@atproto/pds'\nimport { ADMIN_PASSWORD, EXAMPLE_LABELER, JWT_SECRET } from './const.js'\nimport getPort from './get-port.js'\nimport type { PdsConfig } from './types.js'\n\nexport class TestPds {\n constructor(\n public url: UriString,\n public port: number,\n public server: PDS,\n ) {}\n\n static async create(config: PdsConfig): Promise<TestPds> {\n const plcRotationKey = await Secp256k1Keypair.create({ exportable: true })\n const plcRotationPriv = ui8.toString(await plcRotationKey.export(), 'hex')\n const recoveryKey = (await Secp256k1Keypair.create()).did()\n\n const port = config.port || (await getPort())\n const url: UriString = `http://localhost:${port}`\n\n const blobstoreLoc = path.join(os.tmpdir(), randomStr(8, 'base32'))\n const dataDirectory = path.join(os.tmpdir(), randomStr(8, 'base32'))\n await fs.mkdir(dataDirectory, { recursive: true })\n\n const server = await PDS.fromEnv({\n devMode: true,\n port,\n dataDirectory: dataDirectory,\n blobstoreDiskLocation: blobstoreLoc,\n recoveryDidKey: recoveryKey,\n adminPassword: ADMIN_PASSWORD,\n jwtSecret: JWT_SECRET,\n // @NOTE \".example\" will not actually work and is only used to display\n // multiple domains in the sing-up UI\n serviceHandleDomains: ['.test', '.example'],\n bskyAppViewUrl: 'https://appview.invalid',\n bskyAppViewDid: 'did:example:invalid',\n bskyAppViewCdnUrlPattern: 'http://cdn.appview.com/%s/%s/%s',\n modServiceUrl: 'https://moderator.invalid',\n modServiceDid: 'did:example:invalid',\n plcRotationKeyK256PrivateKeyHex: plcRotationPriv,\n inviteRequired: false,\n disableSsrfProtection: true,\n serviceName: 'Development PDS',\n primaryColor: '#f0828d',\n errorColor: 'rgb(238, 0, 78)', // rgb() notation should work too\n logoUrl:\n // Using a \"data:\" instead of a real URL to avoid making CORS requests in dev.\n // License: https://uxwing.com/license/\n // Source: https://uxwing.com/bee-icon/\n `data:image/svg+xml;base64,${Buffer.from('<svg xmlns=\"http://www.w3.org/2000/svg\" shape-rendering=\"geometricPrecision\" text-rendering=\"geometricPrecision\" image-rendering=\"optimizeQuality\" fill-rule=\"evenodd\" clip-rule=\"evenodd\" viewBox=\"0 0 503 511.623\"><path fill=\"#FFB9B9\" d=\"M379.75 85.311l90.022 89.879C503.264 128.804 502.534 31.13 476.188 0c-27.441 31.966-59.103 60.767-96.438 85.311z\"/><path fill=\"#E2828D\" d=\"M399.445 104.976l70.327 70.214c26.443-36.622 31.549-105.205 19.563-147.778-26.692 28.309-56.413 54.344-89.89 77.564z\"/><path fill=\"#FFB9B9\" d=\"M119.595 85.311L29.573 175.19C-3.919 128.804-3.189 31.13 23.156 0c27.441 31.966 59.103 60.767 96.439 85.311z\"/><path fill=\"#E2828D\" d=\"M99.899 104.976L29.573 175.19C3.13 138.568-1.976 69.985 10.01 27.412c26.692 28.309 56.413 54.344 89.889 77.564z\"/><path fill=\"#FFB9B9\" d=\"M251.5 51.303c138.898 0 251.5 103.046 251.5 230.16 0 127.114-112.602 230.16-251.5 230.16C112.6 511.623 0 408.577 0 281.463c0-127.114 112.6-230.16 251.5-230.16z\"/><path fill=\"#331400\" d=\"M138.142 188.245c16.387 0 29.672 13.283 29.672 29.672 0 16.389-13.285 29.673-29.672 29.673-16.389 0-29.675-13.284-29.675-29.673 0-16.389 13.286-29.672 29.675-29.672zM360.695 188.245c16.389 0 29.674 13.283 29.674 29.672 0 16.389-13.285 29.673-29.674 29.673-16.387 0-29.673-13.284-29.673-29.673 0-16.389 13.286-29.672 29.673-29.672z\"/><path fill=\"#F0828D\" fill-rule=\"nonzero\" d=\"M251.5 255.548c37.407 0 71.438 11.136 96.213 29.138 25.886 18.808 41.905 45.125 41.905 74.487 0 29.36-16.017 55.679-41.908 74.49-24.772 18.001-58.805 29.138-96.21 29.138-37.405 0-71.438-11.137-96.21-29.138-25.891-18.811-41.908-45.13-41.908-74.49 0-29.362 16.019-55.679 41.905-74.487 24.775-18.002 58.808-29.138 96.213-29.138z\"/><circle fill=\"#A5414B\" cx=\"203.259\" cy=\"358.515\" r=\"29.673\"/><circle fill=\"#A5414B\" cx=\"298.744\" cy=\"358.515\" r=\"29.673\"/></svg>', 'utf8').toString('base64')}`,\n homeUrl: 'https://bsky.social/',\n termsOfServiceUrl: 'https://bsky.social/about/support/tos',\n privacyPolicyUrl: 'https://bsky.social/about/support/privacy-policy',\n supportUrl: 'https://blueskyweb.zendesk.com/hc/en-us',\n ...config,\n })\n\n await server.start()\n\n return new TestPds(url, port, server)\n }\n\n get ctx(): AppContext {\n return this.server.ctx\n }\n\n getAgent(): AtpAgent {\n const agent = new AtpAgent({ service: this.url })\n agent.configureLabelers([EXAMPLE_LABELER])\n return agent\n }\n\n getClient(): Client {\n const client = new Client({ service: this.url })\n client.setLabelers([EXAMPLE_LABELER])\n return client\n }\n\n adminAuth(): string {\n return (\n 'Basic ' +\n ui8.toString(\n ui8.fromString(`admin:${ADMIN_PASSWORD}`, 'utf8'),\n 'base64pad',\n )\n )\n }\n\n adminAuthHeaders() {\n return {\n authorization: this.adminAuth(),\n }\n }\n\n jwtSecretKey() {\n return createSecretKeyObject(JWT_SECRET)\n }\n\n async processAll() {\n await this.ctx.backgroundQueue.processAll()\n }\n\n async close() {\n await this.server.destroy()\n }\n\n async [Symbol.asyncDispose]() {\n await this.close()\n }\n}\n"]}
1
+ {"version":3,"file":"pds.js","sourceRoot":"","sources":["../src/pds.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'node:fs/promises'\nimport os from 'node:os'\nimport path from 'node:path'\nimport * as ui8 from 'uint8arrays'\nimport { AtpAgent } from '@atproto/api'\nimport { Secp256k1Keypair, randomStr } from '@atproto/crypto'\nimport { Client, type UriString } from '@atproto/lex'\nimport {\n type AppContext,\n type AppContextOptions,\n type KeyObject,\n PDS,\n createSecretKeyObject,\n} from '@atproto/pds'\nimport { ADMIN_PASSWORD, EXAMPLE_LABELER, JWT_SECRET } from './const.js'\nimport getPort from './get-port.js'\nimport type { PdsConfig } from './types.js'\n\n// Re-export types that are part of the public API\nexport { AtpAgent, Client, PDS }\nexport type { AppContextOptions, KeyObject, PdsConfig, UriString }\n\nexport class TestPds {\n constructor(\n public url: UriString,\n public port: number,\n public server: PDS,\n ) {}\n\n static async create(\n env: PdsConfig,\n overrides?: Partial<AppContextOptions>,\n ): Promise<TestPds> {\n const recoveryKey = (await Secp256k1Keypair.create()).did()\n\n const port = env.port || (await getPort())\n const url: UriString = `http://localhost:${port}`\n\n const server = await PDS.fromEnv(\n {\n devMode: true,\n port,\n dataDirectory: await mkTmpdir(),\n blobstoreDiskLocation: await mkTmpdir(),\n recoveryDidKey: recoveryKey,\n adminPassword: ADMIN_PASSWORD,\n jwtSecret: JWT_SECRET,\n // @NOTE \".example\" will not actually work and is only used to display\n // multiple domains in the sing-up UI\n serviceHandleDomains: ['.test', '.example'],\n bskyAppViewUrl: 'https://appview.invalid',\n bskyAppViewDid: 'did:example:invalid',\n bskyAppViewCdnUrlPattern: 'http://cdn.appview.com/%s/%s/%s',\n modServiceUrl: 'https://moderator.invalid',\n modServiceDid: 'did:example:invalid',\n plcRotationKeyK256PrivateKeyHex: overrides?.plcRotationKey\n ? undefined // @NOTE the plcRotationKey might not be exportable\n : await createRotationKey(),\n inviteRequired: false,\n disableSsrfProtection: true,\n serviceName: 'Development PDS',\n primaryColor: '#f0828d',\n errorColor: 'rgb(238, 0, 78)', // rgb() notation should work too\n logoUrl:\n // Using a \"data:\" instead of a real URL to avoid making CORS requests in dev.\n // License: https://uxwing.com/license/\n // Source: https://uxwing.com/bee-icon/\n `data:image/svg+xml;base64,${Buffer.from('<svg xmlns=\"http://www.w3.org/2000/svg\" shape-rendering=\"geometricPrecision\" text-rendering=\"geometricPrecision\" image-rendering=\"optimizeQuality\" fill-rule=\"evenodd\" clip-rule=\"evenodd\" viewBox=\"0 0 503 511.623\"><path fill=\"#FFB9B9\" d=\"M379.75 85.311l90.022 89.879C503.264 128.804 502.534 31.13 476.188 0c-27.441 31.966-59.103 60.767-96.438 85.311z\"/><path fill=\"#E2828D\" d=\"M399.445 104.976l70.327 70.214c26.443-36.622 31.549-105.205 19.563-147.778-26.692 28.309-56.413 54.344-89.89 77.564z\"/><path fill=\"#FFB9B9\" d=\"M119.595 85.311L29.573 175.19C-3.919 128.804-3.189 31.13 23.156 0c27.441 31.966 59.103 60.767 96.439 85.311z\"/><path fill=\"#E2828D\" d=\"M99.899 104.976L29.573 175.19C3.13 138.568-1.976 69.985 10.01 27.412c26.692 28.309 56.413 54.344 89.889 77.564z\"/><path fill=\"#FFB9B9\" d=\"M251.5 51.303c138.898 0 251.5 103.046 251.5 230.16 0 127.114-112.602 230.16-251.5 230.16C112.6 511.623 0 408.577 0 281.463c0-127.114 112.6-230.16 251.5-230.16z\"/><path fill=\"#331400\" d=\"M138.142 188.245c16.387 0 29.672 13.283 29.672 29.672 0 16.389-13.285 29.673-29.672 29.673-16.389 0-29.675-13.284-29.675-29.673 0-16.389 13.286-29.672 29.675-29.672zM360.695 188.245c16.389 0 29.674 13.283 29.674 29.672 0 16.389-13.285 29.673-29.674 29.673-16.387 0-29.673-13.284-29.673-29.673 0-16.389 13.286-29.672 29.673-29.672z\"/><path fill=\"#F0828D\" fill-rule=\"nonzero\" d=\"M251.5 255.548c37.407 0 71.438 11.136 96.213 29.138 25.886 18.808 41.905 45.125 41.905 74.487 0 29.36-16.017 55.679-41.908 74.49-24.772 18.001-58.805 29.138-96.21 29.138-37.405 0-71.438-11.137-96.21-29.138-25.891-18.811-41.908-45.13-41.908-74.49 0-29.362 16.019-55.679 41.905-74.487 24.775-18.002 58.808-29.138 96.213-29.138z\"/><circle fill=\"#A5414B\" cx=\"203.259\" cy=\"358.515\" r=\"29.673\"/><circle fill=\"#A5414B\" cx=\"298.744\" cy=\"358.515\" r=\"29.673\"/></svg>', 'utf8').toString('base64')}`,\n homeUrl: 'https://bsky.social/',\n termsOfServiceUrl: 'https://bsky.social/about/support/tos',\n privacyPolicyUrl: 'https://bsky.social/about/support/privacy-policy',\n supportUrl: 'https://blueskyweb.zendesk.com/hc/en-us',\n ...env,\n },\n overrides,\n )\n\n await server.start()\n\n return new TestPds(url, port, server)\n }\n\n get ctx(): AppContext {\n return this.server.ctx\n }\n\n getAgent(): AtpAgent {\n const agent = new AtpAgent({ service: this.url })\n agent.configureLabelers([EXAMPLE_LABELER])\n return agent\n }\n\n getClient(): Client {\n const client = new Client({ service: this.url })\n client.setLabelers([EXAMPLE_LABELER])\n return client\n }\n\n adminAuth(): string {\n return (\n 'Basic ' +\n ui8.toString(\n ui8.fromString(`admin:${ADMIN_PASSWORD}`, 'utf8'),\n 'base64pad',\n )\n )\n }\n\n adminAuthHeaders() {\n return {\n authorization: this.adminAuth(),\n }\n }\n\n jwtSecretKey() {\n return createSecretKeyObject(JWT_SECRET)\n }\n\n async processAll() {\n await this.ctx.backgroundQueue.processAll()\n }\n\n async close() {\n await this.server.destroy()\n }\n\n async [Symbol.asyncDispose]() {\n await this.close()\n }\n}\n\nasync function createRotationKey(): Promise<string> {\n const plcRotationKey = await Secp256k1Keypair.create({ exportable: true })\n const plcRotationPriv = await plcRotationKey.export()\n return Buffer.from(plcRotationPriv).toString('hex')\n}\n\nasync function mkTmpdir(): Promise<string> {\n const location = path.join(os.tmpdir(), randomStr(8, 'base32'))\n await fs.mkdir(location, { recursive: true })\n return location\n}\n"]}
package/dist/types.d.ts CHANGED
@@ -37,6 +37,7 @@ export type OzoneConfig = Partial<ozone.OzoneEnvironment> & {
37
37
  migration?: string;
38
38
  signingKey?: ExportableKeypair;
39
39
  imgInvalidator?: ozone.ImageInvalidator;
40
+ videoInvalidator?: ozone.VideoInvalidator;
40
41
  };
41
42
  export type TestServerParams = {
42
43
  dbPostgresUrl: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,IAAI,MAAM,eAAe,CAAA;AAC1C,OAAO,KAAK,KAAK,KAAK,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AACjE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,KAAK,KAAK,KAAK,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,KAAK,GAAG,MAAM,cAAc,CAAA;AAExC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,GAAG;IACvD,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG;IACpD,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,EAAE,MAAM,CAAA;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,GAAG;IAC3D,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,GAAG;IAC1D,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,iBAAiB,CAAA;IAC9B,cAAc,CAAC,EAAE,KAAK,CAAC,gBAAgB,CAAA;CACxC,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,EAAE,MAAM,CAAA;IACxB,GAAG,EAAE,OAAO,CAAC,SAAS,CAAC,CAAA;IACvB,GAAG,EAAE,OAAO,CAAC,SAAS,CAAC,CAAA;IACvB,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC,CAAA;IACzB,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;IAC3B,UAAU,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAA;CACtC,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,GAAG,EAAE,SAAS,CAAA;IACd,GAAG,EAAE,OAAO,CAAA;CACb,CAAA"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,IAAI,MAAM,eAAe,CAAA;AAC1C,OAAO,KAAK,KAAK,KAAK,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AACjE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,KAAK,KAAK,KAAK,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,KAAK,GAAG,MAAM,cAAc,CAAA;AAExC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,GAAG;IACvD,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG;IACpD,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,EAAE,MAAM,CAAA;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,GAAG;IAC3D,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,GAAG;IAC1D,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,iBAAiB,CAAA;IAC9B,cAAc,CAAC,EAAE,KAAK,CAAC,gBAAgB,CAAA;IACvC,gBAAgB,CAAC,EAAE,KAAK,CAAC,gBAAgB,CAAA;CAC1C,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,EAAE,MAAM,CAAA;IACxB,GAAG,EAAE,OAAO,CAAC,SAAS,CAAC,CAAA;IACvB,GAAG,EAAE,OAAO,CAAC,SAAS,CAAC,CAAA;IACvB,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC,CAAA;IACzB,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;IAC3B,UAAU,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAA;CACtC,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,GAAG,EAAE,SAAS,CAAA;IACd,GAAG,EAAE,OAAO,CAAA;CACb,CAAA"}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type * as bsky from '@atproto/bsky'\nimport type * as bsync from '@atproto/bsync'\nimport type { ExportableKeypair, Keypair } from '@atproto/crypto'\nimport type { DidString } from '@atproto/lex'\nimport type * as ozone from '@atproto/ozone'\nimport type * as pds from '@atproto/pds'\n\nexport type IntrospectConfig = {\n port?: number\n}\n\nexport type PlcConfig = {\n port?: number\n version?: string\n}\n\nexport type PdsConfig = Partial<pds.ServerEnvironment> & {\n didPlcUrl: string\n migration?: string\n}\n\nexport type BskyConfig = Partial<bsky.ServerConfig> & {\n bsyncUrl: string\n plcUrl: string\n repoProvider: string\n dbPostgresUrl: string\n dbPostgresSchema: string\n redisHost: string\n pdsPort: number\n migration?: string\n privateKey?: string\n}\n\nexport type BsyncConfig = Partial<bsync.ServerEnvironment> & {\n dbUrl: string\n}\n\nexport type OzoneConfig = Partial<ozone.OzoneEnvironment> & {\n plcUrl: string\n appviewUrl: string\n appviewDid: string\n dbPostgresUrl: string\n migration?: string\n signingKey?: ExportableKeypair\n imgInvalidator?: ozone.ImageInvalidator\n}\n\nexport type TestServerParams = {\n dbPostgresUrl: string\n dbPostgresSchema: string\n pds: Partial<PdsConfig>\n plc: Partial<PlcConfig>\n bsky: Partial<BskyConfig>\n ozone: Partial<OzoneConfig>\n introspect: Partial<IntrospectConfig>\n}\n\nexport type DidAndKey = {\n did: DidString\n key: Keypair\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type * as bsky from '@atproto/bsky'\nimport type * as bsync from '@atproto/bsync'\nimport type { ExportableKeypair, Keypair } from '@atproto/crypto'\nimport type { DidString } from '@atproto/lex'\nimport type * as ozone from '@atproto/ozone'\nimport type * as pds from '@atproto/pds'\n\nexport type IntrospectConfig = {\n port?: number\n}\n\nexport type PlcConfig = {\n port?: number\n version?: string\n}\n\nexport type PdsConfig = Partial<pds.ServerEnvironment> & {\n didPlcUrl: string\n migration?: string\n}\n\nexport type BskyConfig = Partial<bsky.ServerConfig> & {\n bsyncUrl: string\n plcUrl: string\n repoProvider: string\n dbPostgresUrl: string\n dbPostgresSchema: string\n redisHost: string\n pdsPort: number\n migration?: string\n privateKey?: string\n}\n\nexport type BsyncConfig = Partial<bsync.ServerEnvironment> & {\n dbUrl: string\n}\n\nexport type OzoneConfig = Partial<ozone.OzoneEnvironment> & {\n plcUrl: string\n appviewUrl: string\n appviewDid: string\n dbPostgresUrl: string\n migration?: string\n signingKey?: ExportableKeypair\n imgInvalidator?: ozone.ImageInvalidator\n videoInvalidator?: ozone.VideoInvalidator\n}\n\nexport type TestServerParams = {\n dbPostgresUrl: string\n dbPostgresSchema: string\n pds: Partial<PdsConfig>\n plc: Partial<PlcConfig>\n bsky: Partial<BskyConfig>\n ozone: Partial<OzoneConfig>\n introspect: Partial<IntrospectConfig>\n}\n\nexport type DidAndKey = {\n did: DidString\n key: Keypair\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/dev-env",
3
- "version": "0.6.7",
3
+ "version": "0.6.9",
4
4
  "license": "MIT",
5
5
  "description": "Local development environment helper for atproto development",
6
6
  "keywords": [
@@ -33,26 +33,25 @@
33
33
  "dependencies": {
34
34
  "@did-plc/lib": "^0.0.1",
35
35
  "@did-plc/server": "^0.0.1",
36
- "dotenv": "^17.4.2",
37
36
  "express": "^4.18.2",
38
37
  "get-port": "^5.1.1",
39
38
  "http-terminator": "^3.2.0",
40
39
  "multiformats": "^13.0.0",
41
40
  "uint8arrays": "^5.0.0",
42
41
  "undici": "^8.5.0",
43
- "@atproto/api": "^0.20.44",
44
- "@atproto/bsky": "^0.0.280",
45
- "@atproto/crypto": "^0.5.5",
46
- "@atproto/bsync": "^0.0.44",
47
- "@atproto/identity": "^0.5.13",
48
- "@atproto/lexicon": "^0.7.14",
49
- "@atproto/ozone": "^0.4.2",
50
- "@atproto/sync": "^0.4.10",
51
- "@atproto/lex": "^0.3.11",
52
- "@atproto/pds": "^0.5.34",
42
+ "@atproto/api": "^0.22.0",
43
+ "@atproto/bsky": "^0.0.282",
44
+ "@atproto/bsync": "^0.1.1",
45
+ "@atproto/common-web": "^0.5.13",
46
+ "@atproto/crypto": "^0.5.6",
47
+ "@atproto/identity": "^0.5.15",
48
+ "@atproto/lex": "^0.3.13",
49
+ "@atproto/lexicon": "^0.7.15",
50
+ "@atproto/ozone": "^0.5.0",
51
+ "@atproto/pds": "^0.5.36",
52
+ "@atproto/sync": "^0.4.12",
53
53
  "@atproto/syntax": "^0.7.6",
54
- "@atproto/common-web": "^0.5.12",
55
- "@atproto/xrpc-server": "^0.13.1"
54
+ "@atproto/xrpc-server": "^0.13.3"
56
55
  },
57
56
  "devDependencies": {
58
57
  "@types/express": "^4.17.13"
package/src/bin.ts CHANGED
@@ -1,4 +1,3 @@
1
- import './env.js'
2
1
  import { generateMockSetup } from './mock/index.js'
3
2
  import { TestNetwork } from './network.js'
4
3
  import { mockMailer } from './util.js'
package/src/get-port.ts CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  mkdir,
6
6
  readFile,
7
7
  readdir,
8
+ rename,
8
9
  unlink,
9
10
  writeFile,
10
11
  } from 'node:fs/promises'
@@ -19,7 +20,9 @@ const lockDir = join(
19
20
  `atproto-dev-env-ports-${process.getuid?.() ?? 'unknown'}`,
20
21
  )
21
22
  const claimedPorts = new Set<string>()
22
- const ownerPath = join(lockDir, `.owner-${process.pid}-${randomUUID()}`)
23
+ const ownerId = `${process.pid}-${randomUUID()}`
24
+ const ownerPath = join(lockDir, `.owner-${ownerId}`)
25
+ const pendingOwnerPath = join(lockDir, `.pending-${ownerId}`)
23
26
 
24
27
  let initialize: Promise<void> | undefined
25
28
 
@@ -112,7 +115,8 @@ export const sweepStaleReservations = async (
112
115
 
113
116
  const initializeLockDir = async () => {
114
117
  await mkdir(lockDir, { recursive: true })
115
- await writeFile(ownerPath, String(process.pid), { flag: 'wx' })
118
+ await writeFile(pendingOwnerPath, String(process.pid), { flag: 'wx' })
119
+ await rename(pendingOwnerPath, ownerPath)
116
120
 
117
121
  const releaseSweepLock = await acquireSweepLock(lockDir, ownerPath)
118
122
  if (!releaseSweepLock) return
package/src/ozone.ts CHANGED
@@ -89,6 +89,7 @@ export class TestOzone {
89
89
  // api server
90
90
  const server = await ozone.OzoneService.create(cfg, secrets, {
91
91
  imgInvalidator: config.imgInvalidator,
92
+ videoInvalidator: config.videoInvalidator,
92
93
  })
93
94
  await server.start()
94
95
 
package/src/pds.ts CHANGED
@@ -5,11 +5,21 @@ import * as ui8 from 'uint8arrays'
5
5
  import { AtpAgent } from '@atproto/api'
6
6
  import { Secp256k1Keypair, randomStr } from '@atproto/crypto'
7
7
  import { Client, type UriString } from '@atproto/lex'
8
- import { type AppContext, PDS, createSecretKeyObject } from '@atproto/pds'
8
+ import {
9
+ type AppContext,
10
+ type AppContextOptions,
11
+ type KeyObject,
12
+ PDS,
13
+ createSecretKeyObject,
14
+ } from '@atproto/pds'
9
15
  import { ADMIN_PASSWORD, EXAMPLE_LABELER, JWT_SECRET } from './const.js'
10
16
  import getPort from './get-port.js'
11
17
  import type { PdsConfig } from './types.js'
12
18
 
19
+ // Re-export types that are part of the public API
20
+ export { AtpAgent, Client, PDS }
21
+ export type { AppContextOptions, KeyObject, PdsConfig, UriString }
22
+
13
23
  export class TestPds {
14
24
  constructor(
15
25
  public url: UriString,
@@ -17,51 +27,53 @@ export class TestPds {
17
27
  public server: PDS,
18
28
  ) {}
19
29
 
20
- static async create(config: PdsConfig): Promise<TestPds> {
21
- const plcRotationKey = await Secp256k1Keypair.create({ exportable: true })
22
- const plcRotationPriv = ui8.toString(await plcRotationKey.export(), 'hex')
30
+ static async create(
31
+ env: PdsConfig,
32
+ overrides?: Partial<AppContextOptions>,
33
+ ): Promise<TestPds> {
23
34
  const recoveryKey = (await Secp256k1Keypair.create()).did()
24
35
 
25
- const port = config.port || (await getPort())
36
+ const port = env.port || (await getPort())
26
37
  const url: UriString = `http://localhost:${port}`
27
38
 
28
- const blobstoreLoc = path.join(os.tmpdir(), randomStr(8, 'base32'))
29
- const dataDirectory = path.join(os.tmpdir(), randomStr(8, 'base32'))
30
- await fs.mkdir(dataDirectory, { recursive: true })
31
-
32
- const server = await PDS.fromEnv({
33
- devMode: true,
34
- port,
35
- dataDirectory: dataDirectory,
36
- blobstoreDiskLocation: blobstoreLoc,
37
- recoveryDidKey: recoveryKey,
38
- adminPassword: ADMIN_PASSWORD,
39
- jwtSecret: JWT_SECRET,
40
- // @NOTE ".example" will not actually work and is only used to display
41
- // multiple domains in the sing-up UI
42
- serviceHandleDomains: ['.test', '.example'],
43
- bskyAppViewUrl: 'https://appview.invalid',
44
- bskyAppViewDid: 'did:example:invalid',
45
- bskyAppViewCdnUrlPattern: 'http://cdn.appview.com/%s/%s/%s',
46
- modServiceUrl: 'https://moderator.invalid',
47
- modServiceDid: 'did:example:invalid',
48
- plcRotationKeyK256PrivateKeyHex: plcRotationPriv,
49
- inviteRequired: false,
50
- disableSsrfProtection: true,
51
- serviceName: 'Development PDS',
52
- primaryColor: '#f0828d',
53
- errorColor: 'rgb(238, 0, 78)', // rgb() notation should work too
54
- logoUrl:
55
- // Using a "data:" instead of a real URL to avoid making CORS requests in dev.
56
- // License: https://uxwing.com/license/
57
- // Source: https://uxwing.com/bee-icon/
58
- `data:image/svg+xml;base64,${Buffer.from('<svg xmlns="http://www.w3.org/2000/svg" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd" viewBox="0 0 503 511.623"><path fill="#FFB9B9" d="M379.75 85.311l90.022 89.879C503.264 128.804 502.534 31.13 476.188 0c-27.441 31.966-59.103 60.767-96.438 85.311z"/><path fill="#E2828D" d="M399.445 104.976l70.327 70.214c26.443-36.622 31.549-105.205 19.563-147.778-26.692 28.309-56.413 54.344-89.89 77.564z"/><path fill="#FFB9B9" d="M119.595 85.311L29.573 175.19C-3.919 128.804-3.189 31.13 23.156 0c27.441 31.966 59.103 60.767 96.439 85.311z"/><path fill="#E2828D" d="M99.899 104.976L29.573 175.19C3.13 138.568-1.976 69.985 10.01 27.412c26.692 28.309 56.413 54.344 89.889 77.564z"/><path fill="#FFB9B9" d="M251.5 51.303c138.898 0 251.5 103.046 251.5 230.16 0 127.114-112.602 230.16-251.5 230.16C112.6 511.623 0 408.577 0 281.463c0-127.114 112.6-230.16 251.5-230.16z"/><path fill="#331400" d="M138.142 188.245c16.387 0 29.672 13.283 29.672 29.672 0 16.389-13.285 29.673-29.672 29.673-16.389 0-29.675-13.284-29.675-29.673 0-16.389 13.286-29.672 29.675-29.672zM360.695 188.245c16.389 0 29.674 13.283 29.674 29.672 0 16.389-13.285 29.673-29.674 29.673-16.387 0-29.673-13.284-29.673-29.673 0-16.389 13.286-29.672 29.673-29.672z"/><path fill="#F0828D" fill-rule="nonzero" d="M251.5 255.548c37.407 0 71.438 11.136 96.213 29.138 25.886 18.808 41.905 45.125 41.905 74.487 0 29.36-16.017 55.679-41.908 74.49-24.772 18.001-58.805 29.138-96.21 29.138-37.405 0-71.438-11.137-96.21-29.138-25.891-18.811-41.908-45.13-41.908-74.49 0-29.362 16.019-55.679 41.905-74.487 24.775-18.002 58.808-29.138 96.213-29.138z"/><circle fill="#A5414B" cx="203.259" cy="358.515" r="29.673"/><circle fill="#A5414B" cx="298.744" cy="358.515" r="29.673"/></svg>', 'utf8').toString('base64')}`,
59
- homeUrl: 'https://bsky.social/',
60
- termsOfServiceUrl: 'https://bsky.social/about/support/tos',
61
- privacyPolicyUrl: 'https://bsky.social/about/support/privacy-policy',
62
- supportUrl: 'https://blueskyweb.zendesk.com/hc/en-us',
63
- ...config,
64
- })
39
+ const server = await PDS.fromEnv(
40
+ {
41
+ devMode: true,
42
+ port,
43
+ dataDirectory: await mkTmpdir(),
44
+ blobstoreDiskLocation: await mkTmpdir(),
45
+ recoveryDidKey: recoveryKey,
46
+ adminPassword: ADMIN_PASSWORD,
47
+ jwtSecret: JWT_SECRET,
48
+ // @NOTE ".example" will not actually work and is only used to display
49
+ // multiple domains in the sing-up UI
50
+ serviceHandleDomains: ['.test', '.example'],
51
+ bskyAppViewUrl: 'https://appview.invalid',
52
+ bskyAppViewDid: 'did:example:invalid',
53
+ bskyAppViewCdnUrlPattern: 'http://cdn.appview.com/%s/%s/%s',
54
+ modServiceUrl: 'https://moderator.invalid',
55
+ modServiceDid: 'did:example:invalid',
56
+ plcRotationKeyK256PrivateKeyHex: overrides?.plcRotationKey
57
+ ? undefined // @NOTE the plcRotationKey might not be exportable
58
+ : await createRotationKey(),
59
+ inviteRequired: false,
60
+ disableSsrfProtection: true,
61
+ serviceName: 'Development PDS',
62
+ primaryColor: '#f0828d',
63
+ errorColor: 'rgb(238, 0, 78)', // rgb() notation should work too
64
+ logoUrl:
65
+ // Using a "data:" instead of a real URL to avoid making CORS requests in dev.
66
+ // License: https://uxwing.com/license/
67
+ // Source: https://uxwing.com/bee-icon/
68
+ `data:image/svg+xml;base64,${Buffer.from('<svg xmlns="http://www.w3.org/2000/svg" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd" viewBox="0 0 503 511.623"><path fill="#FFB9B9" d="M379.75 85.311l90.022 89.879C503.264 128.804 502.534 31.13 476.188 0c-27.441 31.966-59.103 60.767-96.438 85.311z"/><path fill="#E2828D" d="M399.445 104.976l70.327 70.214c26.443-36.622 31.549-105.205 19.563-147.778-26.692 28.309-56.413 54.344-89.89 77.564z"/><path fill="#FFB9B9" d="M119.595 85.311L29.573 175.19C-3.919 128.804-3.189 31.13 23.156 0c27.441 31.966 59.103 60.767 96.439 85.311z"/><path fill="#E2828D" d="M99.899 104.976L29.573 175.19C3.13 138.568-1.976 69.985 10.01 27.412c26.692 28.309 56.413 54.344 89.889 77.564z"/><path fill="#FFB9B9" d="M251.5 51.303c138.898 0 251.5 103.046 251.5 230.16 0 127.114-112.602 230.16-251.5 230.16C112.6 511.623 0 408.577 0 281.463c0-127.114 112.6-230.16 251.5-230.16z"/><path fill="#331400" d="M138.142 188.245c16.387 0 29.672 13.283 29.672 29.672 0 16.389-13.285 29.673-29.672 29.673-16.389 0-29.675-13.284-29.675-29.673 0-16.389 13.286-29.672 29.675-29.672zM360.695 188.245c16.389 0 29.674 13.283 29.674 29.672 0 16.389-13.285 29.673-29.674 29.673-16.387 0-29.673-13.284-29.673-29.673 0-16.389 13.286-29.672 29.673-29.672z"/><path fill="#F0828D" fill-rule="nonzero" d="M251.5 255.548c37.407 0 71.438 11.136 96.213 29.138 25.886 18.808 41.905 45.125 41.905 74.487 0 29.36-16.017 55.679-41.908 74.49-24.772 18.001-58.805 29.138-96.21 29.138-37.405 0-71.438-11.137-96.21-29.138-25.891-18.811-41.908-45.13-41.908-74.49 0-29.362 16.019-55.679 41.905-74.487 24.775-18.002 58.808-29.138 96.213-29.138z"/><circle fill="#A5414B" cx="203.259" cy="358.515" r="29.673"/><circle fill="#A5414B" cx="298.744" cy="358.515" r="29.673"/></svg>', 'utf8').toString('base64')}`,
69
+ homeUrl: 'https://bsky.social/',
70
+ termsOfServiceUrl: 'https://bsky.social/about/support/tos',
71
+ privacyPolicyUrl: 'https://bsky.social/about/support/privacy-policy',
72
+ supportUrl: 'https://blueskyweb.zendesk.com/hc/en-us',
73
+ ...env,
74
+ },
75
+ overrides,
76
+ )
65
77
 
66
78
  await server.start()
67
79
 
@@ -116,3 +128,15 @@ export class TestPds {
116
128
  await this.close()
117
129
  }
118
130
  }
131
+
132
+ async function createRotationKey(): Promise<string> {
133
+ const plcRotationKey = await Secp256k1Keypair.create({ exportable: true })
134
+ const plcRotationPriv = await plcRotationKey.export()
135
+ return Buffer.from(plcRotationPriv).toString('hex')
136
+ }
137
+
138
+ async function mkTmpdir(): Promise<string> {
139
+ const location = path.join(os.tmpdir(), randomStr(8, 'base32'))
140
+ await fs.mkdir(location, { recursive: true })
141
+ return location
142
+ }
package/src/types.ts CHANGED
@@ -43,6 +43,7 @@ export type OzoneConfig = Partial<ozone.OzoneEnvironment> & {
43
43
  migration?: string
44
44
  signingKey?: ExportableKeypair
45
45
  imgInvalidator?: ozone.ImageInvalidator
46
+ videoInvalidator?: ozone.VideoInvalidator
46
47
  }
47
48
 
48
49
  export type TestServerParams = {
package/tsconfig.json CHANGED
@@ -1,4 +1,7 @@
1
1
  {
2
2
  "include": [],
3
- "references": [{ "path": "./tsconfig.build.json" }],
3
+ "references": [
4
+ { "path": "./tsconfig.build.json" },
5
+ { "path": "./tsconfig.config.json" },
6
+ ],
4
7
  }
package/dist/env.d.ts DELETED
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=env.d.ts.map
package/dist/env.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":""}
package/dist/env.js DELETED
@@ -1,10 +0,0 @@
1
- // NOTE: this file should be imported first, particularly before `@atproto/common` (for logging), to ensure that environment variables are respected in library code
2
- import { config } from 'dotenv';
3
- const env = process.env.ENV;
4
- if (env) {
5
- config({ path: `./.${env}.env` });
6
- }
7
- else {
8
- config();
9
- }
10
- //# sourceMappingURL=env.js.map
package/dist/env.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA,oKAAoK;AACpK,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE/B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAA;AAC3B,IAAI,GAAG,EAAE,CAAC;IACR,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAA;AACnC,CAAC;KAAM,CAAC;IACN,MAAM,EAAE,CAAA;AACV,CAAC","sourcesContent":["// NOTE: this file should be imported first, particularly before `@atproto/common` (for logging), to ensure that environment variables are respected in library code\nimport { config } from 'dotenv'\n\nconst env = process.env.ENV\nif (env) {\n config({ path: `./.${env}.env` })\n} else {\n config()\n}\n"]}
package/src/env.ts DELETED
@@ -1,9 +0,0 @@
1
- // NOTE: this file should be imported first, particularly before `@atproto/common` (for logging), to ensure that environment variables are respected in library code
2
- import { config } from 'dotenv'
3
-
4
- const env = process.env.ENV
5
- if (env) {
6
- config({ path: `./.${env}.env` })
7
- } else {
8
- config()
9
- }