@hatk/hatk 0.0.1-alpha.6 → 0.0.1-alpha.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/dist/adapter.d.ts +19 -0
  2. package/dist/adapter.d.ts.map +1 -0
  3. package/dist/adapter.js +108 -0
  4. package/dist/backfill.d.ts +2 -2
  5. package/dist/backfill.d.ts.map +1 -1
  6. package/dist/backfill.js +78 -31
  7. package/dist/car.d.ts +42 -10
  8. package/dist/car.d.ts.map +1 -1
  9. package/dist/car.js +154 -14
  10. package/dist/cli.js +243 -1043
  11. package/dist/config.d.ts +31 -1
  12. package/dist/config.d.ts.map +1 -1
  13. package/dist/config.js +40 -9
  14. package/dist/database/adapter-factory.d.ts +6 -0
  15. package/dist/database/adapter-factory.d.ts.map +1 -0
  16. package/dist/database/adapter-factory.js +20 -0
  17. package/dist/database/adapters/duckdb-search.d.ts +12 -0
  18. package/dist/database/adapters/duckdb-search.d.ts.map +1 -0
  19. package/dist/database/adapters/duckdb-search.js +27 -0
  20. package/dist/database/adapters/duckdb.d.ts +25 -0
  21. package/dist/database/adapters/duckdb.d.ts.map +1 -0
  22. package/dist/database/adapters/duckdb.js +161 -0
  23. package/dist/database/adapters/sqlite-search.d.ts +23 -0
  24. package/dist/database/adapters/sqlite-search.d.ts.map +1 -0
  25. package/dist/database/adapters/sqlite-search.js +74 -0
  26. package/dist/database/adapters/sqlite.d.ts +18 -0
  27. package/dist/database/adapters/sqlite.d.ts.map +1 -0
  28. package/dist/database/adapters/sqlite.js +88 -0
  29. package/dist/{db.d.ts → database/db.d.ts} +57 -6
  30. package/dist/database/db.d.ts.map +1 -0
  31. package/dist/{db.js → database/db.js} +730 -549
  32. package/dist/database/dialect.d.ts +45 -0
  33. package/dist/database/dialect.d.ts.map +1 -0
  34. package/dist/database/dialect.js +72 -0
  35. package/dist/{fts.d.ts → database/fts.d.ts} +7 -0
  36. package/dist/database/fts.d.ts.map +1 -0
  37. package/dist/{fts.js → database/fts.js} +116 -32
  38. package/dist/database/index.d.ts +7 -0
  39. package/dist/database/index.d.ts.map +1 -0
  40. package/dist/database/index.js +6 -0
  41. package/dist/database/ports.d.ts +50 -0
  42. package/dist/database/ports.d.ts.map +1 -0
  43. package/dist/database/ports.js +1 -0
  44. package/dist/{schema.d.ts → database/schema.d.ts} +14 -3
  45. package/dist/database/schema.d.ts.map +1 -0
  46. package/dist/{schema.js → database/schema.js} +81 -41
  47. package/dist/dev-entry.d.ts +8 -0
  48. package/dist/dev-entry.d.ts.map +1 -0
  49. package/dist/dev-entry.js +112 -0
  50. package/dist/feeds.d.ts +12 -8
  51. package/dist/feeds.d.ts.map +1 -1
  52. package/dist/feeds.js +51 -6
  53. package/dist/hooks.d.ts +85 -0
  54. package/dist/hooks.d.ts.map +1 -0
  55. package/dist/hooks.js +161 -0
  56. package/dist/hydrate.d.ts +7 -6
  57. package/dist/hydrate.d.ts.map +1 -1
  58. package/dist/hydrate.js +4 -16
  59. package/dist/indexer.d.ts +22 -0
  60. package/dist/indexer.d.ts.map +1 -1
  61. package/dist/indexer.js +123 -32
  62. package/dist/labels.d.ts +36 -0
  63. package/dist/labels.d.ts.map +1 -1
  64. package/dist/labels.js +71 -6
  65. package/dist/lexicon-resolve.d.ts.map +1 -1
  66. package/dist/lexicon-resolve.js +27 -112
  67. package/dist/lexicons/com/atproto/label/defs.json +75 -0
  68. package/dist/lexicons/com/atproto/moderation/defs.json +30 -0
  69. package/dist/lexicons/com/atproto/repo/strongRef.json +24 -0
  70. package/dist/lexicons/dev/hatk/applyWrites.json +87 -0
  71. package/dist/lexicons/dev/hatk/createRecord.json +40 -0
  72. package/dist/lexicons/dev/hatk/createReport.json +48 -0
  73. package/dist/lexicons/dev/hatk/deleteRecord.json +25 -0
  74. package/dist/lexicons/dev/hatk/describeCollections.json +41 -0
  75. package/dist/lexicons/dev/hatk/describeFeeds.json +29 -0
  76. package/dist/lexicons/dev/hatk/describeLabels.json +45 -0
  77. package/dist/lexicons/dev/hatk/getFeed.json +30 -0
  78. package/dist/lexicons/dev/hatk/getPreferences.json +19 -0
  79. package/dist/lexicons/dev/hatk/getRecord.json +26 -0
  80. package/dist/lexicons/dev/hatk/getRecords.json +32 -0
  81. package/dist/lexicons/dev/hatk/putPreference.json +28 -0
  82. package/dist/lexicons/dev/hatk/putRecord.json +41 -0
  83. package/dist/lexicons/dev/hatk/searchRecords.json +32 -0
  84. package/dist/lexicons/dev/hatk/uploadBlob.json +23 -0
  85. package/dist/logger.d.ts +29 -0
  86. package/dist/logger.d.ts.map +1 -1
  87. package/dist/logger.js +29 -0
  88. package/dist/main.js +137 -67
  89. package/dist/mst.d.ts +18 -1
  90. package/dist/mst.d.ts.map +1 -1
  91. package/dist/mst.js +19 -8
  92. package/dist/oauth/db.d.ts +3 -1
  93. package/dist/oauth/db.d.ts.map +1 -1
  94. package/dist/oauth/db.js +48 -19
  95. package/dist/oauth/server.d.ts +24 -0
  96. package/dist/oauth/server.d.ts.map +1 -1
  97. package/dist/oauth/server.js +198 -22
  98. package/dist/oauth/session.d.ts +11 -0
  99. package/dist/oauth/session.d.ts.map +1 -0
  100. package/dist/oauth/session.js +65 -0
  101. package/dist/opengraph.d.ts +10 -0
  102. package/dist/opengraph.d.ts.map +1 -1
  103. package/dist/opengraph.js +80 -40
  104. package/dist/pds-proxy.d.ts +60 -0
  105. package/dist/pds-proxy.d.ts.map +1 -0
  106. package/dist/pds-proxy.js +277 -0
  107. package/dist/push.d.ts +34 -0
  108. package/dist/push.d.ts.map +1 -0
  109. package/dist/push.js +184 -0
  110. package/dist/renderer.d.ts +27 -0
  111. package/dist/renderer.d.ts.map +1 -0
  112. package/dist/renderer.js +46 -0
  113. package/dist/resolve-hatk.d.ts +6 -0
  114. package/dist/resolve-hatk.d.ts.map +1 -0
  115. package/dist/resolve-hatk.js +20 -0
  116. package/dist/response.d.ts +16 -0
  117. package/dist/response.d.ts.map +1 -0
  118. package/dist/response.js +69 -0
  119. package/dist/scanner.d.ts +21 -0
  120. package/dist/scanner.d.ts.map +1 -0
  121. package/dist/scanner.js +88 -0
  122. package/dist/seed.d.ts +19 -0
  123. package/dist/seed.d.ts.map +1 -1
  124. package/dist/seed.js +43 -4
  125. package/dist/server-init.d.ts +8 -0
  126. package/dist/server-init.d.ts.map +1 -0
  127. package/dist/server-init.js +62 -0
  128. package/dist/server.d.ts +26 -3
  129. package/dist/server.d.ts.map +1 -1
  130. package/dist/server.js +629 -635
  131. package/dist/setup.d.ts +28 -1
  132. package/dist/setup.d.ts.map +1 -1
  133. package/dist/setup.js +50 -3
  134. package/dist/templates/feed.tpl +14 -0
  135. package/dist/templates/hook.tpl +5 -0
  136. package/dist/templates/label.tpl +15 -0
  137. package/dist/templates/og.tpl +17 -0
  138. package/dist/templates/seed.tpl +11 -0
  139. package/dist/templates/setup.tpl +5 -0
  140. package/dist/templates/test-feed.tpl +19 -0
  141. package/dist/templates/test-xrpc.tpl +19 -0
  142. package/dist/templates/xrpc.tpl +41 -0
  143. package/dist/test.d.ts +1 -1
  144. package/dist/test.d.ts.map +1 -1
  145. package/dist/test.js +39 -32
  146. package/dist/views.js +1 -1
  147. package/dist/vite-plugin.d.ts +1 -1
  148. package/dist/vite-plugin.d.ts.map +1 -1
  149. package/dist/vite-plugin.js +254 -66
  150. package/dist/xrpc.d.ts +75 -11
  151. package/dist/xrpc.d.ts.map +1 -1
  152. package/dist/xrpc.js +189 -39
  153. package/package.json +14 -7
  154. package/public/admin.html +133 -54
  155. package/dist/db.d.ts.map +0 -1
  156. package/dist/fts.d.ts.map +0 -1
  157. package/dist/oauth/hooks.d.ts +0 -10
  158. package/dist/oauth/hooks.d.ts.map +0 -1
  159. package/dist/oauth/hooks.js +0 -40
  160. package/dist/schema.d.ts.map +0 -1
  161. package/dist/test-browser.d.ts +0 -14
  162. package/dist/test-browser.d.ts.map +0 -1
  163. package/dist/test-browser.js +0 -26
