@comapeo/core 4.4.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/dist/blob-store/downloader.d.ts +5 -2
  2. package/dist/blob-store/downloader.d.ts.map +1 -1
  3. package/dist/constants.d.ts +0 -1
  4. package/dist/constants.d.ts.map +1 -1
  5. package/dist/datatype/index.d.ts +1 -1
  6. package/dist/datatype/index.d.ts.map +1 -1
  7. package/dist/discovery/local-discovery.d.ts.map +1 -1
  8. package/dist/import-categories.d.ts +19 -0
  9. package/dist/import-categories.d.ts.map +1 -0
  10. package/dist/intl/iso639.d.ts +4 -0
  11. package/dist/intl/iso639.d.ts.map +1 -0
  12. package/dist/intl/parse-bcp-47.d.ts +22 -0
  13. package/dist/intl/parse-bcp-47.d.ts.map +1 -0
  14. package/dist/invite/invite-api.d.ts.map +1 -1
  15. package/dist/lib/drizzle-helpers.d.ts +19 -1
  16. package/dist/lib/drizzle-helpers.d.ts.map +1 -1
  17. package/dist/mapeo-manager.d.ts +15 -9
  18. package/dist/mapeo-manager.d.ts.map +1 -1
  19. package/dist/mapeo-project.d.ts +4968 -3017
  20. package/dist/mapeo-project.d.ts.map +1 -1
  21. package/dist/schema/client.d.ts +246 -232
  22. package/dist/schema/client.d.ts.map +1 -1
  23. package/dist/schema/comapeo-to-drizzle.d.ts +65 -0
  24. package/dist/schema/comapeo-to-drizzle.d.ts.map +1 -0
  25. package/dist/schema/json-schema-to-drizzle.d.ts +18 -0
  26. package/dist/schema/json-schema-to-drizzle.d.ts.map +1 -0
  27. package/dist/schema/project.d.ts +2711 -1835
  28. package/dist/schema/project.d.ts.map +1 -1
  29. package/dist/schema/types.d.ts +73 -66
  30. package/dist/schema/types.d.ts.map +1 -1
  31. package/dist/translation-api.d.ts +111 -189
  32. package/dist/translation-api.d.ts.map +1 -1
  33. package/dist/utils.d.ts +10 -0
  34. package/dist/utils.d.ts.map +1 -1
  35. package/drizzle/client/0004_glorious_shape.sql +1 -0
  36. package/drizzle/client/meta/0000_snapshot.json +13 -9
  37. package/drizzle/client/meta/0001_snapshot.json +13 -9
  38. package/drizzle/client/meta/0002_snapshot.json +13 -9
  39. package/drizzle/client/meta/0003_snapshot.json +13 -9
  40. package/drizzle/client/meta/0004_snapshot.json +239 -0
  41. package/drizzle/client/meta/_journal.json +7 -0
  42. package/drizzle/project/meta/0000_snapshot.json +43 -24
  43. package/drizzle/project/meta/0001_snapshot.json +47 -26
  44. package/drizzle/project/meta/0002_snapshot.json +47 -26
  45. package/package.json +16 -8
  46. package/src/constants.js +0 -3
  47. package/src/datatype/index.js +8 -5
  48. package/src/discovery/local-discovery.js +3 -2
  49. package/src/import-categories.js +364 -0
  50. package/src/index-writer/index.js +1 -1
  51. package/src/intl/iso639.js +8118 -0
  52. package/src/intl/parse-bcp-47.js +91 -0
  53. package/src/invite/invite-api.js +2 -0
  54. package/src/lib/drizzle-helpers.js +70 -18
  55. package/src/mapeo-manager.js +138 -88
  56. package/src/mapeo-project.js +56 -218
  57. package/src/roles.js +1 -1
  58. package/src/schema/client.js +22 -28
  59. package/src/schema/comapeo-to-drizzle.js +57 -0
  60. package/src/schema/{schema-to-drizzle.js → json-schema-to-drizzle.js} +25 -25
  61. package/src/schema/project.js +24 -37
  62. package/src/schema/types.ts +138 -99
  63. package/src/translation-api.js +64 -12
  64. package/src/utils.js +13 -0
  65. package/dist/config-import.d.ts +0 -74
  66. package/dist/config-import.d.ts.map +0 -1
  67. package/dist/schema/schema-to-drizzle.d.ts +0 -20
  68. package/dist/schema/schema-to-drizzle.d.ts.map +0 -1
  69. package/dist/schema/utils.d.ts +0 -55
  70. package/dist/schema/utils.d.ts.map +0 -1
  71. package/src/config-import.js +0 -603
  72. package/src/schema/utils.js +0 -51
@@ -3,47 +3,34 @@
3
3
  import { blob, sqliteTable, text } from 'drizzle-orm/sqlite-core'
