@trainstar/synchro-react-native 0.4.0-rc.1 → 0.4.0-rc.3

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 (34) hide show
  1. package/README.md +62 -11
  2. package/SynchroReactNative.podspec +13 -1
  3. package/android/build.gradle +1 -1
  4. package/android/src/androidTest/kotlin/com/trainstar/synchro/rn/SynchroModuleTransactionTest.kt +10 -11
  5. package/android/src/main/kotlin/com/trainstar/synchro/rn/SynchroModule.kt +22 -2
  6. package/ios/SynchroModule.swift +292 -270
  7. package/ios-test-host/main.m +48 -0
  8. package/ios-tests/SynchroModuleTransactionTests.swift +267 -1
  9. package/lib/module/NativeSynchro.js.map +1 -1
  10. package/lib/module/SynchroClient.js +26 -0
  11. package/lib/module/SynchroClient.js.map +1 -1
  12. package/lib/module/inspection.js +69 -6
  13. package/lib/module/inspection.js.map +1 -1
  14. package/lib/typescript/example/e2e/schema-check.test.d.ts +2 -0
  15. package/lib/typescript/example/e2e/schema-check.test.d.ts.map +1 -0
  16. package/lib/typescript/example/src/conformance/runner.d.ts +16 -2
  17. package/lib/typescript/example/src/conformance/runner.d.ts.map +1 -1
  18. package/lib/typescript/src/NativeSynchro.d.ts +13 -14
  19. package/lib/typescript/src/NativeSynchro.d.ts.map +1 -1
  20. package/lib/typescript/src/SynchroClient.d.ts.map +1 -1
  21. package/lib/typescript/src/inspection.d.ts +11 -4
  22. package/lib/typescript/src/inspection.d.ts.map +1 -1
  23. package/lib/typescript/src/types.d.ts +18 -0
  24. package/lib/typescript/src/types.d.ts.map +1 -1
  25. package/package.json +24 -9
  26. package/src/NativeSynchro.ts +13 -17
  27. package/src/SynchroClient.ts +32 -0
  28. package/src/inspection.ts +95 -8
  29. package/src/types.ts +21 -0
  30. package/tooling-patches/apply.cjs +163 -0
  31. package/tooling-patches/cli-clean-20.2.0.patch +21 -0
  32. package/tooling-patches/cli-config-android-20.2.0.patch +13 -0
  33. package/tooling-patches/react-native-builder-bob-0.40.18.patch +74 -0
  34. package/tooling-patches/stream-json-1.9.1.patch +40 -0
@@ -691,6 +691,14 @@ function parseTransportObservation(value: unknown, index: number): TransportObse
691
691
  if (!isRecord(value) || !TRANSPORT_OPERATION_CLASSES.includes(value.operation_class as TransportOperationClass)) {
692
692
  throw new InvalidResponseError(`Native bridge returned an invalid ${name}`);
693
693
  }
694
+ const errorCode = value.error_code;
695
+ if (errorCode !== undefined && typeof errorCode !== 'string') {
696
+ throw new InvalidResponseError(`Native bridge returned invalid ${name} error code`);
697
+ }
698
+ const retryable = value.retryable;
699
+ if (retryable !== undefined && typeof retryable !== 'boolean') {
700
+ throw new InvalidResponseError(`Native bridge returned invalid ${name} retryability`);
701
+ }
694
702
  const cursorFingerprints = value.cursor_fingerprints;