package/dist/labels.js CHANGED
@@ -6,13 +6,53 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
6
6
  }
7
7
  return path;
8
8
  };
9
+ /**
10
+ * Label system for applying moderation labels to records as they are indexed.
11
+ *
12
+ * Place label modules in the `labels/` directory. Each module default-exports
13
+ * an object with a `definition` (label metadata) and/or an `evaluate` function
14
+ * (rule that returns label values for a given record).
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * // labels/nsfw.ts
19
+ * import type { LabelRuleContext } from '@hatk/hatk/labels'
20
+ *
21
+ * export default {
22
+ * definition: {
23
+ * identifier: 'nsfw',
24
+ * severity: 'alert',
25
+ * blurs: 'media',
26
+ * defaultSetting: 'warn',
27
+ * locales: [{ lang: 'en', name: 'NSFW', description: 'Not safe for work' }],
28
+ * },
29
+ *
30
+ * async evaluate(ctx: LabelRuleContext): Promise<string[]> {
31
+ * if (ctx.record.value.nsfw === true) return ['nsfw']
32
+ * return []
33
+ * },
34
+ * }
35
+ * ```
36
+ */
9
37
  import { resolve } from 'node:path';
10
38
  import { readdirSync } from 'node:fs';
11
- import { querySQL, runSQL, insertLabels, getSchema } from "./db.js";
39
+ import { querySQL, runSQL, insertLabels, getSchema } from "./database/db.js";
12
40
  import { log, emit } from "./logger.js";