4
4
  import { dereferencedDocSchemas as schemas } from '@comapeo/schema'
5
5
  import { NAMESPACES } from '../constants.js'
6
- import { jsonSchemaToDrizzleColumns as toColumns } from './schema-to-drizzle.js'
7
- import { backlinkTable } from './utils.js'
6
+ import {
7
+ comapeoSchemaToDrizzleTable as toDrizzle,
8
+ backlinkTable,
9
+ } from './comapeo-to-drizzle.js'
8
10
 
9
- export const translationTable = sqliteTable(
10
- 'translation',
11
- toColumns(schemas.translation)
12
- )
13
- export const observationTable = sqliteTable(
14
- 'observation',
15
- toColumns(schemas.observation)
16
- )
17
- export const trackTable = sqliteTable('track', toColumns(schemas.track))
18
- export const remoteDetectionAlertTable = sqliteTable(
19
- 'remoteDetectionAlert',
20
- toColumns(schemas.remoteDetectionAlert)
21
- )
22
- export const presetTable = sqliteTable('preset', toColumns(schemas.preset))
23
- export const fieldTable = sqliteTable('field', toColumns(schemas.field))
24
- export const coreOwnershipTable = sqliteTable(
25
- 'coreOwnership',
26
- toColumns(schemas.coreOwnership)
27
- )
28
- export const roleTable = sqliteTable('role', toColumns(schemas.role))
29
- export const deviceInfoTable = sqliteTable(
30
- 'deviceInfo',
31
- toColumns(schemas.deviceInfo)
32
- )
33
- export const iconTable = sqliteTable('icon', toColumns(schemas.icon))
11
+ export const translationTable = toDrizzle(schemas.translation)
12
+ export const observationTable = toDrizzle(schemas.observation)
13
+ export const trackTable = toDrizzle(schemas.track)
14
+ export const remoteDetectionAlertTable = toDrizzle(schemas.remoteDetectionAlert)
15
+ export const presetTable = toDrizzle(schemas.preset)
16
+ export const fieldTable = toDrizzle(schemas.field)
17
+ export const coreOwnershipTable = toDrizzle(schemas.coreOwnership)
18
+ export const roleTable = toDrizzle(schemas.role)
19
+ export const deviceInfoTable = toDrizzle(schemas.deviceInfo)
20
+ export const iconTable = toDrizzle(schemas.icon)
34
21
 
35
- export const translationBacklinkTable = backlinkTable(translationTable)
36
- export const observationBacklinkTable = backlinkTable(observationTable)
37
- export const trackBacklinkTable = backlinkTable(trackTable)
22
+ export const translationBacklinkTable = backlinkTable('translation')
23
+ export const observationBacklinkTable = backlinkTable('observation')
24
+ export const trackBacklinkTable = backlinkTable('track')
38
25
  export const remoteDetectionAlertBacklinkTable = backlinkTable(
39
- remoteDetectionAlertTable
26
+ 'remoteDetectionAlert'
40
27
  )
41
- export const presetBacklinkTable = backlinkTable(presetTable)
42
- export const fieldBacklinkTable = backlinkTable(fieldTable)
43
- export const coreOwnershipBacklinkTable = backlinkTable(coreOwnershipTable)
44
- export const roleBacklinkTable = backlinkTable(roleTable)
45
- export const deviceInfoBacklinkTable = backlinkTable(deviceInfoTable)
46
- export const iconBacklinkTable = backlinkTable(iconTable)
28
+ export const presetBacklinkTable = backlinkTable('preset')
29
+ export const fieldBacklinkTable = backlinkTable('field')
30
+ export const coreOwnershipBacklinkTable = backlinkTable('coreOwnership')
31
+ export const roleBacklinkTable = backlinkTable('role')
32
+ export const deviceInfoBacklinkTable = backlinkTable('deviceInfo')
33
+ export const iconBacklinkTable = backlinkTable('icon')
47
34
 