695
703
  if (cursorFingerprints !== undefined && (!Array.isArray(cursorFingerprints) || !cursorFingerprints.every((item) => typeof item === 'string'))) {
696
704
  throw new InvalidResponseError(`Native bridge returned invalid ${name} cursor fingerprints`);
@@ -699,16 +707,40 @@ function parseTransportObservation(value: unknown, index: number): TransportObse
699
707
  if (cursorFingerprintsComplete !== undefined && typeof cursorFingerprintsComplete !== 'boolean') {
700
708
  throw new InvalidResponseError(`Native bridge returned invalid ${name} cursor completeness`);
701
709
  }
710
+ const connectFacts = optionalTransportFacts(value.connect_response_facts, `${name} connect response facts`);
711
+ if (connectFacts !== undefined) {
712
+ const affected = connectFacts.affected_scope_fingerprints;
713
+ const updates = connectFacts.scope_cursor_updates;
714
+ const hashPattern = /^[0-9a-f]{64}$/;
715
+ if (
716
+ value.operation_class !== 'connect' || value.status_code !== 200 ||
717
+ Object.keys(connectFacts).length !== 7 ||
718
+ !['none', 'replace', 'rebuild_local', 'unsupported'].includes(connectFacts.action as string) ||
719
+ typeof connectFacts.schema_version !== 'number' || !Number.isSafeInteger(connectFacts.schema_version) || connectFacts.schema_version <= 0 ||
720
+ typeof connectFacts.schema_hash !== 'string' || !hashPattern.test(connectFacts.schema_hash) ||
721
+ !Array.isArray(affected) || affected.length > 16 ||
722
+ !affected.every((item, position) => typeof item === 'string' && hashPattern.test(item) && (position === 0 || (affected[position - 1] as string) < item)) ||
723
+ typeof connectFacts.affected_scopes_complete !== 'boolean' ||
724
+ !isRecord(updates) || Object.keys(updates).length > 16 ||
725
+ !Object.entries(updates).every(([scope, cursor]) => hashPattern.test(scope) && (cursor === null || typeof cursor === 'string' && hashPattern.test(cursor))) ||
726
+ typeof connectFacts.scope_cursor_updates_complete !== 'boolean'
727
+ ) {
728
+ throw new InvalidResponseError(`Native bridge returned invalid ${name} connect response facts`);
729
+ }
730
+ }
702
731
  return {
703
732
  sequence: requiredSafeInteger(value.sequence, `${name} sequence`),
704
733
  operationClass: value.operation_class as TransportOperationClass,
705
734
  statusCode: requiredSafeInteger(value.status_code, `${name} status code`),
735
+ ...(errorCode === undefined ? {} : { errorCode }),
736
+ ...(retryable === undefined ? {} : { retryable }),
706
737
  durationNanoseconds: requiredSafeInteger(value.duration_nanoseconds, `${name} duration`),
707
738
  ...(cursorFingerprints === undefined ? {} : { cursorFingerprints: [...cursorFingerprints] as string[] }),
708
739
  ...(cursorFingerprintsComplete === undefined ? {} : { cursorFingerprintsComplete }),
709
740
  ...(value.request_facts == null ? {} : { requestFacts: optionalTransportRequestFacts(value.request_facts, `${name} request facts`)! }),
710
741
  ...(value.rebuild_response_facts == null ? {} : { rebuildResponseFacts: optionalTransportFacts(value.rebuild_response_facts, `${name} rebuild response facts`)! }),
711
742
  ...(value.pull_response_facts == null ? {} : { pullResponseFacts: optionalTransportFacts(value.pull_response_facts, `${name} pull response facts`)! }),
743
+ ...(connectFacts === undefined ? {} : { connectResponseFacts: connectFacts }),
712
744
  };
713
745
  }
714
746
 
package/src/inspection.ts CHANGED
@@ -17,6 +17,9 @@ import type {
17
17
  SQLStatement,
18
18
  TransportObservationSnapshot,
19
19
  TransportOperationClass,
20
+ MigrationCheckpoint,
21
+ MigrationJournalInspection,
22
+ PhysicalSchemaColumnInspection,
20
23
  } from './types';
21
24
 