41
+ export function defineLabel(module) {
42
+ return { __type: 'labels', ...module };
43
+ }
13
44
  const rules = [];
14
45
  let labelDefs = [];
15
46
  let labelSrc = 'self';
47
+ /**
48
+ * Discover and load label rule modules from the `labels/` directory.
49
+ *
50
+ * Each module should default-export an object with an optional `definition`
51
+ * (label metadata like severity and blur behavior) and an optional `evaluate`
52
+ * function that returns label values to apply to a record.
53
+ *
54
+ * @param labelsDir - Absolute path to the `labels/` directory
55
+ */
16
56
  export async function initLabels(labelsDir) {
17
57
  let files;
18
58
  try {
@@ -26,7 +66,7 @@ export async function initLabels(labelsDir) {
26
66
  for (const file of files) {
27
67
  const name = file.replace(/\.(ts|js)$/, '');
28
68
  const scriptPath = resolve(labelsDir, file);
29
- const mod = await import(__rewriteRelativeImportExtension(scriptPath));
69
+ const mod = await import(__rewriteRelativeImportExtension(/* @vite-ignore */ `${scriptPath}?t=${Date.now()}`));
30
70
  const handler = mod.default;
31
71
  if (handler.definition) {
32
72
  labelDefs.push(handler.definition);
@@ -45,6 +85,24 @@ export async function initLabels(labelsDir) {
45
85
  log(`[labels] ${labelDefs.length} label definitions loaded`);
46
86
  }
47
87
  }
88
+ /** Clear all registered label definitions and rules (for hot-reload). */
89
+ export function clearLabels() {
90
+ labelDefs.length = 0;
91
+ rules.length = 0;
92
+ }
93
+ /** Register a single label module from a scanned server/ module. */
94
+ export function registerLabelModule(name, labelMod) {
95
+ if (labelMod.definition) {
96
+ labelDefs.push(labelMod.definition);
97
+ }
98
+ if (labelMod.evaluate) {
99
+ rules.push({ name, evaluate: labelMod.evaluate });
100
+ }
101
+ }
102
+ /**
103
+ * Evaluate all loaded label rules against a record and persist any resulting labels.
104
+ * Called after each record is indexed. Rule errors are logged but never block indexing.
105
+ */
48
106
  export async function runLabelRules(record) {
49
107
  if (rules.length === 0)
50
108
  return;
@@ -69,15 +127,21 @@ export async function runLabelRules(record) {
69
127
  emit('labels', 'applied', { count: allLabels.length, uri: record.uri, vals: allLabels.map((l) => l.val) });
70
128
  }
71
129
  }
130
+ /**
131
+ * Re-evaluate all label rules against every existing record in the given collections.
132
+ * Used by `/admin/rescan-labels` to apply new or updated rules retroactively.
133
+ *
134
+ * @returns Count of records scanned and new labels applied
135
+ */
72
136
  export async function rescanLabels(collections) {
73
- const beforeRows = await querySQL(`SELECT COUNT(*) as count FROM _labels`);
137
+ const beforeRows = (await querySQL(`SELECT COUNT(*) as count FROM _labels`));
74
138
  const beforeCount = Number(beforeRows[0]?.count || 0);
75
139
  let scanned = 0;
76
140
  for (const collection of collections) {
77
141
  const schema = getSchema(collection);
78
142
  if (!schema)
79
143
  continue;
80
- const rows = await querySQL(`SELECT * FROM ${schema.tableName}`);
144
+ const rows = (await querySQL(`SELECT * FROM ${schema.tableName}`));
81
145
  for (const row of rows) {
82
146
  scanned++;
83
147
  const value = {};
@@ -85,7 +149,7 @@ export async function rescanLabels(collections) {
85
149
  let v = row[col.name];
86
150
  if (v === null || v === undefined)
87
151
  continue;
88
- if (col.duckdbType === 'JSON' && typeof v === 'string') {
152
+ if (col.isJson && typeof v === 'string') {
89
153
  try {
90
154
  v = JSON.parse(v);
91
155
  }
@@ -102,10 +166,11 @@ export async function rescanLabels(collections) {
102
166
  });
103
167
  }
104
168
  }
105
- const afterRows = await querySQL(`SELECT COUNT(*) as count FROM _labels`);
169
+ const afterRows = (await querySQL(`SELECT COUNT(*) as count FROM _labels`));
106
170
  const afterCount = Number(afterRows[0]?.count || 0);
107
171
  return { scanned, labeled: afterCount - beforeCount };
108
172
  }
173
+ /** Return all label definitions discovered during {@link initLabels}. */
109
174
  export function getLabelDefinitions() {
110
175
  return labelDefs;
111
176
  }
@@ -1 +1 @@
1
- {"version":3,"file":"lexicon-resolve.d.ts","sourceRoot":"","sources":["../src/lexicon-resolve.ts"],"names":[],"mappings":"AAsMA,UAAU,OAAO;IACf,OAAO,EAAE,MAAM,CAAA;IACf,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB;AA+DD;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAgChF"}
1
+ {"version":3,"file":"lexicon-resolve.d.ts","sourceRoot":"","sources":["../src/lexicon-resolve.ts"],"names":[],"mappings":"AAoFA,UAAU,OAAO;IACf,OAAO,EAAE,MAAM,CAAA;IACf,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB;AAyFD;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAgChF"}
@@ -1,6 +1,8 @@
1
1
  // Lexicon resolver — fetches lexicons from the AT Protocol registry via DNS → DID → PDS chain
2
2
  // and recursively resolves all $ref dependencies.
3
3
  import { isValidDid } from '@bigmoves/lexicon';
4
+ import { join } from 'node:path';
5
+ import { readdirSync, readFileSync, statSync } from 'node:fs';
4
6
  // --- Authority ---
5
7
  function nsidToDomain(nsid) {
6
8
  const parts = nsid.split('.');
@@ -71,118 +73,31 @@ async function resolveDid(did, opts = {}) {
71
73
  return null;
72
74
  }
73
75
  }
74
- // --- Built-in core schemas (not published via DNS) ---
75
- const coreSchemas = {
76
- 'com.atproto.repo.strongRef': {
77
- lexicon: 1,
78
- id: 'com.atproto.repo.strongRef',
79
- description: 'A URI with a content-hash fingerprint.',
80
- defs: {
81
- main: {
82
- type: 'object',
83
- required: ['uri', 'cid'],
84
- properties: { uri: { type: 'string', format: 'at-uri' }, cid: { type: 'string', format: 'cid' } },
85
- },
86
- },
87
- },
88
- 'com.atproto.label.defs': {
89
- lexicon: 1,
90
- id: 'com.atproto.label.defs',
91
- defs: {
92
- label: {
93
- type: 'object',
94
- description: 'Metadata tag on an atproto resource (eg, repo or record).',
95
- required: ['src', 'uri', 'val', 'cts'],
96
- properties: {
97
- ver: { type: 'integer' },
98
- src: { type: 'string', format: 'did' },
99
- uri: { type: 'string', format: 'uri' },
100
- cid: { type: 'string', format: 'cid' },
101
- val: { type: 'string', maxLength: 128 },
102
- neg: { type: 'boolean' },
103
- cts: { type: 'string', format: 'datetime' },
104
- exp: { type: 'string', format: 'datetime' },
105
- sig: { type: 'bytes' },
106
- },
107
- },
108
- selfLabels: {
109
- type: 'object',
110
- description: 'Metadata tags on an atproto record, published by the author within the record.',
111
- required: ['values'],
112
- properties: { values: { type: 'array', items: { type: 'ref', ref: '#selfLabel' }, maxLength: 10 } },
113
- },
114
- selfLabel: { type: 'object', required: ['val'], properties: { val: { type: 'string', maxLength: 128 } } },
115
- labelValueDefinition: {
116
- type: 'object',
117
- description: 'Declares a label value and its expected interpretations and behaviors.',
118
- required: ['identifier', 'severity', 'blurs', 'locales'],
119
- properties: {
120
- identifier: { type: 'string', maxLength: 100 },
121
- severity: { type: 'string', knownValues: ['inform', 'alert', 'none'] },
122
- blurs: { type: 'string', knownValues: ['content', 'media', 'none'] },
123
- defaultSetting: { type: 'string', knownValues: ['ignore', 'warn', 'hide'] },
124
- adultOnly: { type: 'boolean' },
125
- locales: { type: 'array', items: { type: 'ref', ref: '#labelValueDefinitionStrings' } },
126
- },
127
- },
128
- labelValueDefinitionStrings: {
129
- type: 'object',
130
- required: ['lang', 'name', 'description'],
131
- properties: {
132
- lang: { type: 'string', format: 'language' },
133
- name: { type: 'string', maxLength: 640 },
134
- description: { type: 'string', maxLength: 100000 },
135
- },
136
- },
137
- labelValue: {
138
- type: 'string',
139
- knownValues: [
140
- '!hide',
141
- '!no-promote',
142
- '!warn',
143
- '!no-unauthenticated',
144
- 'dmca-violation',
145
- 'doxxing',
146
- 'porn',
147
- 'sexual',
148
- 'nudity',
149
- 'nsfl',
150
- 'gore',
151
- ],
152
- },
153
- },
154
- },
155
- 'com.atproto.moderation.defs': {
156
- lexicon: 1,
157
- id: 'com.atproto.moderation.defs',
158
- defs: {
159
- reasonType: {
160
- type: 'string',
161
- knownValues: [
162
- 'com.atproto.moderation.defs#reasonSpam',
163
- 'com.atproto.moderation.defs#reasonViolation',
164
- 'com.atproto.moderation.defs#reasonMisleading',
165
- 'com.atproto.moderation.defs#reasonSexual',
166
- 'com.atproto.moderation.defs#reasonRude',
167
- 'com.atproto.moderation.defs#reasonOther',
168
- 'com.atproto.moderation.defs#reasonAppeal',
169
- ],
170
- },
171
- reasonSpam: { type: 'token', description: 'Spam: frequent unwanted promotion, replies, mentions.' },
172
- reasonViolation: { type: 'token', description: 'Direct violation of server rules, laws, terms of service.' },
173
- reasonMisleading: { type: 'token', description: 'Misleading identity, affiliation, or content.' },
174
- reasonSexual: { type: 'token', description: 'Unwanted or mislabeled sexual content.' },
175
- reasonRude: { type: 'token', description: 'Rude, harassing, explicit, or otherwise unwelcoming behavior.' },
176
- reasonOther: { type: 'token', description: 'Reports not falling under another report category.' },
177
- reasonAppeal: { type: 'token', description: 'Appeal a previously taken moderation action.' },
178
- subjectType: {
179
- type: 'string',
180
- description: 'Tag describing a type of subject that might be reported.',
181
- knownValues: ['account', 'record', 'chat'],
182
- },
183
- },
184
- },
185
- };
76
+ // --- Built-in core schemas (loaded from src/lexicons/) ---
77
+ function loadCoreSchemas() {
78
+ const schemas = {};
79
+ const lexDir = join(import.meta.dirname, 'lexicons');
80
+ function walk(dir) {
81
+ for (const entry of readdirSync(dir)) {
82
+ const full = join(dir, entry);
83
+ if (statSync(full).isDirectory()) {
84
+ walk(full);
85
+ }
86
+ else if (entry.endsWith('.json')) {
87
+ const lexicon = JSON.parse(readFileSync(full, 'utf-8'));
88
+ if (lexicon.id)
89
+ schemas[lexicon.id] = lexicon;
90
+ }
91
+ }
92
+ }
93
+ try {
94
+ walk(lexDir);
95
+ }
96
+ catch { }
97
+ return schemas;
98
+ }
99
+ const coreSchemas = loadCoreSchemas();
100
+ // --- Resolver ---
186
101
  function refToNsid(ref) {
187
102
  let nsid = ref.startsWith('lex:') ? ref.slice(4) : ref;
188
103
  const hashIndex = nsid.indexOf('#');
@@ -0,0 +1,75 @@
1
+ {
2
+ "lexicon": 1,
3
+ "id": "com.atproto.label.defs",
4
+ "defs": {
5
+ "label": {
6
+ "type": "object",
7
+ "description": "Metadata tag on an atproto resource (eg, repo or record).",
8
+ "required": ["src", "uri", "val", "cts"],
9
+ "properties": {
10
+ "ver": { "type": "integer" },
11
+ "src": { "type": "string", "format": "did" },
12
+ "uri": { "type": "string", "format": "uri" },
13
+ "cid": { "type": "string", "format": "cid" },
14
+ "val": { "type": "string", "maxLength": 128 },
15
+ "neg": { "type": "boolean" },
16
+ "cts": { "type": "string", "format": "datetime" },
17
+ "exp": { "type": "string", "format": "datetime" },
18
+ "sig": { "type": "bytes" }
19
+ }
20
+ },
21
+ "selfLabels": {
22
+ "type": "object",
23
+ "description": "Metadata tags on an atproto record, published by the author within the record.",
24
+ "required": ["values"],
25
+ "properties": {
26
+ "values": { "type": "array", "items": { "type": "ref", "ref": "#selfLabel" }, "maxLength": 10 }
27
+ }
28
+ },
29
+ "selfLabel": {
30
+ "type": "object",
31
+ "required": ["val"],
32
+ "properties": {
33
+ "val": { "type": "string", "maxLength": 128 }
34
+ }
35
+ },
36
+ "labelValueDefinition": {
37
+ "type": "object",
38
+ "description": "Declares a label value and its expected interpretations and behaviors.",
39
+ "required": ["identifier", "severity", "blurs", "locales"],
40
+ "properties": {
41
+ "identifier": { "type": "string", "maxLength": 100 },
42
+ "severity": { "type": "string", "knownValues": ["inform", "alert", "none"] },
43
+ "blurs": { "type": "string", "knownValues": ["content", "media", "none"] },
44
+ "defaultSetting": { "type": "string", "knownValues": ["ignore", "warn", "hide"] },
45
+ "adultOnly": { "type": "boolean" },
46
+ "locales": { "type": "array", "items": { "type": "ref", "ref": "#labelValueDefinitionStrings" } }
47
+ }
48
+ },
49
+ "labelValueDefinitionStrings": {
50
+ "type": "object",
51
+ "required": ["lang", "name", "description"],
52
+ "properties": {
53
+ "lang": { "type": "string", "format": "language" },
54
+ "name": { "type": "string", "maxLength": 640 },
55
+ "description": { "type": "string", "maxLength": 100000 }
56
+ }
57
+ },
58
+ "labelValue": {
59
+ "type": "string",
60
+ "knownValues": [
61
+ "!hide",
62
+ "!no-promote",
63
+ "!warn",
64
+ "!no-unauthenticated",
65
+ "dmca-violation",
66
+ "doxxing",
67
+ "porn",
68
+ "sexual",
69
+ "nudity",
70
+ "nsfl",
71
+ "gore"
72
+ ]
73
+ }
74
+ }
75
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "lexicon": 1,
3
+ "id": "com.atproto.moderation.defs",
4
+ "defs": {
5
+ "reasonType": {
6
+ "type": "string",
7
+ "knownValues": [
8
+ "com.atproto.moderation.defs#reasonSpam",
9
+ "com.atproto.moderation.defs#reasonViolation",
10
+ "com.atproto.moderation.defs#reasonMisleading",
11
+ "com.atproto.moderation.defs#reasonSexual",
12
+ "com.atproto.moderation.defs#reasonRude",
13
+ "com.atproto.moderation.defs#reasonOther",
14
+ "com.atproto.moderation.defs#reasonAppeal"
15
+ ]
16
+ },
17
+ "reasonSpam": { "type": "token", "description": "Spam: frequent unwanted promotion, replies, mentions." },
18
+ "reasonViolation": { "type": "token", "description": "Direct violation of server rules, laws, terms of service." },
19
+ "reasonMisleading": { "type": "token", "description": "Misleading identity, affiliation, or content." },
20
+ "reasonSexual": { "type": "token", "description": "Unwanted or mislabeled sexual content." },
21
+ "reasonRude": { "type": "token", "description": "Rude, harassing, explicit, or otherwise unwelcoming behavior." },
22
+ "reasonOther": { "type": "token", "description": "Reports not falling under another report category." },
23
+ "reasonAppeal": { "type": "token", "description": "Appeal a previously taken moderation action." },
24
+ "subjectType": {
25
+ "type": "string",
26
+ "description": "Tag describing a type of subject that might be reported.",
27
+ "knownValues": ["account", "record", "chat"]
28
+ }
29
+ }
30
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "lexicon": 1,
3
+ "id": "com.atproto.repo.strongRef",
4
+ "description": "A URI with a content-hash fingerprint.",
5
+ "defs": {
6
+ "main": {
7
+ "type": "object",
8
+ "required": [
9
+ "uri",
10
+ "cid"
11
+ ],
12
+ "properties": {
13
+ "uri": {
14
+ "type": "string",
15
+ "format": "at-uri"
16
+ },
17
+ "cid": {
18
+ "type": "string",
19
+ "format": "cid"
20
+ }
21
+ }
22
+ }
23
+ }
24
+ }
@@ -0,0 +1,87 @@
1
+ {
2
+ "lexicon": 1,
3
+ "id": "dev.hatk.applyWrites",
4
+ "defs": {
5
+ "main": {
6
+ "type": "procedure",
7
+ "description": "Apply multiple record writes in a single atomic PDS transaction.",
8
+ "input": {
9
+ "encoding": "application/json",
10
+ "schema": {
11
+ "type": "object",
12
+ "required": ["writes"],
13
+ "properties": {
14
+ "writes": {
15
+ "type": "array",
16
+ "items": {
17
+ "type": "union",
18
+ "refs": ["#create", "#update", "#delete"]
19
+ }
20
+ }
21
+ }
22
+ }
23
+ },
24
+ "output": {
25
+ "encoding": "application/json",
26
+ "schema": {
27
+ "type": "object",
28
+ "properties": {
29
+ "results": {
30
+ "type": "array",
31
+ "items": {
32
+ "type": "union",
33
+ "refs": ["#createResult", "#updateResult", "#deleteResult"]
34
+ }
35
+ }
36
+ }
37
+ }
38
+ }
39
+ },
40
+ "create": {
41
+ "type": "object",
42
+ "required": ["collection", "value"],
43
+ "properties": {
44
+ "collection": { "type": "string" },
45
+ "rkey": { "type": "string" },
46
+ "value": { "type": "unknown" }
47
+ }
48
+ },
49
+ "update": {
50
+ "type": "object",
51
+ "required": ["collection", "rkey", "value"],
52
+ "properties": {
53
+ "collection": { "type": "string" },
54
+ "rkey": { "type": "string" },
55
+ "value": { "type": "unknown" }
56
+ }
57
+ },
58
+ "delete": {
59
+ "type": "object",
60
+ "required": ["collection", "rkey"],
61
+ "properties": {
62
+ "collection": { "type": "string" },
63
+ "rkey": { "type": "string" }
64
+ }
65
+ },
66
+ "createResult": {
67
+ "type": "object",
68
+ "required": ["uri", "cid"],
69
+ "properties": {
70
+ "uri": { "type": "string", "format": "at-uri" },
71
+ "cid": { "type": "string", "format": "cid" }
72
+ }
73
+ },
74
+ "updateResult": {
75
+ "type": "object",
76
+ "required": ["uri", "cid"],
77
+ "properties": {
78
+ "uri": { "type": "string", "format": "at-uri" },
79
+ "cid": { "type": "string", "format": "cid" }
80
+ }
81
+ },
82
+ "deleteResult": {
83
+ "type": "object",
84
+ "properties": {}
85
+ }
86
+ }
87
+ }
@@ -0,0 +1,40 @@
1
+ {
2
+ "lexicon": 1,
3
+ "id": "dev.hatk.createRecord",
4
+ "defs": {
5
+ "main": {
6
+ "type": "procedure",
7
+ "description": "Create a record via the user's PDS.",
8
+ "input": {
9
+ "encoding": "application/json",
10
+ "schema": {
11
+ "type": "object",
12
+ "required": ["collection", "repo", "record"],
13
+ "properties": {
14
+ "collection": { "type": "string" },
15
+ "repo": { "type": "string", "format": "did" },
16
+ "record": { "type": "unknown" }
17
+ }
18
+ }
19
+ },
20
+ "output": {
21
+ "encoding": "application/json",
22
+ "schema": {
23
+ "type": "object",
24
+ "properties": {
25
+ "uri": { "type": "string", "format": "at-uri" },
26
+ "cid": { "type": "string", "format": "cid" },
27
+ "commit": {
28
+ "type": "object",
29
+ "properties": {
30
+ "cid": { "type": "string", "format": "cid" },
31
+ "rev": { "type": "string" }
32
+ }
33
+ },
34
+ "validationStatus": { "type": "string" }
35
+ }
36
+ }
37
+ }
38
+ }
39
+ }
40
+ }
@@ -0,0 +1,48 @@
1
+ {
2
+ "lexicon": 1,
3
+ "id": "dev.hatk.createReport",
4
+ "defs": {
5
+ "main": {
6
+ "type": "procedure",
7
+ "description": "Report an account or record for moderation review.",
8
+ "input": {
9
+ "encoding": "application/json",
10
+ "schema": {
11
+ "type": "object",
12
+ "required": ["subject", "label"],
13
+ "properties": {
14
+ "subject": {
15
+ "type": "union",
16
+ "description": "The account or record being reported.",
17
+ "refs": ["#repoRef", "com.atproto.repo.strongRef"]
18
+ },
19
+ "label": { "type": "string", "description": "Label identifier for the report reason." },
20
+ "reason": { "type": "string", "maxLength": 2000, "description": "Optional free-text explanation." }
21
+ }
22
+ }
23
+ },
24
+ "output": {
25
+ "encoding": "application/json",
26
+ "schema": {
27
+ "type": "object",
28
+ "required": ["id", "subject", "label", "reportedBy", "createdAt"],
29
+ "properties": {
30
+ "id": { "type": "integer" },
31
+ "subject": { "type": "unknown" },
32
+ "label": { "type": "string" },
33
+ "reason": { "type": "string" },
34
+ "reportedBy": { "type": "string", "format": "did" },
35
+ "createdAt": { "type": "string", "format": "datetime" }
36
+ }
37
+ }
38
+ }
39
+ },
40
+ "repoRef": {
41
+ "type": "object",
42
+ "required": ["did"],
43
+ "properties": {
44
+ "did": { "type": "string", "format": "did" }
45
+ }
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "lexicon": 1,
3
+ "id": "dev.hatk.deleteRecord",
4
+ "defs": {
5
+ "main": {
6
+ "type": "procedure",
7
+ "description": "Delete a record via the user's PDS.",
8
+ "input": {
9
+ "encoding": "application/json",
10
+ "schema": {
11
+ "type": "object",
12
+ "required": ["collection", "rkey"],
13
+ "properties": {
14
+ "collection": { "type": "string" },
15
+ "rkey": { "type": "string" }
16
+ }
17
+ }
18
+ },
19
+ "output": {
20
+ "encoding": "application/json",
21
+ "schema": { "type": "object", "properties": {} }
22
+ }
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "lexicon": 1,
3
+ "id": "dev.hatk.describeCollections",
4
+ "defs": {
5
+ "main": {
6
+ "type": "query",
7
+ "description": "List indexed collections and their schemas.",
8
+ "output": {
9
+ "encoding": "application/json",
10
+ "schema": {
11
+ "type": "object",
12
+ "properties": {
13
+ "collections": {
14
+ "type": "array",
15
+ "items": {
16
+ "type": "object",
17
+ "required": ["collection"],
18
+ "properties": {
19
+ "collection": { "type": "string" },
20
+ "columns": {
21
+ "type": "array",
22
+ "items": {
23
+ "type": "object",
24
+ "required": ["name", "originalName", "type", "required"],
25
+ "properties": {
26
+ "name": { "type": "string" },
27
+ "originalName": { "type": "string" },
28
+ "type": { "type": "string" },
29
+ "required": { "type": "boolean" }
30
+ }
31
+ }
32
+ }
33
+ }
34
+ }
35
+ }
36
+ }
37
+ }
38
+ }
39
+ }
40
+ }
41
+ }