48
35
  export const coresTable = sqliteTable('cores', {
49
36
  publicKey: blob('publicKey', { mode: 'buffer' }).notNull(),
@@ -3,101 +3,86 @@ import {
3
3
  JSONSchema7 as JSONSchema7Writable,
4
4
  JSONSchema7Type,
5
5
  } from 'json-schema'
6
+ import type {
7
+ SQLiteBooleanBuilder,
8
+ SQLiteIntegerBuilder,
9
+ SQLiteRealBuilder,
10
+ SQLiteTableWithColumns,
11
+ SQLiteTextBuilder,
12
+ SQLiteTextJsonBuilder,
13
+ } from 'drizzle-orm/sqlite-core'
14
+ import type {
15
+ $Type,
16
+ BuildColumns,
17
+ ColumnBuilderBase,
18
+ HasDefault,
19
+ IsPrimaryKey,
20
+ NotNull,
21
+ } from 'drizzle-orm'
6
22
 
7
- /** Convert optional properties to nullable */
8
- export type OptionalToNull<T extends {}> = {
9
- [K in keyof T]-?: undefined extends T[K] ? T[K] | null : T[K]
10
- }
11
23
  /** Convert a readonly array/object to writeable */
12
24
  type Writable<T> = { -readonly [P in keyof T]: T[P] }
13
- /** Type returned by text(columnName, { enum: [] }) */
14
- type TextBuilder<
15
- TName extends string,
16
- TEnum extends readonly [string, ...string[]],
17
- TNotNull extends boolean,
18
- THasDefault extends boolean
19
- > = import('drizzle-orm/sqlite-core').SQLiteTextBuilder<{
20
- name: TName
21
- data: Writable<TEnum>[number]
22
- driverParam: string
23
- columnType: 'SQLiteText'
24
- dataType: 'string'
25
- enumValues: Writable<TEnum>
26
- notNull: TNotNull
27
- hasDefault: THasDefault
28
- }>
29
-
30
- /** Type returned by integer(columnName, { mode: 'boolean' }) */
31
- type BooleanBuilder<
32
- TName extends string,
33
- TNotNull extends boolean,
34
- THasDefault extends boolean
35
- > = import('drizzle-orm/sqlite-core').SQLiteBooleanBuilder<{
36
- name: TName
37
- data: boolean
38
- driverParam: number
39
- columnType: 'SQLiteBoolean'
40
- dataType: 'boolean'
41
- notNull: TNotNull
42
- hasDefault: THasDefault
43
- enumValues: undefined
44
- }>
45
-
46
- /** Type returned by real(columnName) */
47
- type RealBuilder<
48
- TName extends string,
49
- TNotNull extends boolean,
50
- THasDefault extends boolean
51
- > = import('drizzle-orm/sqlite-core').SQLiteRealBuilder<{
52
- name: TName
53
- data: number
54
- driverParam: number
55
- columnType: 'SQLiteReal'
56
- dataType: 'number'
57
- notNull: TNotNull
58
- hasDefault: THasDefault
59
- enumValues: undefined
60
- }>
61
-
62
- /** Type returned by integer(columnName) */
63
- type IntegerBuilder<
64
- TName extends string,
65
- TNotNull extends boolean,
66
- THasDefault extends boolean
67
- > = import('drizzle-orm/sqlite-core').SQLiteIntegerBuilder<{
68
- name: TName
69
- data: number
70
- driverParam: number
71
- columnType: 'SQLiteInteger'
72
- dataType: 'number'
73
- notNull: TNotNull
74
- hasDefault: THasDefault
75
- enumValues: undefined
76
- }>
77
-
78
- /** Type returned by the `customJson` custom type */
79
- type JsonBuilder<
80
- TName extends string,
81
- TData extends unknown,
82
- TNotNull extends boolean,
83
- THasDefault extends boolean
84
- > = import('drizzle-orm/sqlite-core').SQLiteCustomColumnBuilder<{
85
- name: TName
86
- data: TData
87
- dataType: 'custom'
88
- driverParam: string
89
- columnType: 'SQLiteCustomColumn'
90
- notNull: TNotNull
91
- hasDefault: THasDefault
92
- enumValues: undefined
93
- }>
94
25
 
95
26
  export type JSONSchema7 = ReadonlyDeep<JSONSchema7Writable>
96
27
  type JsonSchema7Properties = { readonly [K: string]: JSONSchema7 }
97
- export type JSONSchema7WithProps = Omit<JSONSchema7, 'properties'> & {
28
+ export type JSONSchema7Object = Omit<JSONSchema7, 'properties' | 'type'> & {
29
+ readonly type: 'object'
98
30
  readonly properties: JsonSchema7Properties
99
31
  }
100
32
 
33
+ /**
34
+ * Create a Drizzle SQLite table definition from a JSONSchema object. All
35
+ * top-level properties map to SQLite columns, with `required` properties marked
36
+ * as `NOT NULL`, and JSONSchema `default` will map to SQLite defaults.
37
+ *
38
+ * Any properties that are of type `object` or `array` in the JSONSchema will be
39
+ * mapped to a text field, which drizzle will parse and stringify. Types for
40
+ * `object` and `array` properties will be derived from `TObjectType`.
41
+ */
42
+ export type JsonSchemaToDrizzleSqliteTable<
43
+ /** Typescript type for the object defined in the JSONSchema */
44
+ TObjectType extends { [K in keyof TSchema['properties']]?: any },
45
+ /** The JSONSchema object schema */
46
+ TSchema extends JSONSchema7Object,
47
+ /** Name of the table to create */
48
+ TTableName extends string,
49
+ /** Additional columns to add to the table definition (e.g. not defined in JSONSchema ) */
50
+ TColumnsMap extends Record<string, ColumnBuilderBase> = {},
51
+ /** Name of the property to use as primary key */
52
+ TPrimaryKey extends keyof TSchema['properties'] | undefined = undefined
53
+ > = SQLiteTableWithColumns<{
54
+ name: TTableName
55
+ schema: undefined
56
+ columns: BuildColumns<
57
+ TTableName,
58
+ JsonSchemaToDrizzleColumns<TObjectType, TSchema, TPrimaryKey> & TColumnsMap,
59
+ 'sqlite'
60
+ >
61
+ dialect: 'sqlite'
62
+ }>
63
+
64
+ /**
65
+ * Convert a JSONSchema Object Schema to a Drizzle Columns map (e.g. parameter
66
+ * for `sqliteTable()`). All top-level properties map to SQLite columns, with
67
+ * `required` properties marked as `NOT NULL`, and JSONSchema `default` will map
68
+ * to SQLite defaults.
69
+ *
70
+ * Any properties that are of type `object` or `array` in the JSONSchema will be
71
+ * mapped to a text field, which drizzle will parse and stringify. Types for
72
+ * `object` and `array` properties will be derived from `TObjectType`.
73
+ */
74
+ type JsonSchemaToDrizzleColumns<
75
+ TObjectType extends { [K in keyof TSchema['properties']]?: any },
76
+ TSchema extends JSONSchema7Object,
77
+ TPrimaryKey extends keyof TSchema['properties'] | undefined = undefined
78
+ > = AddJSONSchemaDefaults<
79
+ TSchema,
80
+ AddJSONSchemaRequired<
81
+ TSchema,
82
+ SchemaToDrizzleColumnsBase<TSchema, TObjectType>
83
+ >
84
+ >
85
+
101
86
  /** Get the type of a JSONSchema string: array of constants for an enum,
102
87
  otherwise string[]. Strangeness is to convert it into the format expected by
103
88
  drizzle, which results in the correct type for the field from SQLite */
@@ -111,13 +96,12 @@ type Enum<
111
96
  : [string, ...string[]]
112
97
 
113
98
  /** True if JSONSchema object has a default */
114
- type HasDefault<T extends JSONSchema7> = T['default'] extends JSONSchema7Type
115
- ? true
116
- : false
99
+ type HasJSONSchemaDefault<T extends JSONSchema7> =
100
+ T['default'] extends JSONSchema7Type ? true : false
117
101
 
118
102
  /** True if JSONSchema value is required */
119
- type IsRequired<
120
- T extends JSONSchema7WithProps,
103
+ type IsJSONSchemaRequired<
104
+ T extends JSONSchema7Object,
121
105
  U extends string,
122
106
  V extends JSONSchema7['required'] = T['required']
123
107
  > = V extends readonly any[] ? Includes<V, U> : false
@@ -131,23 +115,78 @@ type IsRequired<
131
115
  * stringify. Types for parsed JSON will be derived from MapeoDoc types.
132
116
  */
133
117
  export type SchemaToDrizzleColumns<
134
- T extends JSONSchema7WithProps,
118
+ TSchema extends JSONSchema7Object,
119
+ /** This is the type matching the JSONSchema */
120
+ TObjectType extends { [K in keyof TSchema['properties']]?: any },
121
+ TPrimaryKey extends keyof TSchema['properties'] | undefined = undefined
122
+ > = AddPrimaryKey<
123
+ AddJSONSchemaDefaults<
124
+ TSchema,
125
+ AddJSONSchemaRequired<
126
+ TSchema,
127
+ SchemaToDrizzleColumnsBase<TSchema, TObjectType>
128
+ >
129
+ >,
130
+ TPrimaryKey
131
+ >
132
+
133
+ /**
134
+ * Add `HasDefault` to columns if the JSONSchema has a default for that property
135
+ */
136
+ type AddJSONSchemaDefaults<
137
+ TJSONSchema extends JSONSchema7Object,
138
+ TColumns extends Record<string, ColumnBuilderBase>,
139
+ U extends JsonSchema7Properties = TJSONSchema['properties']
140
+ > = {
141
+ [K in keyof TColumns]: K extends keyof U
142
+ ? HasJSONSchemaDefault<U[K]> extends true
143
+ ? HasDefault<TColumns[K]>
144
+ : TColumns[K]
145
+ : TColumns[K]
146
+ }
147
+
148
+ /**
149
+ * Mark columns as NotNull if they are required in the JSONSchema
150
+ */
151
+ type AddJSONSchemaRequired<
152
+ TJSONSchema extends JSONSchema7Object,
153
+ TColumns extends Record<string, ColumnBuilderBase>
154
+ > = {
155
+ [K in keyof TColumns]: K extends string
156
+ ? IsJSONSchemaRequired<TJSONSchema, K> extends true
157
+ ? NotNull<TColumns[K]>
158
+ : TColumns[K]
159
+ : TColumns[K]
160
+ }
161
+
162
+ type AddPrimaryKey<
163
+ TColumns extends Record<string, ColumnBuilderBase>,
164
+ TKey extends keyof TColumns | undefined
165
+ > = TKey extends string
166
+ ? Omit<TColumns, TKey> & { [K in TKey]: IsPrimaryKey<TColumns[TKey]> }
167
+ : TColumns
168
+
169
+ /**
170
+ * Map JSONSchema object properties to Drizzle column types.
171
+ */
172
+ type SchemaToDrizzleColumnsBase<
173
+ TSchema extends JSONSchema7Object,
135
174
  TObjectType extends { [K in keyof U]?: any },
136
- U extends JsonSchema7Properties = T['properties']
175
+ U extends JsonSchema7Properties = TSchema['properties']
137
176
  > = {
138
177
  [K in keyof U]: K extends string
139
178
  ? U[K]['type'] extends 'string'
140
- ? TextBuilder<K, Enum<U[K]>, IsRequired<T, K>, HasDefault<U[K]>>
179
+ ? SQLiteTextBuilder<Enum<U[K]>>
141
180
  : U[K]['type'] extends 'boolean'
142
- ? BooleanBuilder<K, IsRequired<T, K>, HasDefault<U[K]>>
181
+ ? SQLiteBooleanBuilder
143
182
  : U[K]['type'] extends 'number'
144
- ? RealBuilder<K, IsRequired<T, K>, HasDefault<U[K]>>
183
+ ? SQLiteRealBuilder
145
184
  : U[K]['type'] extends 'integer'
146
- ? IntegerBuilder<K, IsRequired<T, K>, HasDefault<U[K]>>
185
+ ? SQLiteIntegerBuilder
147
186
  : U[K]['type'] extends 'array' | 'object'
148
- ? JsonBuilder<K, TObjectType[K], IsRequired<T, K>, HasDefault<U[K]>>
187
+ ? $Type<SQLiteTextJsonBuilder, TObjectType[K]>
149
188
  : never
150
189
  : never
151
- } & { forks: JsonBuilder<'forks', string[], true, false> }
190
+ } & { forks: $Type<SQLiteTextJsonBuilder, string[]> }
152
191
 
153
192
  export type NonEmptyArray<T> = [T, ...T[]]
@@ -1,9 +1,11 @@
1
- import { and, sql } from 'drizzle-orm'
1
+ import { and, eq, inArray, sql } from 'drizzle-orm'
2
2
  import { kCreateWithDocId, kSelect } from './datatype/index.js'
3
3
  import { deNullify, hashObject } from './utils.js'
4
4
  import { nullIfNotFound } from './errors.js'
5
5
  import { omit } from './lib/omit.js'
6
- /** @import { Translation, TranslationValue } from '@comapeo/schema' */
6
+ import { iso6391To6393, iso6393To6391 } from './intl/iso639.js'
7
+ import { translationTable } from './schema/project.js'
8
+ /** @import { MapeoDoc, Translation, TranslationValue } from '@comapeo/schema' */
7
9
  /** @import { SetOptional } from 'type-fest' */
8
10
 
9
11
  export const ktranslatedLanguageCodeToSchemaNames = Symbol(
@@ -14,6 +16,9 @@ export default class TranslationApi {
14
16
  * TranslationValue['languageCode'],
15
17
  * Set<import('@comapeo/schema/dist/types.js').SchemaName>>} */
16
18
  #translatedLanguageCodeToSchemaNames = new Map()
19
+ // A bug in previous versions meant that translations were stored with ISO
20
+ // 639-1 codes, so we need to handle backwards compatibility for that case.
21
+ #hasLegacyIso6391Translations = false
17
22
  #dataType
18
23
  #indexPromise
19
24
 
@@ -69,19 +74,48 @@ export default class TranslationApi {
69
74
  async get(value) {
70
75
  await this.ready()
71
76
 
72
- const docTypeIsTranslatedToLanguage =
73
- this.#translatedLanguageCodeToSchemaNames
74
- .get(value.languageCode)
75
- ?.has(
76
- /** @type {import('@comapeo/schema/dist/types.js').SchemaName} */ (
77
- value.docRefType
78
- )
77
+ // Allow this API to accept both ISO 639-1 and ISO 639-3 codes for languageCode
78
+ const normalizedLanguageCode =
79
+ value.languageCode.length === 2
80
+ ? iso6391To6393.get(value.languageCode)
81
+ : value.languageCode
82
+ if (!normalizedLanguageCode) return [] // invalid language code
83
+
84
+ const languageCodesToQuery = [normalizedLanguageCode]
85
+
86
+ // A bug in previous versions meant that translations could be stored with
87
+ // ISO 639-1 codes, so we need to query for both in this case by looking up
88
+ // the ISO 639-1 code for the langauge, and then checking whether there are
89
+ // translations for that language (looking up in our in-memory index saves
90
+ // an extra sqlite query when unnecessary)
91
+ if (this.#hasLegacyIso6391Translations) {
92
+ const iso6391LanguageCode =
93
+ value.languageCode.length === 2
94
+ ? value.languageCode
95
+ : iso6393To6391.get(value.languageCode)
96
+ const isTranslationStoredWithIso6391Code =
97
+ iso6391LanguageCode &&
98
+ this.#isTranslated(
99
+ /** @type {MapeoDoc['schemaName']} */
100
+ (value.docRefType),
101
+ iso6391LanguageCode
79
102
  )
103
+ if (isTranslationStoredWithIso6391Code) {
104
+ languageCodesToQuery.push(iso6391LanguageCode)
105
+ }
106
+ }
107
+
108
+ const docTypeIsTranslatedToLanguage =
109
+ this.#isTranslated(
110
+ /** @type {MapeoDoc['schemaName']} */
111
+ (value.docRefType),
112
+ normalizedLanguageCode
113
+ ) || languageCodesToQuery.length > 1
80
114
  if (!docTypeIsTranslatedToLanguage) return []
81
115
 
82
116
  const filters = [
83
- sql`docRefType = ${value.docRefType}`,
84
- sql`languageCode = ${value.languageCode}`,
117
+ eq(translationTable.docRefType, value.docRefType),
118
+ inArray(translationTable.languageCode, languageCodesToQuery),
85
119
  sql`json_extract(docRef, '$.docId') = ${value.docRef.docId}`,
86
120
  ]
87
121
 
@@ -94,7 +128,9 @@ export default class TranslationApi {
94
128
  filters.push(sql`propertyRef = ${value.propertyRef}`)
95
129
  }
96
130
  if (value.regionCode) {
97
- filters.push(sql`regionCode = ${value.regionCode}`)
131
+ // Use COLLATE NOCASE for case-insensitive matching because in previous
132
+ // versions we did not normalize regionCode to uppercase.
133
+ filters.push(sql`regionCode = ${value.regionCode} COLLATE NOCASE`)
98
134
  }
99
135
 
100
136
  return (await this.#dataType[kSelect]())
@@ -118,6 +154,9 @@ export default class TranslationApi {
118
154
  translatedSchemas
119
155
  )
120
156
  }
157
+ if (doc.languageCode.length === 2) {
158
+ this.#hasLegacyIso6391Translations = true
159
+ }
121
160
  translatedSchemas.add(
122
161
  /** @type {import('@comapeo/schema/dist/types.js').SchemaName} */ (
123
162
  doc.docRefType
@@ -125,6 +164,19 @@ export default class TranslationApi {
125
164
  )
126
165
  }
127
166
 
167
+ /**
168
+ * @param {MapeoDoc['schemaName']} docType
169
+ * @param {string} languageCode
170
+ * @returns {boolean}
171
+ */
172
+ #isTranslated(docType, languageCode) {
173
+ return (
174
+ this.#translatedLanguageCodeToSchemaNames
175
+ .get(languageCode)
176
+ ?.has(docType) || false
177
+ )
178
+ }
179
+
128
180
  // This should only be used by tests.
129
181
  get [ktranslatedLanguageCodeToSchemaNames]() {
130
182
  return this.#translatedLanguageCodeToSchemaNames
package/src/utils.js CHANGED
@@ -240,3 +240,16 @@ export function buildBlobId(attachment, requestedVariant) {
240
240
  driveId: attachment.driveDiscoveryId,
241
241
  }
242
242
  }
243
+
244
+ /**
245
+ * Get typed entries from an object. Use this only on objects that you are
246
+ * certain have no extra properties - TS does not check for extra properties on
247
+ * an object, which is why Object.entries is untyped by default.
248
+ *
249
+ * @template {Record<string, unknown>} T
250
+ * @param {T} obj - The object to get entries from (must _not_ have extra properties)
251
+ * @returns {import('type-fest').Entries<T>}
252
+ */
253
+ export function typedEntries(obj) {
254
+ return /** @type {import('type-fest').Entries<T>} */ (Object.entries(obj))
255
+ }
@@ -1,74 +0,0 @@
1
- /**
2
- * @typedef {yauzl.Entry} Entry
3
- */
4
- /**
5
- * @typedef {{
6
- * presets: { [id: string]: unknown }
7
- * fields: { [id: string]: unknown }
8
- * }} PresetsFile
9
- */
10
- /** @typedef {('presets' | 'fields')} ValidDocTypes */
11
- /**
12
- * @typedef {{
13
- * [lang: string]: unknown
14
- * }} TranslationsFile
15
- */
16
- /** @typedef {NonNullable<import('@comapeo/schema').ProjectSettingsValue['configMetadata']>} MetadataFile */
17
- /**
18
- * @typedef {Parameters<import('./icon-api.js').IconApi['create']>[0]} IconData
19
- */
20
- /**
21
- * @param {string} configPath
22
- */
23
- export function readConfig(configPath: string): Promise<{
24
- readonly warnings: Error[];
25
- readonly metadata: {
26
- importDate: string;
27
- name: string;
28
- buildDate: string;
29
- fileVersion: string;
30
- };
31
- close(): Promise<void>;
32
- /**
33
- * @returns {AsyncIterable<IconData>}
34
- */
35
- icons(): AsyncIterable<IconData>;
36
- /**
37
- * @returns {Iterable<{ name: string, value: import('@comapeo/schema').FieldValue }>}
38
- */
39
- fields(): Iterable<{
40
- name: string;
41
- value: import("@comapeo/schema").FieldValue;
42
- }>;
43
- /**
44
- * @returns {Iterable<{ fieldNames: string[], iconName: string | undefined, value: import('@comapeo/schema').PresetValue, name: string}>}
45
- */
46
- presets(): Iterable<{
47
- fieldNames: string[];
48
- iconName: string | undefined;
49
- value: import("@comapeo/schema").PresetValue;
50
- name: string;
51
- }>;
52
- /** @returns {Iterable<{ name: string, value:Omit<import('@comapeo/schema').TranslationValue, 'docRef'>}>} */
53
- translations(): Iterable<{
54
- name: string;
55
- value: Omit<import("@comapeo/schema").TranslationValue, "docRef">;
56
- }>;
57
- }>;
58
- export type Entry = yauzl.Entry;
59
- export type PresetsFile = {
60
- presets: {
61
- [id: string]: unknown;
62
- };
63
- fields: {
64
- [id: string]: unknown;
65
- };
66
- };
67
- export type ValidDocTypes = ("presets" | "fields");
68
- export type TranslationsFile = {
69
- [lang: string]: unknown;
70
- };
71
- export type MetadataFile = NonNullable<import("@comapeo/schema").ProjectSettingsValue["configMetadata"]>;
72
- export type IconData = Parameters<import("./icon-api.js").IconApi["create"]>[0];
73
- import yauzl from 'yauzl-promise';
74
- //# sourceMappingURL=config-import.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"config-import.d.ts","sourceRoot":"","sources":["../src/config-import.js"],"names":[],"mappings":"AAaA;;GAEG;AACH;;;;;GAKG;AAEH,sDAAsD;AACtD;;;;GAIG;AAEH,4GAA4G;AAE5G;;GAEG;AAEH;;GAEG;AACH,uCAFW,MAAM;;;;;;;;;IAmCb;;OAEG;aADU,aAAa,CAAC,QAAQ,CAAC;IAiDpC;;OAEG;cADU,QAAQ,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,iBAAiB,EAAE,UAAU,CAAA;KAAE,CAAC;IA6BpF;;OAEG;eADU,QAAQ,CAAC;QAAE,UAAU,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,KAAK,EAAE,OAAO,iBAAiB,EAAE,WAAW,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,CAAC;IAiFxI,6GAA6G;oBAA/F,QAAQ,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAC,IAAI,CAAC,OAAO,iBAAiB,EAAE,gBAAgB,EAAE,QAAQ,CAAC,CAAA;KAAC,CAAC;GAkB5G;oBA9OY,KAAK,CAAC,KAAK;0BAGX;IACR,OAAO,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAA;IAClC,MAAM,EAAG;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAA;CACnC;4BAGU,CAAC,SAAS,GAAG,QAAQ,CAAC;+BAEvB;IACZ,CAAI,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;2BAGU,WAAW,CAAC,OAAO,iBAAiB,EAAE,oBAAoB,CAAC,gBAAgB,CAAC,CAAC;uBAG9E,UAAU,CAAC,OAAO,eAAe,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;kBAjCnD,eAAe"}
@@ -1,20 +0,0 @@
1
- /** @import { MapeoDoc } from '@comapeo/schema' */
2
- /** @import { MapeoDocMap } from '../types.js' */
3
- /**
4
- Convert a JSONSchema definition to a Drizzle Columns Map (the parameter for
5
- `sqliteTable()`).
6
-
7
- **NOTE**: The return of this function is _not_ type-checked (it is coerced with
8
- `as`, because it's not possible to type-check what this function is doing), but
9
- the return type _should_ be correct when using this function.
10
- @template {import('./types.js').JSONSchema7WithProps} TSchema
11
- NB: The inline typescript checker often marks this next line as an error, but this seems to be a bug with JSDoc parsing - running `tsc` does not show this as an error.
12
- @template {import('type-fest').Get<TSchema, 'properties.schemaName.const'>} TSchemaName
13
- @template {TSchemaName extends MapeoDoc['schemaName'] ? MapeoDocMap[TSchemaName] : any} TObjectType
14
- @param {TSchema} schema
15
- @returns {import('./types.js').SchemaToDrizzleColumns<TSchema, TObjectType>}
16
- */
17
- export function jsonSchemaToDrizzleColumns<TSchema extends import("./types.js").JSONSchema7WithProps, TSchemaName extends import("type-fest").Get<TSchema, "properties.schemaName.const">, TObjectType extends TSchemaName extends MapeoDoc["schemaName"] ? MapeoDocMap[TSchemaName] : any>(schema: TSchema): import("./types.js").SchemaToDrizzleColumns<TSchema, TObjectType>;
18
- import type { MapeoDoc } from '@comapeo/schema';
19
- import type { MapeoDocMap } from '../types.js';
20
- //# sourceMappingURL=schema-to-drizzle.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"schema-to-drizzle.d.ts","sourceRoot":"","sources":["../../src/schema/schema-to-drizzle.js"],"names":[],"mappings":"AAGA,kDAAkD;AAClD,iDAAiD;AAEjD;;;;;;;;;;;;;GAaG;AACH,2CAPsD,OAAO,SAAnD,OAAQ,YAAY,EAAE,oBAAqB,EAEuB,WAAW,SAA7E,OAAQ,WAAW,EAAE,GAAG,CAAC,OAAO,EAAE,6BAA6B,CAAE,EACa,WAAW,SAAxF,WAAW,SAAS,QAAQ,CAAC,YAAY,CAAC,GAAG,WAAW,CAAC,WAAW,CAAC,GAAG,GAAI,UAC/E,OAAO,GACL,OAAO,YAAY,EAAE,sBAAsB,CAAC,OAAO,EAAE,WAAW,CAAC,CAyD1E;8BAxE6B,iBAAiB;iCACd,aAAa"}
@@ -1,55 +0,0 @@
1
- /**
2
- * Table for storing backlinks, used for indexing. There needs to be one for
3
- * each indexed document type
4
- * @param {SqliteTable} tableSchema
5
- */
6
- export function backlinkTable(tableSchema: SqliteTable): SQLiteTableWithColumns<{
7
- name: string;
8
- schema: undefined;
9
- columns: {
10
- versionId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
11
- name: "versionId";
12
- tableName: string;
13
- dataType: "string";
14
- columnType: "SQLiteText";
15
- data: string;
16
- driverParam: string;
17
- notNull: true;
18
- hasDefault: false;
19
- enumValues: [string, ...string[]];
20
- baseColumn: never;
21
- }, object>;
22
- };
23
- dialect: "sqlite";
24
- }>;
25
- /**
26
- * @param {string} tableName
27
- */
28
- export function getBacklinkTableName(tableName: string): string;
29
- /** @import { SQLiteTableWithColumns } from 'drizzle-orm/sqlite-core' */
30
- /**
31
- * @template {string} [TName=string]
32
- * @typedef {SQLiteTableWithColumns<{
33
- * name: TName;
34
- * dialect: 'sqlite';
35
- * schema: string | undefined;
36
- * columns: any
37
- * }>} SqliteTable
38
- */
39
- export const BACKLINK_TABLE_POSTFIX: "_backlink";
40
- export const customJson: <TName extends string>(dbName: TName, fieldConfig?: unknown) => import("drizzle-orm/sqlite-core").SQLiteCustomColumnBuilder<{
41
- name: TName;
42
- dataType: "custom";
43
- columnType: "SQLiteCustomColumn";
44
- data: unknown;
45
- driverParam: unknown;
46
- enumValues: undefined;
47
- }>;
48
- export type SqliteTable<TName extends string = string> = SQLiteTableWithColumns<{
49
- name: TName;
50
- dialect: "sqlite";
51
- schema: string | undefined;
52
- columns: any;
53
- }>;
54
- import type { SQLiteTableWithColumns } from 'drizzle-orm/sqlite-core';
55
- //# sourceMappingURL=utils.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/schema/utils.js"],"names":[],"mappings":"AAoBA;;;;GAIG;AACH,2CAFW,WAAW;;;;;;;;;;;;;;;;;;GAOrB;AAED;;GAEG;AACH,gDAFW,MAAM,UAIhB;AA/BD,wEAAwE;AAExE;;;;;;;;GAQG;AAEH,iDAAiD;AAqBjD;;;;;;;GAWE;wBAzCqB,KAAK,SAAf,MAAQ,aACR,uBAAuB;IAC/B,IAAI,EAAE,KAAK,CAAC;IACZ,OAAO,EAAE,QAAQ,CAAC;IAClB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,GAAG,CAAA;CACb,CAAC;4CATuC,yBAAyB"}