22
25
  const TRANSPORT_OPERATION_CLASSES: readonly TransportOperationClass[] = [
@@ -59,9 +62,16 @@ export interface DurableStateInspection {
59
62
  */
60
63
  export interface ClientStateSnapshotInspection {
61
64
  clientState: ClientStateInspection;
65
+ captureOverflowed: boolean;
62
66
  retainedMutations: RetainedMutationInspection[] | null;
63
67
  rejectedMutations: RetainedRejectionInspection[] | null;
64
68
  applicationRows: Row[];
69
+ migrationJournal: MigrationJournalInspection | null;
70
+ migrationJournalTruncated: boolean;
71
+ physicalSchema: PhysicalSchemaColumnInspection[];
72
+ physicalSchemaTruncated: boolean;
73
+ acceptedMutationOutcomes: Record<string, string>;
74
+ acceptedMutationOutcomesTruncated: boolean;
65
75
  }
66
76
 
67
77
  export interface SynchroInspectionOptions {
@@ -89,8 +99,15 @@ export class SynchroInspection {
89
99
  }));
90
100
  const result = await nativeForInspection(this.client).inspectClientStateSnapshot(statements);
91
101
  const snapshot = requireRecord(parseJSON(result.inspection), 'client state snapshot');
102
+ const extensions = parseCaptureExtensions(snapshot);
103
+ const clientState = requireRecord(snapshot.client_state, 'client state');
104
+ if (typeof clientState.capture_overflowed !== 'boolean') {
105
+ throw new InvalidResponseError('Native bridge returned invalid capture bound');
106
+ }
92
107
  return {
93
- clientState: parseClientStateInspection(snapshot.client_state),
108
+ ...extensions,
109
+ clientState: parseClientStateInspection(clientState),
110
+ captureOverflowed: clientState.capture_overflowed,
94
111
  retainedMutations:
95
112
  nullableArray(snapshot.retained_mutations, 'retained mutations')?.map(parseRetainedMutationInspection) ?? null,
96
113
  rejectedMutations:
@@ -137,8 +154,8 @@ export class SynchroInspection {
137
154
  }
138
155
  }
139
156
 
140
- async armTransportPause(operationClass: TransportOperationClass): Promise<void> {
141
- requireTransportOperationClass(operationClass);
157
+ async armTransportPause(operationClass: TransportOperationClass | MigrationCheckpoint): Promise<void> {
158
+ requirePauseTarget(operationClass);
142
159
  try {
143
160
  await nativeForInspection(this.client).armTransportPause(operationClass);
144
161
  } catch (error) {
@@ -146,8 +163,8 @@ export class SynchroInspection {
146
163
  }
147
164
  }
148
165
 
149
- async awaitTransportPause(operationClass: TransportOperationClass, timeoutMs: number): Promise<void> {
150
- requireTransportOperationClass(operationClass);
166
+ async awaitTransportPause(operationClass: TransportOperationClass | MigrationCheckpoint, timeoutMs: number): Promise<void> {
167
+ requirePauseTarget(operationClass);
151
168
  if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 60_000) {
152
169
  throw new InvalidResponseError('Transport pause request is invalid');
153
170
  }
@@ -248,10 +265,77 @@ function isNullableString(value: unknown): value is string | null {
248
265
  return value === null || typeof value === 'string';
249
266
  }
250
267
 
251
- function requireTransportOperationClass(value: TransportOperationClass): void {
252
- if (!TRANSPORT_OPERATION_CLASSES.includes(value)) {
253
- throw new InvalidResponseError('Transport operation class is invalid');
268
+ function requirePauseTarget(value: TransportOperationClass | MigrationCheckpoint): void {
269
+ if (value !== 'migration_prepared' && value !== 'migration_committed' &&
270
+ !TRANSPORT_OPERATION_CLASSES.includes(value as TransportOperationClass)) {
271
+ throw new InvalidResponseError('Pause target is invalid');
272
+ }
273
+ }
274
+
275
+ function parseCaptureExtensions(snapshot: Record<string, unknown>): Pick<ClientStateSnapshotInspection,
276
+ 'migrationJournal' | 'migrationJournalTruncated' | 'physicalSchema' | 'physicalSchemaTruncated' |
277
+ 'acceptedMutationOutcomes' | 'acceptedMutationOutcomesTruncated'> {
278
+ if (typeof snapshot.migration_journal_truncated !== 'boolean' ||
279
+ typeof snapshot.physical_schema_truncated !== 'boolean' || !Array.isArray(snapshot.physical_schema) ||
280
+ snapshot.physical_schema.length > 512) {
281
+ throw new InvalidResponseError('Native bridge returned invalid migration capture');
282
+ }
283
+ const journal = snapshot.migration_journal;
284
+ if (journal !== null) {
285
+ const value = requireRecord(journal, 'migration journal');
286
+ for (const key of ['source', 'target']) {
287
+ const schema = requireRecord(value[key], 'migration schema');
288
+ if (!isNonnegativeSafeInteger(schema.version) || typeof schema.hash !== 'string') {
289
+ throw new InvalidResponseError('Native bridge returned invalid migration schema');
290
+ }
291
+ }
292
+ const stored = requireRecord(value.stored, 'stored migration bindings');
293
+ if (typeof value.action !== 'string' || typeof value.phase !== 'string' ||
294
+ Object.values(stored).some((item) => typeof item !== 'string') ||
295
+ ['journal_version', 'target_manifest_json', 'affected_scopes_json', 'scope_cursor_updates_json',
296
+ 'migration_plan_version', 'migration_plan_json', 'migration_plan_hash'].some((key) => typeof stored[key] !== 'string')) {
297
+ throw new InvalidResponseError('Native bridge returned invalid migration journal');
298
+ }
299
+ }
300
+ const physicalSchema = snapshot.physical_schema.map((item) => {
301
+ const column = requireRecord(item, 'physical schema column');
302
+ if (typeof column.table_name !== 'string' || typeof column.name !== 'string' || typeof column.type !== 'string' ||
303
+ typeof column.not_null !== 'boolean' || !isNonnegativeSafeInteger(column.primary_key_position)) {
304
+ throw new InvalidResponseError('Native bridge returned invalid physical schema column');
305
+ }
306
+ return column as unknown as PhysicalSchemaColumnInspection;
307
+ });
308
+ if (snapshot.migration_journal_truncated && journal !== null) {
309
+ throw new InvalidResponseError('Native bridge returned truncated migration details');
310
+ }
311
+ const accepted = requireRecord(snapshot.accepted_mutation_outcomes, 'accepted mutation outcomes');
312
+ const entries = Object.entries(accepted);
313
+ if (typeof snapshot.accepted_mutation_outcomes_truncated !== 'boolean' || entries.length > 512 ||
314
+ (snapshot.accepted_mutation_outcomes_truncated && entries.length !== 0)) {
315
+ throw new InvalidResponseError('Native bridge returned invalid accepted outcome capture');
316
+ }
317
+ let acceptedBytes = 0;
318
+ for (const [id, outcome] of entries) {
319
+ if (typeof outcome !== 'string') throw new InvalidResponseError('Native bridge returned invalid accepted outcome');
320
+ for (const text of [id, outcome]) {
321
+ for (const character of text) {
322
+ const first = character.charCodeAt(0);
323
+ if (character.length === 1 && first >= 0xd800 && first <= 0xdfff) {
324
+ throw new InvalidResponseError('Native bridge returned invalid accepted outcome encoding');
325
+ }
326
+ acceptedBytes += character.length === 2 ? 4 : first <= 0x7f ? 1 : first <= 0x7ff ? 2 : 3;
327
+ if (acceptedBytes > 65_536) throw new InvalidResponseError('Native bridge returned oversized accepted outcomes');
328
+ }
329
+ }
254
330
  }
331
+ return {
332
+ migrationJournal: journal as MigrationJournalInspection | null,
333
+ migrationJournalTruncated: snapshot.migration_journal_truncated,
334
+ physicalSchema,
335
+ physicalSchemaTruncated: snapshot.physical_schema_truncated,
336
+ acceptedMutationOutcomes: accepted as Record<string, string>,
337
+ acceptedMutationOutcomesTruncated: snapshot.accepted_mutation_outcomes_truncated,
338
+ };
255
339
  }
256
340
 
257
341
  export { sha256Hex } from './digest';
@@ -265,4 +349,7 @@ export type {
265
349
  TransportObservation,
266
350
  TransportObservationSnapshot,
267
351
  TransportOperationClass,
352
+ MigrationCheckpoint,
353
+ MigrationJournalInspection,
354
+ PhysicalSchemaColumnInspection,
268
355
  } from './types';
package/src/types.ts CHANGED
@@ -329,6 +329,24 @@ export interface ClientStateInspection {
329
329
  provenanceMaintenanceWorkCursor: string;
330
330
  }
331
331
 
332
+ export type MigrationCheckpoint = 'migration_prepared' | 'migration_committed';
333
+
334
+ export interface MigrationJournalInspection {
335
+ source: SchemaRef;
336
+ target: SchemaRef;
337
+ action: string;
338
+ phase: string;
339
+ stored: Record<string, string>;
340
+ }
341
+
342
+ export interface PhysicalSchemaColumnInspection {
343
+ table_name: string;
344
+ name: string;
345
+ type: string;
346
+ not_null: boolean;
347
+ primary_key_position: number;
348
+ }
349
+
332
350
  export type TransportOperationClass =
333
351
  | 'connect'
334
352
  | 'pull'
@@ -342,12 +360,15 @@ export interface TransportObservation {
342
360
  sequence: number;
343
361
  operationClass: TransportOperationClass;
344
362
  statusCode: number;
363
+ errorCode?: string;
364
+ retryable?: boolean;
345
365
  durationNanoseconds: number;
346
366
  cursorFingerprints?: string[];
347
367
  cursorFingerprintsComplete?: boolean;
348
368
  requestFacts?: Record<string, JSONValue>;
349
369
  rebuildResponseFacts?: Record<string, JSONValue>;
350
370
  pullResponseFacts?: Record<string, JSONValue>;
371
+ connectResponseFacts?: Record<string, JSONValue>;
351
372
  }
352
373
 
353
374
  export interface TransportObservationSnapshot {
@@ -0,0 +1,163 @@
1
+ 'use strict';
2
+
3
+ const crypto = require('crypto');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const {spawnSync} = require('child_process');
7
+
8
+ const appRoot = fs.realpathSync(process.cwd());
9
+
10
+ const repairs = {
11
+ cli: [{
12
+ caller: '@react-native-community/cli-platform-android',
13
+ callerVersion: '20.2.0',
14
+ packageName: '@react-native-community/cli-config-android',
15
+ version: '20.2.0',
16
+ patch: 'cli-config-android-20.2.0.patch',
17
+ sources: [{
18
+ path: 'build/config/findComponentDescriptors.js',
19
+ preimage: '2cfe70a25818c4cd61fc8c122602f5363b8d57ffc1cf8c153f26fe68f1b8f047',
20
+ postimage: '75ace921b876fb1164e2b80dda9045bfebf2972b9465da992bba5758452add94'
21
+ }]
22
+ }, {
23
+ caller: '@react-native-community/cli',
24
+ callerVersion: '20.2.0',
25
+ packageName: '@react-native-community/cli-clean',
26
+ version: '20.2.0',
27
+ patch: 'cli-clean-20.2.0.patch',
28
+ sources: [{
29
+ path: 'build/clean.js',
30
+ preimage: 'f46f188859df1d616676d5cb246ed0b96b8f5e71868c5df914d31830acdde4be',
31
+ postimage: '9aea3a5112e0432ec78f5716d91ceac56d35e2b24eb5c45f4f28d254b9eaf9c2'
32
+ }]
33
+ }],
34
+ 'stream-json': [{
35
+ caller: 'bunyamin',
36
+ callerVersion: '1.6.3',
37
+ packageName: 'stream-json',
38
+ version: '1.9.1',
39
+ patch: 'stream-json-1.9.1.patch',
40
+ sources: [{
41
+ path: 'Assembler.js',
42
+ preimage: 'a341b21d86a9c005e5433eadb6c33d4708d611c8bc624ee383463c0fb6d2e32b',
43
+ postimage: '51bf2168f777c8c7285dd521c32f0de636f363efc9baa80dbfb140fa55372203'
44
+ }]
45
+ }],
46
+ bob: [{
47
+ caller: 'react-native-builder-bob',
48
+ callerVersion: '0.40.18',
49
+ packageName: 'react-native-builder-bob',
50
+ version: '0.40.18',
51
+ patch: 'react-native-builder-bob-0.40.18.patch',
52
+ sources: [{
53
+ path: 'lib/targets/module.js',
54
+ preimage: '1ef8ee20f2767347348e86de7f421358b7705623ae04a9b952fabc61f365ede6',
55
+ postimage: '16686f186b97a7a83fa6fc761db191a623d0f5697acb45b8df907e343c7629fa'
56
+ }, {
57
+ path: 'lib/targets/typescript.js',
58
+ preimage: '451fe8050e20bc1c4724564b2c33fb819f815848f5ad2eb529be5915bf62f2bb',
59
+ postimage: 'd22c16e9d4ebe97b15a3b734b70e1060a5526b907cad2b1ec3bb1004d4ed0675'
60
+ }]
61
+ }]
62
+ };
63
+
64
+ function fail(message) {
65
+ throw new Error(`Synchro tooling patch: ${message}`);
66
+ }
67
+
68
+ function resolvedPackageRoot(request, paths) {
69
+ const packageJson = require.resolve(`${request}/package.json`, {paths});
70
+ return fs.realpathSync(path.dirname(packageJson));
71
+ }
72
+
73
+ function assertInsideInstall(target) {
74
+ const relative = path.relative(appRoot, target);
75
+ if (relative === '' || relative.startsWith('..') || path.isAbsolute(relative)) {
76
+ fail('resolved target is outside the caller installation');
77
+ }
78
+ if (!relative.split(path.sep).includes('node_modules')) {
79
+ fail('resolved target is not in node_modules');
80
+ }
81
+ }
82
+
83
+ function packageIdentity(packageRoot, expectedName, expectedVersion) {
84
+ const manifest = JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8'));
85
+ if (manifest.name !== expectedName) {
86
+ fail('unexpected package identity');
87
+ }
88
+ if (manifest.version !== expectedVersion) {
89
+ fail('unexpected package version');
90
+ }
91
+ }
92
+
93
+ function hashFile(file) {
94
+ return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
95
+ }
96
+
97
+ function callerRoot(repair) {
98
+ if (repair.caller === 'bunyamin') {
99
+ const entry = require.resolve('bunyamin', {paths: [appRoot]});
100
+ let directory = path.dirname(fs.realpathSync(entry));
101
+ while (directory !== path.dirname(directory)) {
102
+ const manifest = path.join(directory, 'package.json');
103
+ if (fs.existsSync(manifest) && JSON.parse(fs.readFileSync(manifest, 'utf8')).name === 'bunyamin') {
104
+ return directory;
105
+ }
106
+ directory = path.dirname(directory);
107
+ }
108
+ fail('could not locate the resolved bunyamin package root');
109
+ }
110
+ return resolvedPackageRoot(repair.caller, [appRoot]);
111
+ }
112
+
113
+ function targetRoot(repair, caller) {
114
+ if (repair.caller === 'bunyamin') {
115
+ const source = require.resolve('stream-json/Assembler.js', {paths: [caller]});
116
+ return fs.realpathSync(path.dirname(source));
117
+ }
118
+ return resolvedPackageRoot(repair.packageName, [caller]);
119
+ }
120
+
121
+ function applyRepair(repair) {
122
+ const caller = callerRoot(repair);
123
+ assertInsideInstall(caller);
124
+ packageIdentity(caller, repair.caller, repair.callerVersion);
125
+ const target = targetRoot(repair, caller);
126
+ assertInsideInstall(target);
127
+ packageIdentity(target, repair.packageName, repair.version);
128
+ const sources = repair.sources.map(source => {
129
+ const file = path.join(target, source.path);
130
+ if (!fs.existsSync(file) || fs.realpathSync(file) !== file) {
131
+ fail('unexpected or missing source target');
132
+ }
133
+ return {file, source};
134
+ });
135
+ if (sources.every(({file, source}) => hashFile(file) === source.postimage)) {
136
+ process.stdout.write(`${repair.packageName} ${repair.version} already has the verified patch\n`);
137
+ return;
138
+ }
139
+ if (!sources.every(({file, source}) => hashFile(file) === source.preimage)) {
140
+ fail('unexpected source hash');
141
+ }
142
+ const patchFile = path.join(__dirname, repair.patch);
143
+ const result = spawnSync('patch', ['--batch', '--forward', '--fuzz=0', '-p1', '-d', target], {
144
+ input: fs.readFileSync(patchFile),
145
+ encoding: 'utf8',
146
+ stdio: ['pipe', 'inherit', 'inherit']
147
+ });
148
+ if (result.error || result.status !== 0) {
149
+ fail('patch command failed');
150
+ }
151
+ if (!sources.every(({file, source}) => hashFile(file) === source.postimage)) {
152
+ fail('postimage verification failed');
153
+ }
154
+ process.stdout.write(`applied verified ${repair.packageName} ${repair.version} patch\n`);
155
+ }
156
+
157
+ const name = process.argv[2] || 'cli';
158
+ if (!Object.prototype.hasOwnProperty.call(repairs, name)) {
159
+ fail('unsupported repair');
160
+ }
161
+ for (const repair of repairs[name]) {
162
+ applyRepair(repair);
163
+ }
@@ -0,0 +1,21 @@
1
+ diff --git a/build/clean.js b/build/clean.js
2
+ index 0cad23b..942fb42 100644
3
+ --- a/build/clean.js
4
+ +++ b/build/clean.js
5
+ @@ -157,13 +157,13 @@ async function clean(_argv, ctx, cleanOptions) {
6
+ description: 'Metro, haste-map caches',
7
+ tasks: [{
8
+ label: 'Clean Metro cache',
9
+ - action: () => cleanDir(`${_os().default.tmpdir()}/metro-*`)
10
+ + action: () => cleanDir(`${_fastGlob().default.convertPathToPattern(_os().default.tmpdir())}/metro-*`)
11
+ }, {
12
+ label: 'Clean Haste cache',
13
+ - action: () => cleanDir(`${_os().default.tmpdir()}/haste-map-*`)
14
+ + action: () => cleanDir(`${_fastGlob().default.convertPathToPattern(_os().default.tmpdir())}/haste-map-*`)
15
+ }, {
16
+ label: 'Clean React Native cache',
17
+ - action: () => cleanDir(`${_os().default.tmpdir()}/react-*`)
18
+ + action: () => cleanDir(`${_fastGlob().default.convertPathToPattern(_os().default.tmpdir())}/react-*`)
19
+ }]
20
+ },
21
+ bun: {
@@ -0,0 +1,13 @@
1
+ diff --git a/build/config/findComponentDescriptors.js b/build/config/findComponentDescriptors.js
2
+ index 36fdb7c..4e857d0 100644
3
+ --- a/build/config/findComponentDescriptors.js
4
+ +++ b/build/config/findComponentDescriptors.js
5
+ @@ -42,7 +42,7 @@ function findComponentDescriptors(packageRoot) {
6
+ } catch (error) {
7
+ // no jsSrcsDir, continue with default glob pattern
8
+ }
9
+ - const globPattern = jsSrcsDir ? `${jsSrcsDir}/**/*{.js,.jsx,.ts,.tsx}` : '**/*{.js,.jsx,.ts,.tsx}';
10
+ + const globPattern = jsSrcsDir ? `${_fastGlob().default.convertPathToPattern(jsSrcsDir)}/**/*{.js,.jsx,.ts,.tsx}` : '**/*{.js,.jsx,.ts,.tsx}';
11
+ const files = _fastGlob().default.sync(globPattern, {
12
+ cwd: (0, _cliTools().unixifyPaths)(packageRoot),
13
+ onlyFiles: true,
@@ -0,0 +1,74 @@
1
+ diff --git a/lib/targets/module.js b/lib/targets/module.js
2
+ --- a/lib/targets/module.js
3
+ +++ b/lib/targets/module.js
4
+ @@ -6,7 +6,7 @@
5
+ exports.default = build;
6
+ var _path = _interopRequireDefault(require("path"));
7
+ var _kleur = _interopRequireDefault(require("kleur"));
8
+ -var _del = _interopRequireDefault(require("del"));
9
+ +var _fsExtra = _interopRequireDefault(require("fs-extra"));
10
+ var _compile = _interopRequireDefault(require("../utils/compile"));
11
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12
+ async function build({
13
+ @@ -19,7 +19,11 @@
14
+ report
15
+ }) {
16
+ report.info(`Cleaning up previous build at ${_kleur.default.blue(_path.default.relative(root, output))}`);
17
+ - await (0, _del.default)([output]);
18
+ + const outputRelative = _path.default.relative(process.cwd(), output);
19
+ + if (outputRelative === '' || outputRelative === '..' || outputRelative.startsWith(`..${_path.default.sep}`) || _path.default.isAbsolute(outputRelative)) {
20
+ + throw new Error('Refusing to remove build output outside the current working directory.');
21
+ + }
22
+ + await _fsExtra.default.remove(output);
23
+ await (0, _compile.default)({
24
+ ...options,
25
+ variants,
26
+ diff --git a/lib/targets/typescript.js b/lib/targets/typescript.js
27
+ --- a/lib/targets/typescript.js
28
+ +++ b/lib/targets/typescript.js
29
+ @@ -4,7 +4,6 @@
30
+ value: true
31
+ });
32
+ exports.default = build;
33
+ -var _del = _interopRequireDefault(require("del"));
34
+ var _fsExtra = _interopRequireDefault(require("fs-extra"));
35
+ var _json = _interopRequireDefault(require("json5"));
36
+ var _kleur = _interopRequireDefault(require("kleur"));
37
+ @@ -23,7 +22,11 @@
38
+ esm
39
+ }) {
40
+ report.info(`Cleaning up previous build at ${_kleur.default.blue(_path.default.relative(root, output))}`);
41
+ - await (0, _del.default)([output]);
42
+ + const outputRelative = _path.default.relative(process.cwd(), output);
43
+ + if (outputRelative === '' || outputRelative === '..' || outputRelative.startsWith(`..${_path.default.sep}`) || _path.default.isAbsolute(outputRelative)) {
44
+ + throw new Error('Refusing to remove build output outside the current working directory.');
45
+ + }
46
+ + await _fsExtra.default.remove(output);
47
+ report.info(`Generating type definitions with ${_kleur.default.blue('tsc')}`);
48
+ const project = options?.project ? options.project : 'tsconfig.json';
49
+ const tsconfig = _path.default.join(root, project);
50
+ @@ -101,8 +104,12 @@
51
+ throw new Error('Neither commonjs nor module output is enabled.');
52
+ }
53
+ const tsbuildinfo = _path.default.join(outDir, project.replace(/\.json$/, '.tsbuildinfo'));
54
+ + const buildInfoRelative = _path.default.relative(process.cwd(), tsbuildinfo);
55
+ + const tsbuildinfoIsOwned = buildInfoRelative !== '' && buildInfoRelative !== '..' && !buildInfoRelative.startsWith(`..${_path.default.sep}`) && !_path.default.isAbsolute(buildInfoRelative);
56
+ try {
57
+ - await (0, _del.default)([tsbuildinfo]);
58
+ + if (tsbuildinfoIsOwned) {
59
+ + await _fsExtra.default.remove(tsbuildinfo);
60
+ + }
61
+ } catch (e) {
62
+ // Ignore
63
+ }
64
+ @@ -110,7 +117,9 @@
65
+ cwd: root
66
+ });
67
+ try {
68
+ - await (0, _del.default)([tsbuildinfo]);
69
+ + if (tsbuildinfoIsOwned) {
70
+ + await _fsExtra.default.remove(tsbuildinfo);
71
+ + }
72
+ } catch (e) {
73
+ // Ignore
74
+ }
@@ -0,0 +1,40 @@
1
+ diff --git a/Assembler.js b/Assembler.js
2
+ index 2a8bda9..3a0aace 100644
3
+ --- a/Assembler.js
4
+ +++ b/Assembler.js
5
+ @@ -120,7 +120,16 @@ class Assembler extends EventEmitter {
6
+ if (this.current instanceof Array) {
7
+ this.current.push(value);
8
+ } else {
9
+ - this.current[this.key] = value;
10
+ + if (this.key === '__proto__') {
11
+ + Object.defineProperty(this.current, this.key, {
12
+ + value,
13
+ + writable: true,
14
+ + enumerable: true,
15
+ + configurable: true
16
+ + });
17
+ + } else {
18
+ + this.current[this.key] = value;
19
+ + }
20
+ this.key = null;
21
+ }
22
+ }
23
+ @@ -138,7 +147,16 @@ class Assembler extends EventEmitter {
24
+ } else {
25
+ value = this.reviver(this.key, value);
26
+ if (value !== undefined) {
27
+ - this.current[this.key] = value;
28
+ + if (this.key === '__proto__') {
29
+ + Object.defineProperty(this.current, this.key, {
30
+ + value,
31
+ + writable: true,
32
+ + enumerable: true,
33
+ + configurable: true
34
+ + });
35
+ + } else {
36
+ + this.current[this.key] = value;
37
+ + }
38
+ }
39
+ this.key = null;
40
+ }