@wix/bex-core 2.336.0 → 2.337.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 (58) hide show
  1. package/dist/cjs/hooks/useImport.js.map +1 -1
  2. package/dist/cjs/index.js +7 -0
  3. package/dist/cjs/index.js.map +1 -1
  4. package/dist/cjs/state/ImportState/ImportState.js +6 -5
  5. package/dist/cjs/state/ImportState/ImportState.js.map +1 -1
  6. package/dist/cjs/state/ImportState/__tests__/ImportState.engine.spec.js +28 -0
  7. package/dist/cjs/state/ImportState/__tests__/ImportState.engine.spec.js.map +1 -1
  8. package/dist/cjs/testkit/createInMemorySchemaSource.js +2 -0
  9. package/dist/cjs/testkit/createInMemorySchemaSource.js.map +1 -1
  10. package/dist/cjs/testkit/hubJobImportMocks.js +14 -4
  11. package/dist/cjs/testkit/hubJobImportMocks.js.map +1 -1
  12. package/dist/cjs/types/Field.js.map +1 -1
  13. package/dist/cjs/types/FieldTypeDefinition.js +79 -0
  14. package/dist/cjs/types/FieldTypeDefinition.js.map +1 -0
  15. package/dist/cjs/types/FieldsSource.js.map +1 -1
  16. package/dist/cjs/types/SchemaSource.js +14 -2
  17. package/dist/cjs/types/SchemaSource.js.map +1 -1
  18. package/dist/cjs/types/__tests__/defineSchemaSource.fieldTypes.spec.js +78 -0
  19. package/dist/cjs/types/__tests__/defineSchemaSource.fieldTypes.spec.js.map +1 -0
  20. package/dist/esm/hooks/useImport.js +1 -1
  21. package/dist/esm/hooks/useImport.js.map +1 -1
  22. package/dist/esm/index.js +1 -0
  23. package/dist/esm/index.js.map +1 -1
  24. package/dist/esm/state/ImportState/ImportState.js +6 -4
  25. package/dist/esm/state/ImportState/ImportState.js.map +1 -1
  26. package/dist/esm/state/ImportState/__tests__/ImportState.engine.spec.js +28 -1
  27. package/dist/esm/state/ImportState/__tests__/ImportState.engine.spec.js.map +1 -1
  28. package/dist/esm/testkit/createInMemorySchemaSource.js +2 -1
  29. package/dist/esm/testkit/createInMemorySchemaSource.js.map +1 -1
  30. package/dist/esm/testkit/hubJobImportMocks.js +14 -4
  31. package/dist/esm/testkit/hubJobImportMocks.js.map +1 -1
  32. package/dist/esm/types/FieldTypeDefinition.js +16 -0
  33. package/dist/esm/types/FieldTypeDefinition.js.map +1 -0
  34. package/dist/esm/types/SchemaSource.js +15 -3
  35. package/dist/esm/types/SchemaSource.js.map +1 -1
  36. package/dist/esm/types/__tests__/defineSchemaSource.fieldTypes.spec.js +51 -0
  37. package/dist/esm/types/__tests__/defineSchemaSource.fieldTypes.spec.js.map +1 -0
  38. package/dist/types/hooks/useImport.d.ts.map +1 -1
  39. package/dist/types/index.d.ts +2 -1
  40. package/dist/types/index.d.ts.map +1 -1
  41. package/dist/types/state/ImportState/ImportState.d.ts.map +1 -1
  42. package/dist/types/testkit/WixPatternsContainer.driver.d.ts +2 -0
  43. package/dist/types/testkit/WixPatternsContainer.driver.d.ts.map +1 -1
  44. package/dist/types/testkit/createInMemorySchemaSource.d.ts +3 -0
  45. package/dist/types/testkit/createInMemorySchemaSource.d.ts.map +1 -1
  46. package/dist/types/testkit/hubJobImportMocks.d.ts +8 -0
  47. package/dist/types/testkit/hubJobImportMocks.d.ts.map +1 -1
  48. package/dist/types/types/Field.d.ts +3 -1
  49. package/dist/types/types/Field.d.ts.map +1 -1
  50. package/dist/types/types/FieldTypeDefinition.d.ts +253 -0
  51. package/dist/types/types/FieldTypeDefinition.d.ts.map +1 -0
  52. package/dist/types/types/FieldsSource.d.ts +29 -1
  53. package/dist/types/types/FieldsSource.d.ts.map +1 -1
  54. package/dist/types/types/SchemaSource.d.ts +21 -2
  55. package/dist/types/types/SchemaSource.d.ts.map +1 -1
  56. package/dist/types/types/__tests__/defineSchemaSource.fieldTypes.spec.d.ts +2 -0
  57. package/dist/types/types/__tests__/defineSchemaSource.fieldTypes.spec.d.ts.map +1 -0
  58. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"names":["_http","require","_types","UPLOAD_BASE","UPLOAD_PATH","FILE_ID","JOB_ID","hubJobImportMocks","whenRequest","when","state","outcome","succeeded","failed","created","updated","inProgressPolls","failedRowIndices","uploadedBody","undefined","submittedRequest","submittedJobId","terminatedJobIds","pollCounts","Map","terminalStatus","JobStatus","FAILED","PARTIALLY_SUCCESSFUL","COMPLETED","total","terminalJob","id","status","progress","current","itemsSuccessful","itemsRejected","itemsSuccessfulInfo","itemsCreated","itemsUpdated","error","code","description","setOutcome","counts","setInProgressPolls","n","setFailedRowIndices","indices","getUploadedCsv","body","blob","text","arrayBuffer","TextDecoder","decode","Promise","resolve","reject","reader","FileReader","onload","String","result","onerror","readAsText","reset","clear","mocks","createFileUploadUrl","reply","uploadUrl","fileId","persist","put","data","submitJob","payload","set","job","INITIALIZING","getJob","seen","get","jobId","IN_PROGRESS","terminateJob","push","listMovementLogs","logs","map","index","sourceItemId","failure","pagingMetadata","hasNext"],"sources":["../../../src/testkit/hubJobImportMocks.ts"],"sourcesContent":["import {\n createFileUploadUrl,\n submitJob,\n getJob,\n terminateJob,\n listMovementLogs,\n} from '@wix/bex-utils/@wix/ambassador-hub-v1-job/http';\nimport {\n JobStatus,\n type DataMovementJob,\n type SubmitJobRequest,\n} from '@wix/bex-utils/@wix/ambassador-hub-v1-job/types';\n\nconst UPLOAD_BASE = 'https://import-upload.test';\nconst UPLOAD_PATH = '/file';\nconst FILE_ID = 'file-1';\nconst JOB_ID = 'job-1';\n\nexport type ImportJobOutcome = 'completed' | 'partial' | 'failed';\n\n/**\n * Test mocks for the data-hub Hub Job import lifecycle, mirroring\n * `exportServiceMocks`. Auto-included in the test container's httpClient\n * scenarios so every import test drives a fake server. Behavior is controllable\n * per test via `setOutcome` / `setInProgressPolls`, and the uploaded (aligned)\n * CSV + the submitted job request are captured for assertions.\n */\nexport function hubJobImportMocks(\n whenRequest: typeof import('@wix/http-client-testkit/client').whenRequest,\n when: typeof import('@wix/http-client-testkit/client').when,\n) {\n const state = {\n outcome: 'completed' as ImportJobOutcome,\n succeeded: 0,\n failed: 0,\n /** new items created (subset of `succeeded`), for the summary step */\n created: 0,\n /** existing items updated (subset of `succeeded`), for the summary step */\n updated: 0,\n /** number of IN_PROGRESS polls before the job goes terminal */\n inProgressPolls: 0,\n /** csvRows indices reported as failed via movement logs */\n failedRowIndices: [] as number[],\n /** the raw body PUT to the signed upload URL (the aligned CSV File) */\n uploadedBody: undefined as unknown,\n /** the SubmitJobRequest payload */\n submittedRequest: undefined as SubmitJobRequest | undefined,\n submittedJobId: undefined as string | undefined,\n terminatedJobIds: [] as string[],\n };\n\n const pollCounts = new Map<string, number>();\n\n const terminalStatus = (): JobStatus =>\n state.outcome === 'failed'\n ? JobStatus.FAILED\n : state.outcome === 'partial'\n ? JobStatus.PARTIALLY_SUCCESSFUL\n : JobStatus.COMPLETED;\n\n const total = () => state.succeeded + state.failed;\n\n const terminalJob = (id: string): DataMovementJob => ({\n id,\n status: terminalStatus(),\n progress: {\n current: total(),\n total: total(),\n itemsSuccessful: state.succeeded,\n itemsRejected: state.failed,\n itemsSuccessfulInfo: {\n itemsCreated: state.created,\n itemsUpdated: state.updated,\n },\n },\n ...(state.outcome === 'failed' && {\n error: { code: 'import_failed', description: 'Import job failed' },\n }),\n });\n\n return {\n state,\n\n /**\n * Set the job's terminal outcome and its counts. `created`/`updated` are the\n * split of `succeeded` reported to the summary step; when omitted they\n * default so `created` covers all successes (older jobs report neither).\n */\n setOutcome(\n outcome: ImportJobOutcome,\n counts: {\n succeeded?: number;\n failed?: number;\n created?: number;\n updated?: number;\n } = {},\n ) {\n state.outcome = outcome;\n state.succeeded = counts.succeeded ?? 0;\n state.failed = counts.failed ?? 0;\n state.updated = counts.updated ?? 0;\n state.created = counts.created ?? state.succeeded - state.updated;\n },\n\n /** Make the job report IN_PROGRESS for `n` polls before going terminal. */\n setInProgressPolls(n: number) {\n state.inProgressPolls = n;\n },\n\n /** Row indices `listMovementLogs` reports as failed (sourceItemId). */\n setFailedRowIndices(indices: number[]) {\n state.failedRowIndices = indices;\n },\n\n /** The aligned CSV uploaded to the signed URL, as text. */\n async getUploadedCsv(): Promise<string> {\n const body = state.uploadedBody;\n if (body == null) {\n return '';\n }\n if (typeof body === 'string') {\n return body;\n }\n const blob = body as Blob;\n // jsdom's File implements neither text() nor arrayBuffer() here, so fall\n // back to FileReader.\n if (typeof blob.text === 'function') {\n return blob.text();\n }\n if (typeof blob.arrayBuffer === 'function') {\n return new TextDecoder().decode(await blob.arrayBuffer());\n }\n return new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(String(reader.result));\n reader.onerror = () => reject(reader.error);\n reader.readAsText(blob);\n });\n },\n\n reset() {\n state.outcome = 'completed';\n state.succeeded = 0;\n state.failed = 0;\n state.created = 0;\n state.updated = 0;\n state.inProgressPolls = 0;\n state.failedRowIndices = [];\n state.uploadedBody = undefined;\n state.submittedRequest = undefined;\n state.submittedJobId = undefined;\n state.terminatedJobIds = [];\n pollCounts.clear();\n },\n\n mocks: [\n whenRequest(createFileUploadUrl)\n .reply(200, {\n uploadUrl: `${UPLOAD_BASE}${UPLOAD_PATH}`,\n fileId: FILE_ID,\n })\n .persist(),\n\n when(UPLOAD_BASE)\n .put(UPLOAD_PATH)\n .reply(200, (data: unknown) => {\n state.uploadedBody = data;\n return {};\n })\n .persist(),\n\n whenRequest(submitJob)\n .reply(200, (payload: SubmitJobRequest) => {\n state.submittedRequest = payload;\n state.submittedJobId = JOB_ID;\n pollCounts.set(JOB_ID, 0);\n return {\n job: {\n id: JOB_ID,\n status: JobStatus.INITIALIZING,\n progress: { current: 0, total: total() },\n },\n };\n })\n .persist(),\n\n whenRequest(getJob)\n .reply(200, (payload: { jobId: string }) => {\n const seen = pollCounts.get(payload.jobId) ?? 0;\n pollCounts.set(payload.jobId, seen + 1);\n\n if (seen < state.inProgressPolls) {\n // No `total` here so ImportState keeps its initial CSV-row total;\n // `current` is 0-based so the first poll reports nothing processed.\n return {\n job: {\n id: payload.jobId,\n status: JobStatus.IN_PROGRESS,\n progress: {\n current: seen,\n itemsSuccessful: seen,\n itemsRejected: 0,\n },\n },\n };\n }\n\n return { job: terminalJob(payload.jobId) };\n })\n .persist(),\n\n whenRequest(terminateJob)\n .reply(200, (payload: { jobId: string }) => {\n state.terminatedJobIds.push(payload.jobId);\n return {};\n })\n .persist(),\n\n whenRequest(listMovementLogs)\n .reply(200, () => ({\n // data-hub formats a CSV item's sourceItemId as \"row N\", where N is\n // the 1-based file line (header is line 1) — so csvRows[i] is \"row i+2\".\n logs: state.failedRowIndices.map((index) => ({\n sourceItemId: `row ${index + 2}`,\n failure: { code: 'row_failed', description: 'Invalid row' },\n })),\n pagingMetadata: { hasNext: false },\n }))\n .persist(),\n ] as import('@wix/http-client-testkit').AnyScenario[],\n };\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_http","require","_types","UPLOAD_BASE","UPLOAD_PATH","FILE_ID","JOB_ID","hubJobImportMocks","whenRequest","when","state","outcome","succeeded","failed","created","updated","noBreakdown","inProgressPolls","failedRowIndices","uploadedBody","undefined","submittedRequest","submittedJobId","terminatedJobIds","pollCounts","Map","terminalStatus","JobStatus","FAILED","PARTIALLY_SUCCESSFUL","COMPLETED","total","terminalJob","id","status","progress","current","itemsSuccessful","itemsRejected","itemsSuccessfulInfo","itemsCreated","itemsUpdated","error","code","description","setOutcome","counts","setInProgressPolls","n","setFailedRowIndices","indices","getUploadedCsv","body","blob","text","arrayBuffer","TextDecoder","decode","Promise","resolve","reject","reader","FileReader","onload","String","result","onerror","readAsText","reset","clear","mocks","createFileUploadUrl","reply","uploadUrl","fileId","persist","put","data","submitJob","payload","set","job","INITIALIZING","getJob","seen","get","jobId","IN_PROGRESS","terminateJob","push","listMovementLogs","logs","map","index","sourceItemId","failure","pagingMetadata","hasNext"],"sources":["../../../src/testkit/hubJobImportMocks.ts"],"sourcesContent":["import {\n createFileUploadUrl,\n submitJob,\n getJob,\n terminateJob,\n listMovementLogs,\n} from '@wix/bex-utils/@wix/ambassador-hub-v1-job/http';\nimport {\n JobStatus,\n type DataMovementJob,\n type SubmitJobRequest,\n} from '@wix/bex-utils/@wix/ambassador-hub-v1-job/types';\n\nconst UPLOAD_BASE = 'https://import-upload.test';\nconst UPLOAD_PATH = '/file';\nconst FILE_ID = 'file-1';\nconst JOB_ID = 'job-1';\n\nexport type ImportJobOutcome = 'completed' | 'partial' | 'failed';\n\n/**\n * Test mocks for the data-hub Hub Job import lifecycle, mirroring\n * `exportServiceMocks`. Auto-included in the test container's httpClient\n * scenarios so every import test drives a fake server. Behavior is controllable\n * per test via `setOutcome` / `setInProgressPolls`, and the uploaded (aligned)\n * CSV + the submitted job request are captured for assertions.\n */\nexport function hubJobImportMocks(\n whenRequest: typeof import('@wix/http-client-testkit/client').whenRequest,\n when: typeof import('@wix/http-client-testkit/client').when,\n) {\n const state = {\n outcome: 'completed' as ImportJobOutcome,\n succeeded: 0,\n failed: 0,\n /** new items created (subset of `succeeded`), for the summary step */\n created: 0,\n /** existing items updated (subset of `succeeded`), for the summary step */\n updated: 0,\n /**\n * When true, the terminal job omits `itemsSuccessfulInfo` entirely — the\n * created/updated breakdown some jobs (and older servers) don't report, in\n * which case only the total `itemsSuccessful` is available.\n */\n noBreakdown: false,\n /** number of IN_PROGRESS polls before the job goes terminal */\n inProgressPolls: 0,\n /** csvRows indices reported as failed via movement logs */\n failedRowIndices: [] as number[],\n /** the raw body PUT to the signed upload URL (the aligned CSV File) */\n uploadedBody: undefined as unknown,\n /** the SubmitJobRequest payload */\n submittedRequest: undefined as SubmitJobRequest | undefined,\n submittedJobId: undefined as string | undefined,\n terminatedJobIds: [] as string[],\n };\n\n const pollCounts = new Map<string, number>();\n\n const terminalStatus = (): JobStatus =>\n state.outcome === 'failed'\n ? JobStatus.FAILED\n : state.outcome === 'partial'\n ? JobStatus.PARTIALLY_SUCCESSFUL\n : JobStatus.COMPLETED;\n\n const total = () => state.succeeded + state.failed;\n\n const terminalJob = (id: string): DataMovementJob => ({\n id,\n status: terminalStatus(),\n progress: {\n current: total(),\n total: total(),\n itemsSuccessful: state.succeeded,\n itemsRejected: state.failed,\n ...(!state.noBreakdown && {\n itemsSuccessfulInfo: {\n itemsCreated: state.created,\n itemsUpdated: state.updated,\n },\n }),\n },\n ...(state.outcome === 'failed' && {\n error: { code: 'import_failed', description: 'Import job failed' },\n }),\n });\n\n return {\n state,\n\n /**\n * Set the job's terminal outcome and its counts. `created`/`updated` are the\n * split of `succeeded` reported to the summary step; when omitted they\n * default so `created` covers all successes (older jobs report neither).\n */\n setOutcome(\n outcome: ImportJobOutcome,\n counts: {\n succeeded?: number;\n failed?: number;\n created?: number;\n updated?: number;\n /** Omit the created/updated breakdown from the job (see `noBreakdown`). */\n noBreakdown?: boolean;\n } = {},\n ) {\n state.outcome = outcome;\n state.succeeded = counts.succeeded ?? 0;\n state.failed = counts.failed ?? 0;\n state.updated = counts.updated ?? 0;\n state.created = counts.created ?? state.succeeded - state.updated;\n state.noBreakdown = counts.noBreakdown ?? false;\n },\n\n /** Make the job report IN_PROGRESS for `n` polls before going terminal. */\n setInProgressPolls(n: number) {\n state.inProgressPolls = n;\n },\n\n /** Row indices `listMovementLogs` reports as failed (sourceItemId). */\n setFailedRowIndices(indices: number[]) {\n state.failedRowIndices = indices;\n },\n\n /** The aligned CSV uploaded to the signed URL, as text. */\n async getUploadedCsv(): Promise<string> {\n const body = state.uploadedBody;\n if (body == null) {\n return '';\n }\n if (typeof body === 'string') {\n return body;\n }\n const blob = body as Blob;\n // jsdom's File implements neither text() nor arrayBuffer() here, so fall\n // back to FileReader.\n if (typeof blob.text === 'function') {\n return blob.text();\n }\n if (typeof blob.arrayBuffer === 'function') {\n return new TextDecoder().decode(await blob.arrayBuffer());\n }\n return new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(String(reader.result));\n reader.onerror = () => reject(reader.error);\n reader.readAsText(blob);\n });\n },\n\n reset() {\n state.outcome = 'completed';\n state.succeeded = 0;\n state.failed = 0;\n state.created = 0;\n state.updated = 0;\n state.noBreakdown = false;\n state.inProgressPolls = 0;\n state.failedRowIndices = [];\n state.uploadedBody = undefined;\n state.submittedRequest = undefined;\n state.submittedJobId = undefined;\n state.terminatedJobIds = [];\n pollCounts.clear();\n },\n\n mocks: [\n whenRequest(createFileUploadUrl)\n .reply(200, {\n uploadUrl: `${UPLOAD_BASE}${UPLOAD_PATH}`,\n fileId: FILE_ID,\n })\n .persist(),\n\n when(UPLOAD_BASE)\n .put(UPLOAD_PATH)\n .reply(200, (data: unknown) => {\n state.uploadedBody = data;\n return {};\n })\n .persist(),\n\n whenRequest(submitJob)\n .reply(200, (payload: SubmitJobRequest) => {\n state.submittedRequest = payload;\n state.submittedJobId = JOB_ID;\n pollCounts.set(JOB_ID, 0);\n return {\n job: {\n id: JOB_ID,\n status: JobStatus.INITIALIZING,\n progress: { current: 0, total: total() },\n },\n };\n })\n .persist(),\n\n whenRequest(getJob)\n .reply(200, (payload: { jobId: string }) => {\n const seen = pollCounts.get(payload.jobId) ?? 0;\n pollCounts.set(payload.jobId, seen + 1);\n\n if (seen < state.inProgressPolls) {\n // No `total` here so ImportState keeps its initial CSV-row total;\n // `current` is 0-based so the first poll reports nothing processed.\n return {\n job: {\n id: payload.jobId,\n status: JobStatus.IN_PROGRESS,\n progress: {\n current: seen,\n itemsSuccessful: seen,\n itemsRejected: 0,\n },\n },\n };\n }\n\n return { job: terminalJob(payload.jobId) };\n })\n .persist(),\n\n whenRequest(terminateJob)\n .reply(200, (payload: { jobId: string }) => {\n state.terminatedJobIds.push(payload.jobId);\n return {};\n })\n .persist(),\n\n whenRequest(listMovementLogs)\n .reply(200, () => ({\n // data-hub formats a CSV item's sourceItemId as \"row N\", where N is\n // the 1-based file line (header is line 1) — so csvRows[i] is \"row i+2\".\n logs: state.failedRowIndices.map((index) => ({\n sourceItemId: `row ${index + 2}`,\n failure: { code: 'row_failed', description: 'Invalid row' },\n })),\n pagingMetadata: { hasNext: false },\n }))\n .persist(),\n ] as import('@wix/http-client-testkit').AnyScenario[],\n };\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["../../../src/types/Field.ts"],"sourcesContent":["export type PatternsFieldType =\n | 'SHORT_TEXT'\n | 'LONG_TEXT'\n | 'NUMBER'\n | 'BOOLEAN'\n | 'DATE'\n | 'DATETIME'\n | 'TIME'\n | 'URL'\n | 'ARRAY'\n | 'REFERENCE'\n | 'MULTI_REFERENCE'\n | 'OBJECT'\n | 'IMAGE'\n | 'VIDEO'\n | 'MEDIA_GALLERY'\n | 'ADDRESS'\n | 'RICH_TEXT'\n | 'DOCUMENT'\n | 'RICH_CONTENT'\n | 'MULTI_DOCUMENT'\n | 'EMAIL'\n | 'AUDIO'\n | 'COLOR'\n | 'UNKNOWN';\n\nexport type QueryOperator =\n | 'CONTAINS'\n | 'ENDS_WITH'\n | 'EQ'\n | 'EXISTS'\n | 'GT'\n | 'GTE'\n | 'HAS_ALL'\n | 'HAS_SOME'\n | 'LT'\n | 'LTE'\n | 'NE'\n | 'STARTS_WITH'\n | 'URLIZED';\n\nexport interface NumberRange {\n min?: number;\n max?: number;\n}\n\nexport interface StringLengthRange {\n minLength?: number;\n maxLength?: number;\n}\n\ninterface BaseField {\n id: string;\n displayName: string;\n isPii?: boolean;\n helpText?: string;\n /** Whether this is a system-managed field (not user-created). */\n isSystem?: boolean;\n /** The group this field belongs to, when the source groups its fields.\n * Absent ⇒ ungrouped. */\n group?: string;\n /** Whether this is the collection's primary display field. */\n isPrimary?: boolean;\n validation?: {\n numberRange?: NumberRange;\n stringLengthRange?: StringLengthRange;\n required: boolean;\n };\n capabilities?: {\n supportedQueryOperators: QueryOperator[];\n sortable: boolean;\n };\n}\n\nexport interface BaseReferenceField extends BaseField {\n referenceMetadata: {\n referencedCollectionId: string;\n };\n}\n\nexport interface ReferenceField extends BaseReferenceField {\n type: 'REFERENCE';\n}\n\nexport interface MultiReferenceField extends BaseReferenceField {\n type: 'MULTI_REFERENCE';\n}\n\nexport interface NonReferenceField extends BaseField {\n type: Exclude<PatternsFieldType, 'REFERENCE' | 'MULTI_REFERENCE'>;\n}\n\nexport type Field = ReferenceField | MultiReferenceField | NonReferenceField;\nexport type FieldSpec = Omit<Field, 'id'>;\n"],"mappings":"","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["../../../src/types/Field.ts"],"sourcesContent":["export type PatternsFieldType =\n | 'SHORT_TEXT'\n | 'LONG_TEXT'\n | 'NUMBER'\n | 'BOOLEAN'\n | 'DATE'\n | 'DATETIME'\n | 'TIME'\n | 'URL'\n | 'ARRAY'\n | 'REFERENCE'\n | 'MULTI_REFERENCE'\n | 'OBJECT'\n | 'IMAGE'\n | 'VIDEO'\n | 'MEDIA_GALLERY'\n | 'ADDRESS'\n | 'RICH_TEXT'\n | 'DOCUMENT'\n | 'RICH_CONTENT'\n | 'MULTI_DOCUMENT'\n | 'EMAIL'\n | 'AUDIO'\n | 'COLOR'\n | 'CUSTOM'\n | 'UNKNOWN';\n\nexport type QueryOperator =\n | 'CONTAINS'\n | 'ENDS_WITH'\n | 'EQ'\n | 'EXISTS'\n | 'GT'\n | 'GTE'\n | 'HAS_ALL'\n | 'HAS_SOME'\n | 'LT'\n | 'LTE'\n | 'NE'\n | 'STARTS_WITH'\n | 'URLIZED';\n\nexport interface NumberRange {\n min?: number;\n max?: number;\n}\n\nexport interface StringLengthRange {\n minLength?: number;\n maxLength?: number;\n}\n\ninterface BaseField {\n id: string;\n displayName: string;\n isPii?: boolean;\n helpText?: string;\n /** Whether this is a system-managed field (not user-created). */\n isSystem?: boolean;\n /** The group this field belongs to, when the source groups its fields.\n * Absent ⇒ ungrouped. */\n group?: string;\n /** Whether this is the collection's primary display field. */\n isPrimary?: boolean;\n validation?: {\n numberRange?: NumberRange;\n stringLengthRange?: StringLengthRange;\n required: boolean;\n };\n capabilities?: {\n supportedQueryOperators: QueryOperator[];\n sortable: boolean;\n };\n}\n\nexport interface BaseReferenceField extends BaseField {\n referenceMetadata: {\n referencedCollectionId: string;\n };\n}\n\nexport interface ReferenceField extends BaseReferenceField {\n type: 'REFERENCE';\n}\n\nexport interface MultiReferenceField extends BaseReferenceField {\n type: 'MULTI_REFERENCE';\n}\n\nexport interface NonReferenceField extends BaseField {\n type: Exclude<PatternsFieldType, 'REFERENCE' | 'MULTI_REFERENCE'>;\n /** Present iff `type` is 'CUSTOM'; keys a field type definition registered on the source. */\n customType?: string;\n}\n\nexport type Field = ReferenceField | MultiReferenceField | NonReferenceField;\nexport type FieldSpec = Omit<Field, 'id'>;\n"],"mappings":"","ignoreList":[]}
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.defineFieldType = defineFieldType;
5
+ exports.isFilterTypeConfig = isFilterTypeConfig;
6
+ exports.resolveFieldTypeDefinition = resolveFieldTypeDefinition;
7
+ /**
8
+ * Props passed to a cell type's optional `EditWrapper`. The table mounts the
9
+ * wrapper around the cell content and keeps it mounted across the edit↔view
10
+ * transition, so the wrapper can own behavior that must outlive the inline
11
+ * editor (e.g. a reference cell navigating to an overlay and refreshing on
12
+ * return). The wrapper hands anything the editor needs down via its own context.
13
+ */
14
+
15
+ /**
16
+ * The editable-cell slot of a field type definition: how a cell of that type
17
+ * renders, edits, and moves through the clipboard. The definition's key
18
+ * identifies the type and the definition's `value` owns validation, so the
19
+ * slot carries neither.
20
+ *
21
+ * Members that take `V` use method syntax so a definition typed with a
22
+ * concrete value type stays assignable where a loosely typed definition is
23
+ * expected (see `FieldTypeDefinition`).
24
+ */
25
+
26
+ /** The props the filter builder passes to a definition's toolbar filter
27
+ * component — the same contract the built-in filter elements receive. */
28
+
29
+ /** The filter slot of a field type definition: the filter state registered on
30
+ * the collection query and the toolbar element that renders it. */
31
+
32
+ /** Narrows a definition's filter slot to an active filter config. */
33
+ function isFilterTypeConfig(filter) {
34
+ return 'createFilter' in filter;
35
+ }
36
+
37
+ /**
38
+ * The read-only table slot of a field type definition: how a cell of that
39
+ * type renders in the table, and the column's layout. Independent of
40
+ * `editableTable` — neither slot falls back to the other.
41
+ */
42
+
43
+ /** An extra menu item a field type definition adds to a field's actions menu. */
44
+
45
+ /** The actions slot of a field type definition: extra items for the field's "more actions" menu. */
46
+
47
+ /** Display metadata for a field type: menu label and type icons. */
48
+
49
+ /** Value semantics of a field type, independent of any rendering surface.
50
+ * Members use method syntax so a definition typed with a concrete value type
51
+ * stays assignable where a loosely typed definition is expected. */
52
+
53
+ /**
54
+ * A consumer-defined field type. Defined once on the fields source; every
55
+ * entry point that renders the field resolves its slot from here. `V` is the
56
+ * field's value type — `defineFieldType` infers it, and it keeps all the
57
+ * slots consistent with each other.
58
+ *
59
+ * Sources hold definitions of mixed value types in one
60
+ * `FieldTypeDefinition[]` (= `FieldTypeDefinition<unknown>[]`) array. That
61
+ * works because every member that takes `V` is declared with method syntax,
62
+ * which TypeScript checks bivariantly — so `FieldTypeDefinition<number>` is
63
+ * assignable to `FieldTypeDefinition<unknown>` without casts.
64
+ */
65
+
66
+ /** Identity factory: infers `V` and keeps the definition's slots type-checked. */
67
+ function defineFieldType(definition) {
68
+ return definition;
69
+ }
70
+
71
+ /** Resolves the definition a CUSTOM field references on its source. */
72
+ function resolveFieldTypeDefinition(source, field) {
73
+ var _source$fieldTypes;
74
+ if (field.type !== 'CUSTOM' || !field.customType) {
75
+ return undefined;
76
+ }
77
+ return (_source$fieldTypes = source.fieldTypes) == null ? void 0 : _source$fieldTypes.find(def => def.type === field.customType);
78
+ }
79
+ //# sourceMappingURL=FieldTypeDefinition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["isFilterTypeConfig","filter","defineFieldType","definition","resolveFieldTypeDefinition","source","field","_source$fieldTypes","type","customType","undefined","fieldTypes","find","def"],"sources":["../../../src/types/FieldTypeDefinition.ts"],"sourcesContent":["import type {\n ComponentType,\n MutableRefObject,\n ReactElement,\n ReactNode,\n} from 'react';\nimport type { Field } from './Field';\nimport type { Filter } from '../model/filter';\nimport type { FieldsSource } from './FieldsSource';\n\nexport type ValidationRule =\n | { type: 'required'; message: string }\n | { type: 'min' | 'max'; value: number; message: string }\n | { type: 'minLength' | 'maxLength'; value: number; message: string }\n | { type: 'pattern'; value: RegExp; message: string }\n | { type: 'custom'; validate: (value: unknown) => boolean; message: string }\n | {\n type: 'specificValues';\n values: { id: string; value: string }[];\n message: string;\n };\n\nexport interface Focusable {\n focus: () => void;\n}\n\nexport type EditTrigger =\n | { kind: 'type'; key: string }\n | 'enter'\n | 'click'\n | 'doubleClick';\n\nexport interface CellViewProps<V> {\n value: V;\n typeConfig?: unknown;\n validation?: ValidationRule[];\n /** Stable id of the column. Cell components can use it to derive unique DOM ids. */\n columnId: string;\n /** Stable key of the row. Cell components can use it to derive unique DOM ids. */\n rowKey: string;\n}\n\nexport interface CellEditProps<V> {\n value: V;\n // Method syntax keeps CellEditProps covariant in V (see FieldTypeDefinition).\n onChange(value: V): void;\n onCommit: () => void;\n onCancel: () => void;\n /**\n * Signals that the cell is starting an editing operation that will move focus\n * outside the table (e.g., opening a media manager or a modal). Calling this\n * sets isEditing=true so the table does not clear cell focus while the\n * external UI is open. Pair with onCommit (after the operation succeeds)\n * or onCancel (if the user dismisses without a value change).\n */\n onStartEdit?: () => void;\n /** Ref to the focusable input element. Used for type-to-edit (focusing the input when user starts typing on a focused cell). */\n inputRef?: MutableRefObject<Focusable | undefined | null>;\n typeConfig?: unknown;\n /** How edit mode was triggered. Edit components can use this to customize behavior (e.g., date picker auto-opens on 'type'). */\n editTrigger?: EditTrigger | null;\n /** Column-level validation rules. Edit components can derive UI behavior (e.g., maxLength blocking/tooltip) from these. */\n validation?: ValidationRule[];\n /** Stable id of the column. Cell components can use it to derive unique DOM ids. */\n columnId: string;\n /** Stable key of the row. Cell components can use it to derive unique DOM ids. */\n rowKey: string;\n}\n\n/**\n * Props passed to a cell type's optional `EditWrapper`. The table mounts the\n * wrapper around the cell content and keeps it mounted across the edit↔view\n * transition, so the wrapper can own behavior that must outlive the inline\n * editor (e.g. a reference cell navigating to an overlay and refreshing on\n * return). The wrapper hands anything the editor needs down via its own context.\n */\nexport interface CellEditWrapperProps {\n /** The column's typeConfig (same value handed to the cell's Edit/View). */\n typeConfig?: unknown;\n /** Stable id of the column. */\n columnId: string;\n /** Stable key of the row. */\n rowKey: string;\n /** The cell content (the Edit or View component) to render inside the wrapper. */\n children: ReactNode;\n}\n\n/**\n * The editable-cell slot of a field type definition: how a cell of that type\n * renders, edits, and moves through the clipboard. The definition's key\n * identifies the type and the definition's `value` owns validation, so the\n * slot carries neither.\n *\n * Members that take `V` use method syntax so a definition typed with a\n * concrete value type stays assignable where a loosely typed definition is\n * expected (see `FieldTypeDefinition`).\n */\nexport interface EditableCellConfig<V> {\n /** Render cell in view mode (read-only display). */\n ViewComponent(props: CellViewProps<V>): ReactElement | null;\n /** Render cell in edit mode (inline editor). Optional — omit for view-only cells. */\n EditComponent?(props: CellEditProps<V>): ReactElement | null;\n /** Serialize value to clipboard string. */\n serialize?(value: V): string;\n /** Deserialize clipboard string to value. */\n deserialize?(raw: string): V;\n /** Can this cell type be edited? (default: true) */\n editable?: boolean;\n /** Can this cell type be cleared with Delete key? (default: true) */\n clearable?: boolean;\n /** Toggle value on single click (e.g., boolean checkbox). Skips focus→edit flow. */\n toggleValue?(value: V): V;\n /** Enter edit mode on single click (e.g., image cell with no value). */\n editOnClick?(value: V): boolean;\n /** Show the EditComponent when the cell is focused, without entering edit mode.\n * Keyboard navigation remains active. Similar to toggleValue but without toggling. */\n showEditWhenFocused?: boolean;\n /** Optional wrapper component the cell type defines to own behavior that must\n * outlive the inline editor. Mounted around the cell content and kept mounted\n * across the edit↔view transition. */\n EditWrapper?: ComponentType<CellEditWrapperProps>;\n /** Cell content can grow past row height when focused. Only applies to\n * showEditWhenFocused cells. */\n growable?: boolean;\n /** Called on type-to-edit. Return object with cleared value and/or prevent flag.\n * Return undefined to block the key. Return {} to enter edit mode without changing the value. */\n onTypeToEdit?(params: {\n key: string;\n validation?: ValidationRule[];\n }): { value?: V } | undefined;\n /** Optional hook to normalize the committed value (e.g. auto-prepend https://).\n * Called before the edit commits; the value is written back before committing. */\n normalizeOnCommit?(value: V): V;\n /** When true, type-to-edit flushes the edit-mode render synchronously and\n * focuses the input within the same keydown event, then lets the browser's\n * default action deliver the keystroke to the now-focused input naturally.\n * Use for cells that wrap a controlled input whose value can't be seeded via props. */\n nativeTypeToEdit?: boolean;\n}\n\n/** The props the filter builder passes to a definition's toolbar filter\n * component — the same contract the built-in filter elements receive. */\nexport interface FilterTypeComponentProps<V = any> {\n filter: Filter<V>;\n toolbarItemProps?: { label?: ReactNode };\n toolbarTagProps?: { label?: string };\n accordionItemProps?: { label?: string };\n}\n\n/** The filter slot of a field type definition: the filter state registered on\n * the collection query and the toolbar element that renders it. */\nexport interface FilterTypeConfig {\n createFilter: (field: Field) => Filter<any>;\n Component: ComponentType<FilterTypeComponentProps>;\n}\n\n/** Narrows a definition's filter slot to an active filter config. */\nexport function isFilterTypeConfig(\n filter: FieldTypeDefinition['filter'],\n): filter is FilterTypeConfig {\n return 'createFilter' in filter;\n}\n\n/**\n * The read-only table slot of a field type definition: how a cell of that\n * type renders in the table, and the column's layout. Independent of\n * `editableTable` — neither slot falls back to the other.\n */\nexport interface TableCellConfig<V> {\n /** Render the cell. Method syntax keeps the definition assignable to\n * `FieldTypeDefinition<unknown>` (see FieldTypeDefinition). */\n CellComponent(props: { value: V }): ReactElement | null;\n /** Column width. Required — custom types get no built-in layout default. */\n width: string;\n /** Default: 'start'. */\n align?: 'start' | 'center' | 'end';\n}\n\n/** An extra menu item a field type definition adds to a field's actions menu. */\nexport interface FieldTypeActionItem {\n key: string;\n label: string;\n icon?: ReactNode;\n skin?: 'destructive';\n onClick: (field: Field) => void;\n /** Default: visible. */\n visible?: (field: Field) => boolean;\n /** Returning a string disables the item and shows the tooltip. */\n disabledTooltip?: (field: Field) => string | undefined;\n}\n\n/** The actions slot of a field type definition: extra items for the field's \"more actions\" menu. */\nexport interface FieldActionsConfig {\n items?: FieldTypeActionItem[];\n}\n\n/** Display metadata for a field type: menu label and type icons. */\nexport interface FieldTypeDisplay {\n label: string;\n icons: {\n Small: ComponentType;\n Medium: ComponentType;\n };\n}\n\n/** Value semantics of a field type, independent of any rendering surface.\n * Members use method syntax so a definition typed with a concrete value type\n * stays assignable where a loosely typed definition is expected. */\nexport interface FieldValueBehavior<V> {\n defaultValue?: V;\n isEmpty?(value: V): boolean;\n /** Returns an error message, or null when the value is valid. Every surface\n * that validates a field of this type runs this. */\n validate?(value: V, rules?: ValidationRule[]): string | null;\n}\n\nexport interface FieldCapabilities {\n sortable: boolean;\n}\n\n/**\n * A consumer-defined field type. Defined once on the fields source; every\n * entry point that renders the field resolves its slot from here. `V` is the\n * field's value type — `defineFieldType` infers it, and it keeps all the\n * slots consistent with each other.\n *\n * Sources hold definitions of mixed value types in one\n * `FieldTypeDefinition[]` (= `FieldTypeDefinition<unknown>[]`) array. That\n * works because every member that takes `V` is declared with method syntax,\n * which TypeScript checks bivariantly — so `FieldTypeDefinition<number>` is\n * assignable to `FieldTypeDefinition<unknown>` without casts.\n */\nexport interface FieldTypeDefinition<V = unknown> {\n /** Unique key for the type. A built-in type uses its `PatternsFieldType`; a\n * consumer-defined type uses the string its fields reference via `customType`. */\n type: string;\n display: FieldTypeDisplay;\n /** Filter behavior, or an explicit opt-out — never silently absent. */\n filter: FilterTypeConfig | { filterable: false };\n value?: FieldValueBehavior<V>;\n capabilities?: FieldCapabilities;\n /** Absent ⇒ the field renders with the default cell in the read-only table. */\n table?: TableCellConfig<V>;\n /** Absent ⇒ the field renders as a read-only cell in editable surfaces. */\n editableTable?: EditableCellConfig<V>;\n /** Extra items for the field's \"more actions\" menu. */\n actions?: FieldActionsConfig;\n}\n\n/** Identity factory: infers `V` and keeps the definition's slots type-checked. */\nexport function defineFieldType<V>(\n definition: FieldTypeDefinition<V>,\n): FieldTypeDefinition<V> {\n return definition;\n}\n\n/** Resolves the definition a CUSTOM field references on its source. */\nexport function resolveFieldTypeDefinition(\n source: Pick<FieldsSource, 'fieldTypes'>,\n field: Field,\n) {\n if (field.type !== 'CUSTOM' || !field.customType) {\n return undefined;\n }\n return source.fieldTypes?.find((def) => def.type === field.customType);\n}\n"],"mappings":";;;;;;AAqEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAYA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AA4CA;AACA;;AAQA;AACA;;AAMA;AACO,SAASA,kBAAkBA,CAChCC,MAAqC,EACT;EAC5B,OAAO,cAAc,IAAIA,MAAM;AACjC;;AAEA;AACA;AACA;AACA;AACA;;AAWA;;AAaA;;AAKA;;AASA;AACA;AACA;;AAaA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAkBA;AACO,SAASC,eAAeA,CAC7BC,UAAkC,EACV;EACxB,OAAOA,UAAU;AACnB;;AAEA;AACO,SAASC,0BAA0BA,CACxCC,MAAwC,EACxCC,KAAY,EACZ;EAAA,IAAAC,kBAAA;EACA,IAAID,KAAK,CAACE,IAAI,KAAK,QAAQ,IAAI,CAACF,KAAK,CAACG,UAAU,EAAE;IAChD,OAAOC,SAAS;EAClB;EACA,QAAAH,kBAAA,GAAOF,MAAM,CAACM,UAAU,qBAAjBJ,kBAAA,CAAmBK,IAAI,CAAEC,GAAG,IAAKA,GAAG,CAACL,IAAI,KAAKF,KAAK,CAACG,UAAU,CAAC;AACxE","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["../../../src/types/FieldsSource.ts"],"sourcesContent":["import type { Field, PatternsFieldType } from './Field';\nimport type { FieldActions, Schema } from './Schema';\n\n/** A field the source has archived. Sources without archiving omit `archive`. */\nexport interface ArchivedFieldItem {\n id: string;\n label: string;\n isPii?: boolean;\n}\n\n/** A field type a source allows creating, with display metadata for the modal. */\nexport interface FieldTypeOption {\n /** Stored on the draft; the source maps it back to its native type. */\n id: string;\n label: string;\n namePlaceholder?: string;\n /** Drives the type icon in the modal. */\n patternsType: PatternsFieldType;\n}\n\n/** Read/write permission defaults a source supports, per audience. */\nexport interface FieldPermissions {\n installedApps?: {\n hide?: boolean;\n defaultRead?: boolean;\n defaultWrite?: boolean;\n };\n siteVisitors?: {\n hide?: boolean;\n defaultRead?: boolean;\n defaultWrite?: boolean;\n };\n}\n\n/** The values the add/edit field modal collects, source-neutral. */\nexport interface FieldDraft {\n type?: string;\n key: string;\n name: string;\n readApps: boolean;\n readUsers: boolean;\n writeApps: boolean;\n writeUsers: boolean;\n containsPii: boolean;\n}\n\n/** Add/edit/delete capability a source exposes. Present ⇒ field management UI shows. */\nexport interface FieldManagement {\n /** False ⇒ the add-field CTA is hidden entirely. */\n canAddFields: boolean;\n /** True ⇒ the add-field CTA renders but is disabled. */\n addFieldDisabled: boolean;\n /** Hint text shown beneath the add-field CTA while it is disabled. */\n addFieldDisabledHint?: string;\n canEditField(field: Field): boolean;\n /** `origin` is the UI surface the delete was triggered from; used for BI. */\n deleteField(fieldId: string, origin: string): Promise<void>;\n\n /** Field types the source allows creating, shown in the add/edit modal. */\n creatableFieldTypes: FieldTypeOption[];\n /** Existing field keys, used by the add modal to reject duplicates. */\n existingFieldKeys: string[];\n /** True when no more fields can be added because a limit is reached. */\n maxReached?: boolean;\n /** Permission defaults; absent ⇒ the modal hides the permissions section. */\n permissions?: FieldPermissions;\n /** True when the source supports marking a field as PII. */\n supportsPii?: boolean;\n addField(draft: FieldDraft): Promise<void>;\n editField(fieldId: string, draft: FieldDraft): Promise<void>;\n /** Current values for an existing field, to prefill the edit modal. */\n getFieldDraft(fieldId: string): FieldDraft | undefined;\n}\n\n/** Archiving capability a source exposes. Present ⇒ archive/restore UI shows. */\nexport interface FieldArchive {\n items: ArchivedFieldItem[];\n shouldDisplay: boolean;\n /** Whether the archived-items panel is currently open. */\n isOpen: boolean;\n openPanel(): void;\n closePanel(): void;\n /** `origin` is the UI surface the action was triggered from; used for BI. */\n archiveField(fieldId: string, origin: string): Promise<void>;\n restoreField(fieldId: string, origin: string): Promise<void>;\n /** Display name for a field, for the confirm-dialog copy. */\n fieldName(fieldId: string): string;\n}\n\n/**\n * The source-neutral fields data a collection reads, implemented by each source\n * adapter. Rendering — field modals, filter elements — is not here; it lives in\n * the collection-page layer.\n */\nexport interface FieldsSource {\n /** Loads the fields (schema/data). The collection runs this once, tracks its\n * status, and shows a retry toast on failure. */\n load(): Promise<void>;\n\n fields: Field[];\n filterableFields: Field[];\n /** The fields the manage-fields panel lists — the source's editable fields. */\n manageableFields: Field[];\n /** Column id for a field. Sources that group fields prefix the group; flat sources use the field key. */\n columnIdForField(field: Field): string;\n /** WQL field path for a filter. Sources that group fields prefix the group; flat sources use the field key. */\n queryFieldId(field: Field): string;\n /** The value to display for a field on a row. */\n getCellValue(row: unknown, field: Field): unknown;\n /** Writes a new value for a field on a row, returning the updated row. Present when cells can be edited in place. */\n setCellValue?(row: unknown, field: Field, value: unknown): unknown;\n\n /** Replaces the source's fields with a freshly-returned map, updating the\n * columns with no refetch. Present when the source holds its fields in a\n * mergeable state (e.g. a loaded schema); used after an import creates fields. */\n mergeFields?(fields: Schema['fields']): void;\n\n /** Present when fields can be added/edited/deleted. */\n fieldManagement?: FieldManagement;\n\n /** The collection's field-mutation actions (delete/makePrimary/duplicate/…),\n * driving the shared FieldActions menu. Absent ⇒ no per-field action menu. */\n fieldActions?: FieldActions;\n\n /** Present when the source supports archiving fields. */\n archive?: FieldArchive;\n\n /** Present when fields are grouped. Absent ⇒ flat list. */\n groups?: {\n all: string[];\n fieldsForGroup(group: string): Field[];\n };\n}\n"],"mappings":"","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["../../../src/types/FieldsSource.ts"],"sourcesContent":["import type { Field, PatternsFieldType } from './Field';\nimport type { FieldActions, Schema } from './Schema';\nimport type { FieldTypeDefinition } from './FieldTypeDefinition';\n\n/** A field the source has archived. Sources without archiving omit `archive`. */\nexport interface ArchivedFieldItem {\n id: string;\n label: string;\n isPii?: boolean;\n}\n\n/** A field type a source allows creating, with display metadata for the modal. */\nexport interface FieldTypeOption {\n /** Stored on the draft; the source maps it back to its native type. */\n id: string;\n label: string;\n namePlaceholder?: string;\n subtitle?: string;\n /** Group heading in the type list; types sharing a heading form one group. */\n group?: string;\n /** Drives the type icon in the modal. */\n patternsType: PatternsFieldType;\n}\n\n/** Read/write permission defaults a source supports, per audience. */\nexport interface FieldPermissions {\n installedApps?: {\n hide?: boolean;\n defaultRead?: boolean;\n defaultWrite?: boolean;\n };\n siteVisitors?: {\n hide?: boolean;\n defaultRead?: boolean;\n defaultWrite?: boolean;\n };\n}\n\n/** The values the add/edit field modal collects, source-neutral. */\nexport interface FieldDraft {\n type?: string;\n key: string;\n name: string;\n readApps: boolean;\n readUsers: boolean;\n writeApps: boolean;\n writeUsers: boolean;\n containsPii: boolean;\n}\n\n/** The add-modal subtitle: a line of text, an optional second note line, and an\n * optional \"learn more\" link. */\nexport interface FieldModalSubtitle {\n text: string;\n note?: string;\n learnMore?: { label: string; href: string };\n}\n\n/** Title and subtitle a source supplies for the add/edit field modal. */\nexport interface FieldModalText {\n addTitle: string;\n editTitle: string;\n /** Subtitle for the add modal; omit for none. */\n addSubtitle?: FieldModalSubtitle;\n}\n\n/** Add/edit/delete capability a source exposes. Present ⇒ field management UI shows. */\nexport interface FieldManagement {\n /** False ⇒ the add-field CTA is hidden entirely. */\n canAddFields: boolean;\n /** True ⇒ the add-field CTA renders but is disabled. */\n addFieldDisabled: boolean;\n /** Hint text shown beneath the add-field CTA while it is disabled. */\n addFieldDisabledHint?: string;\n canEditField(field: Field): boolean;\n /** `origin` is the UI surface the delete was triggered from; used for BI. */\n deleteField(fieldId: string, origin: string): Promise<void>;\n\n /** Field types the source allows creating, shown in the add/edit modal. */\n creatableFieldTypes: FieldTypeOption[];\n /** Existing field keys, used by the add modal to reject duplicates. */\n existingFieldKeys: string[];\n /** True when no more fields can be added because a limit is reached. */\n maxReached?: boolean;\n /** Permission defaults; absent ⇒ the modal hides the permissions section. */\n permissions?: FieldPermissions;\n /** Source-wide default for whether a field type can be marked PII. A field\n * type overrides it by declaring `form.pii` in its definition. */\n supportsPii?: boolean;\n /** Title and subtitle for the add/edit field modal. */\n fieldModalText: FieldModalText;\n addField(draft: FieldDraft): Promise<void>;\n editField(fieldId: string, draft: FieldDraft): Promise<void>;\n /** Current values for an existing field, to prefill the edit modal. */\n getFieldDraft(fieldId: string): FieldDraft | undefined;\n}\n\n/** Archiving capability a source exposes. Present ⇒ archive/restore UI shows. */\nexport interface FieldArchive {\n items: ArchivedFieldItem[];\n shouldDisplay: boolean;\n /** Whether the archived-items panel is currently open. */\n isOpen: boolean;\n openPanel(): void;\n closePanel(): void;\n /** `origin` is the UI surface the action was triggered from; used for BI. */\n archiveField(fieldId: string, origin: string): Promise<void>;\n restoreField(fieldId: string, origin: string): Promise<void>;\n /** Display name for a field, for the confirm-dialog copy. */\n fieldName(fieldId: string): string;\n}\n\n/**\n * The source-neutral fields data a collection reads, implemented by each source\n * adapter. Rendering — field modals, filter elements — is not here; it lives in\n * the collection-page layer.\n */\nexport interface FieldsSource {\n /** Loads the fields (schema/data). The collection runs this once, tracks its\n * status, and shows a retry toast on failure. */\n load(): Promise<void>;\n\n fields: Field[];\n filterableFields: Field[];\n /** The fields the manage-fields panel lists — the source's editable fields. */\n manageableFields: Field[];\n /** Column id for a field. Sources that group fields prefix the group; flat sources use the field key. */\n columnIdForField(field: Field): string;\n /** WQL field path for a filter. Sources that group fields prefix the group; flat sources use the field key. */\n queryFieldId(field: Field): string;\n /** The value to display for a field on a row. */\n getCellValue(row: unknown, field: Field): unknown;\n /** Writes a new value for a field on a row, returning the updated row. Present when cells can be edited in place. */\n setCellValue?(row: unknown, field: Field, value: unknown): unknown;\n\n /** Replaces the source's fields with a freshly-returned map, updating the\n * columns with no refetch. Present when the source holds its fields in a\n * mergeable state (e.g. a loaded schema); used after an import creates fields. */\n mergeFields?(fields: Schema['fields']): void;\n\n /** Field-type definitions the source supports. Built-in types are keyed by\n * their `PatternsFieldType`; consumer-defined types are keyed by the string a\n * field references via `customType`. */\n fieldTypes?: FieldTypeDefinition[];\n\n /** Present when fields can be added/edited/deleted. */\n fieldManagement?: FieldManagement;\n\n /** The collection's field-mutation actions (delete/makePrimary/duplicate/…),\n * driving the shared FieldActions menu. Absent ⇒ no per-field action menu. */\n fieldActions?: FieldActions;\n\n /** Present when the source supports archiving fields. */\n archive?: FieldArchive;\n\n /** Present when fields are grouped. Absent ⇒ flat list. */\n groups?: {\n all: string[];\n fieldsForGroup(group: string): Field[];\n };\n}\n"],"mappings":"","ignoreList":[]}
@@ -11,15 +11,23 @@ exports.sourceFetchData = sourceFetchData;
11
11
  /** The resolved source: defaults filled, `loadSchema` memoized. */
12
12
 
13
13
  /** The collection fetch derived from a source: loads once, then queries the
14
- * backend's `find` scoped to the source's searchable fields. */
14
+ * backend's `find`.
15
+ *
16
+ * Free-text search covers the columns the page is showing: the table keeps
17
+ * `query.columns` in sync with them, so the scope comes off the query rather
18
+ * than being threaded in. Falls back to the source's own `searchableFieldIds`
19
+ * before any columns are known (or with no table attached). */
15
20
  function sourceFetchData(source) {
16
21
  return async query => {
17
22
  const {
18
23
  backend,
19
24
  schema
20
25
  } = await source.loadSchema();
26
+ // `columns` is the visible-column list; `fields` is the projection, which a
27
+ // source-driven table doesn't set.
28
+ const visibleColumns = query.columns;
21
29
  return backend.find(query, {
22
- searchableFieldIds: source.searchableFieldIds(schema)
30
+ searchableFieldIds: visibleColumns != null && visibleColumns.length ? visibleColumns : source.searchableFieldIds(schema)
23
31
  });
24
32
  };
25
33
  }
@@ -31,8 +39,10 @@ function defineSchemaSource(input) {
31
39
  collectionId,
32
40
  paginationMode = 'offset',
33
41
  creatableFieldTypes = [],
42
+ fieldTypes = [],
34
43
  fieldPermissions,
35
44
  supportsPii = false,
45
+ fieldModalText,
36
46
  references = {}
37
47
  } = input;
38
48
  let loaded;
@@ -78,12 +88,14 @@ function defineSchemaSource(input) {
78
88
  itemName,
79
89
  references,
80
90
  creatableFieldTypes,
91
+ fieldTypes,
81
92
  searchableFieldIds,
82
93
  canAddFields,
83
94
  canEditField,
84
95
  canDeleteField,
85
96
  fieldPermissions,
86
97
  supportsPii,
98
+ fieldModalText,
87
99
  getFieldValue,
88
100
  setFieldValue
89
101
  };
@@ -1 +1 @@
1
- {"version":3,"names":["sourceFetchData","source","query","backend","schema","loadSchema","find","searchableFieldIds","defineSchemaSource","input","collectionId","paginationMode","creatableFieldTypes","fieldPermissions","supportsPii","references","loaded","then","result","catch","error","undefined","s","displayField","itemKey","item","_schema","String","idField","itemName","_schema2","canAddFields","_s$schemaPermissions","schemaPermissions","createField","canEditField","field","_s$schemaPermissions2","updateField","isSystem","canDeleteField","_s$schemaPermissions3","deleteField","getFieldValue","id","setFieldValue","value"],"sources":["../../../src/types/SchemaSource.ts"],"sourcesContent":["import type { Field, PatternsFieldType } from './Field';\nimport type { Schema, LoadedSchema, FindOptions } from './Schema';\nimport type { FieldPermissions } from './FieldsSource';\nimport type {\n OffsetQuery,\n OffsetQueryResult,\n} from '../hooks/paginationModeConfig';\n\n/** A field type a source allows creating. Declare one entry per\n * `patternsType` — the type is the entry's identity. */\nexport interface CreatableFieldType {\n patternsType: PatternsFieldType;\n /** Custom display name. Absent ⇒ the UI's standard name for this type. */\n label?: string;\n /** Custom name-input placeholder. Absent ⇒ the UI's standard placeholder. */\n namePlaceholder?: string;\n}\n\n/** What a caller passes. Only `collectionId` and `loadSchema` are required. */\nexport interface SchemaSourceInput<T> {\n collectionId: string;\n loadSchema: () => Promise<LoadedSchema<T>>;\n /** Sources for collections this one references, keyed by the referenced\n * collection id. A renderer resolves a reference field through the matching\n * entry — its `loadSchema` gives the referenced schema and `sourceFetchData`\n * queries the referenced items. Default: none. */\n references?: Record<string, SchemaSource<any>>;\n paginationMode?: 'offset';\n /** Stable row identity. Default: `row[idField]`. */\n itemKey?: (item: T) => string;\n /** Row display label. Default: `row[displayField]`. */\n itemName?: (item: T) => string;\n /** Fields free-text search runs against. Default: `[displayField]`. */\n searchableFieldIds?: (schema: Schema) => string[];\n /** Field types the add-field modal offers. Default: none. */\n creatableFieldTypes?: CreatableFieldType[];\n /** Default: `schemaPermissions.createField`. */\n canAddFields?: (schema: Schema) => boolean;\n /** Default: `schemaPermissions.updateField && !field.isSystem`. */\n canEditField?: (field: Field, schema: Schema) => boolean;\n /** Default: `schemaPermissions.deleteField && !field.isSystem`. */\n canDeleteField?: (field: Field, schema: Schema) => boolean;\n /** Permission defaults shown in the modal. Absent ⇒ no permissions section. */\n fieldPermissions?: FieldPermissions;\n /** Default: `false`. */\n supportsPii?: boolean;\n /** The value of a field on an item. Default: `item[field.id]`. */\n getFieldValue?: (item: T, field: Field) => unknown;\n /** Writes a field's value on an item, returning the updated item.\n * Default: `{ ...item, [field.id]: value }`. */\n setFieldValue?: (item: T, field: Field, value: unknown) => T;\n}\n\n/** The resolved source: defaults filled, `loadSchema` memoized. */\nexport interface SchemaSource<T> {\n readonly collectionId: string;\n loadSchema: () => Promise<LoadedSchema<T>>;\n readonly paginationMode: 'offset';\n /** Stable row identity. */\n itemKey: (item: T) => string;\n /** Row display label. */\n itemName: (item: T) => string;\n /** Sources for referenced collections, keyed by referenced collection id. */\n readonly references: Record<string, SchemaSource<any>>;\n readonly creatableFieldTypes: CreatableFieldType[];\n searchableFieldIds: (schema: Schema) => string[];\n canAddFields: (schema: Schema) => boolean;\n canEditField: (field: Field, schema: Schema) => boolean;\n canDeleteField: (field: Field, schema: Schema) => boolean;\n readonly fieldPermissions?: FieldPermissions;\n readonly supportsPii: boolean;\n /** The value of a field on an item. */\n getFieldValue: (item: T, field: Field) => unknown;\n /** Writes a field's value on an item, returning the updated item. */\n setFieldValue: (item: T, field: Field, value: unknown) => T;\n}\n\ntype OffsetFind<T> = (\n query: OffsetQuery,\n options?: FindOptions,\n) => Promise<OffsetQueryResult<T>>;\n\n/** The collection fetch derived from a source: loads once, then queries the\n * backend's `find` scoped to the source's searchable fields. */\nexport function sourceFetchData<T>(\n source: SchemaSource<T>,\n): (query: OffsetQuery) => Promise<OffsetQueryResult<T>> {\n return async (query) => {\n const { backend, schema } = await source.loadSchema();\n return (backend.find as OffsetFind<T>)(query, {\n searchableFieldIds: source.searchableFieldIds(schema),\n });\n };\n}\n\n/** Fills source-neutral defaults over `input` and memoizes the load so the collection\n * query and the table columns share a single fetch. */\nexport function defineSchemaSource<T>(\n input: SchemaSourceInput<T>,\n): SchemaSource<T> {\n const {\n collectionId,\n paginationMode = 'offset',\n creatableFieldTypes = [],\n fieldPermissions,\n supportsPii = false,\n references = {},\n } = input;\n\n let loaded: Promise<LoadedSchema<T>> | undefined;\n let schema: Schema | undefined;\n\n const loadSchema = () =>\n (loaded ??= input\n .loadSchema()\n .then((result) => {\n schema = result.schema;\n return result;\n })\n .catch((error) => {\n loaded = undefined;\n throw error;\n }));\n\n const searchableFieldIds =\n input.searchableFieldIds ?? ((s) => [s.displayField]);\n\n const itemKey =\n input.itemKey ??\n ((item) =>\n String(\n (item as Record<string, unknown>)[schema?.idField ?? '_id'] ?? '',\n ));\n const itemName =\n input.itemName ??\n ((item) =>\n String(\n (item as Record<string, unknown>)[schema?.displayField ?? '_id'] ?? '',\n ));\n const canAddFields =\n input.canAddFields ?? ((s) => s.schemaPermissions?.createField ?? false);\n const canEditField =\n input.canEditField ??\n ((field, s) =>\n (s.schemaPermissions?.updateField ?? false) && !field.isSystem);\n const canDeleteField =\n input.canDeleteField ??\n ((field, s) =>\n (s.schemaPermissions?.deleteField ?? false) && !field.isSystem);\n const getFieldValue =\n input.getFieldValue ??\n ((item, field) => (item as Record<string, unknown>)[field.id]);\n const setFieldValue =\n input.setFieldValue ??\n ((item, field, value) => ({ ...item, [field.id]: value } as T));\n\n return {\n collectionId,\n loadSchema,\n paginationMode,\n itemKey,\n itemName,\n references,\n creatableFieldTypes,\n searchableFieldIds,\n canAddFields,\n canEditField,\n canDeleteField,\n fieldPermissions,\n supportsPii,\n getFieldValue,\n setFieldValue,\n };\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["sourceFetchData","source","query","backend","schema","loadSchema","visibleColumns","columns","find","searchableFieldIds","length","defineSchemaSource","input","collectionId","paginationMode","creatableFieldTypes","fieldTypes","fieldPermissions","supportsPii","fieldModalText","references","loaded","then","result","catch","error","undefined","s","displayField","itemKey","item","_schema","String","idField","itemName","_schema2","canAddFields","_s$schemaPermissions","schemaPermissions","createField","canEditField","field","_s$schemaPermissions2","updateField","isSystem","canDeleteField","_s$schemaPermissions3","deleteField","getFieldValue","id","setFieldValue","value"],"sources":["../../../src/types/SchemaSource.ts"],"sourcesContent":["import type { Field, PatternsFieldType } from './Field';\nimport type { FieldTypeDefinition } from './FieldTypeDefinition';\nimport type { Schema, LoadedSchema, FindOptions } from './Schema';\nimport type { FieldModalText, FieldPermissions } from './FieldsSource';\nimport type {\n OffsetQuery,\n OffsetQueryResult,\n} from '../hooks/paginationModeConfig';\n\n/** A field type a source allows creating. Declare one entry per\n * `patternsType` — the type is the entry's identity. */\nexport interface CreatableFieldType {\n patternsType: PatternsFieldType;\n /** Custom display name. Absent ⇒ the UI's standard name for this type. */\n label?: string;\n /** Custom name-input placeholder. Absent ⇒ the UI's standard placeholder. */\n namePlaceholder?: string;\n /** A one-line description shown under the type in the add-field menu. */\n subtitle?: string;\n /** The group heading this type is listed under in the add-field menu, in\n * first-seen order. Types sharing a heading form one group. Absent ⇒\n * ungrouped. */\n group?: string;\n}\n\n/** What a caller passes. Only `collectionId` and `loadSchema` are required. */\nexport interface SchemaSourceInput<T> {\n collectionId: string;\n loadSchema: () => Promise<LoadedSchema<T>>;\n /** Sources for collections this one references, keyed by the referenced\n * collection id. A renderer resolves a reference field through the matching\n * entry — its `loadSchema` gives the referenced schema and `sourceFetchData`\n * queries the referenced items. Default: none. */\n references?: Record<string, SchemaSource<any>>;\n paginationMode?: 'offset';\n /** Stable row identity. Default: `row[idField]`. */\n itemKey?: (item: T) => string;\n /** Row display label. Default: `row[displayField]`. */\n itemName?: (item: T) => string;\n /** Fields free-text search runs against. Default: `[displayField]`. */\n searchableFieldIds?: (schema: Schema) => string[];\n /** Field types the add-field modal offers. Default: none. */\n creatableFieldTypes?: CreatableFieldType[];\n /** Consumer-defined field types. A field with `type: 'CUSTOM'` resolves its\n * definition here by `customType`. Default: none. */\n fieldTypes?: FieldTypeDefinition[];\n /** Default: `schemaPermissions.createField`. */\n canAddFields?: (schema: Schema) => boolean;\n /** Default: `schemaPermissions.updateField && !field.isSystem`. */\n canEditField?: (field: Field, schema: Schema) => boolean;\n /** Default: `schemaPermissions.deleteField && !field.isSystem`. */\n canDeleteField?: (field: Field, schema: Schema) => boolean;\n /** Permission defaults shown in the modal. Absent ⇒ no permissions section. */\n fieldPermissions?: FieldPermissions;\n /** Default: `false`. */\n supportsPii?: boolean;\n /** Title and subtitle for the add/edit field modal. Absent ⇒ generic default. */\n fieldModalText?: FieldModalText;\n /** The value of a field on an item. Default: `item[field.id]`. */\n getFieldValue?: (item: T, field: Field) => unknown;\n /** Writes a field's value on an item, returning the updated item.\n * Default: `{ ...item, [field.id]: value }`. */\n setFieldValue?: (item: T, field: Field, value: unknown) => T;\n}\n\n/** The resolved source: defaults filled, `loadSchema` memoized. */\nexport interface SchemaSource<T> {\n readonly collectionId: string;\n loadSchema: () => Promise<LoadedSchema<T>>;\n readonly paginationMode: 'offset';\n /** Stable row identity. */\n itemKey: (item: T) => string;\n /** Row display label. */\n itemName: (item: T) => string;\n /** Sources for referenced collections, keyed by referenced collection id. */\n readonly references: Record<string, SchemaSource<any>>;\n readonly creatableFieldTypes: CreatableFieldType[];\n readonly fieldTypes: FieldTypeDefinition[];\n searchableFieldIds: (schema: Schema) => string[];\n canAddFields: (schema: Schema) => boolean;\n canEditField: (field: Field, schema: Schema) => boolean;\n canDeleteField: (field: Field, schema: Schema) => boolean;\n readonly fieldPermissions?: FieldPermissions;\n readonly supportsPii: boolean;\n readonly fieldModalText?: FieldModalText;\n /** The value of a field on an item. */\n getFieldValue: (item: T, field: Field) => unknown;\n /** Writes a field's value on an item, returning the updated item. */\n setFieldValue: (item: T, field: Field, value: unknown) => T;\n}\n\ntype OffsetFind<T> = (\n query: OffsetQuery,\n options?: FindOptions,\n) => Promise<OffsetQueryResult<T>>;\n\n/** The collection fetch derived from a source: loads once, then queries the\n * backend's `find`.\n *\n * Free-text search covers the columns the page is showing: the table keeps\n * `query.columns` in sync with them, so the scope comes off the query rather\n * than being threaded in. Falls back to the source's own `searchableFieldIds`\n * before any columns are known (or with no table attached). */\nexport function sourceFetchData<T>(\n source: SchemaSource<T>,\n): (query: OffsetQuery) => Promise<OffsetQueryResult<T>> {\n return async (query) => {\n const { backend, schema } = await source.loadSchema();\n // `columns` is the visible-column list; `fields` is the projection, which a\n // source-driven table doesn't set.\n const visibleColumns = query.columns;\n return (backend.find as OffsetFind<T>)(query, {\n searchableFieldIds: visibleColumns?.length\n ? visibleColumns\n : source.searchableFieldIds(schema),\n });\n };\n}\n\n/** Fills source-neutral defaults over `input` and memoizes the load so the collection\n * query and the table columns share a single fetch. */\nexport function defineSchemaSource<T>(\n input: SchemaSourceInput<T>,\n): SchemaSource<T> {\n const {\n collectionId,\n paginationMode = 'offset',\n creatableFieldTypes = [],\n fieldTypes = [],\n fieldPermissions,\n supportsPii = false,\n fieldModalText,\n references = {},\n } = input;\n\n let loaded: Promise<LoadedSchema<T>> | undefined;\n let schema: Schema | undefined;\n\n const loadSchema = () =>\n (loaded ??= input\n .loadSchema()\n .then((result) => {\n schema = result.schema;\n return result;\n })\n .catch((error) => {\n loaded = undefined;\n throw error;\n }));\n\n const searchableFieldIds =\n input.searchableFieldIds ?? ((s) => [s.displayField]);\n\n const itemKey =\n input.itemKey ??\n ((item) =>\n String(\n (item as Record<string, unknown>)[schema?.idField ?? '_id'] ?? '',\n ));\n const itemName =\n input.itemName ??\n ((item) =>\n String(\n (item as Record<string, unknown>)[schema?.displayField ?? '_id'] ?? '',\n ));\n const canAddFields =\n input.canAddFields ?? ((s) => s.schemaPermissions?.createField ?? false);\n const canEditField =\n input.canEditField ??\n ((field, s) =>\n (s.schemaPermissions?.updateField ?? false) && !field.isSystem);\n const canDeleteField =\n input.canDeleteField ??\n ((field, s) =>\n (s.schemaPermissions?.deleteField ?? false) && !field.isSystem);\n const getFieldValue =\n input.getFieldValue ??\n ((item, field) => (item as Record<string, unknown>)[field.id]);\n const setFieldValue =\n input.setFieldValue ??\n ((item, field, value) => ({ ...item, [field.id]: value } as T));\n\n return {\n collectionId,\n loadSchema,\n paginationMode,\n itemKey,\n itemName,\n references,\n creatableFieldTypes,\n fieldTypes,\n searchableFieldIds,\n canAddFields,\n canEditField,\n canDeleteField,\n fieldPermissions,\n supportsPii,\n fieldModalText,\n getFieldValue,\n setFieldValue,\n };\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+
3
+ var _SchemaSource = require("../SchemaSource");
4
+ var _FieldTypeDefinition = require("../FieldTypeDefinition");
5
+ const loadSchema = () => Promise.reject(new Error('not loaded in this test'));
6
+ const Icon = () => null;
7
+ describe('defineSchemaSource — fieldTypes', () => {
8
+ it('defaults fieldTypes to an empty array', () => {
9
+ const source = (0, _SchemaSource.defineSchemaSource)({
10
+ collectionId: 'c1',
11
+ loadSchema
12
+ });
13
+ expect(source.fieldTypes).toEqual([]);
14
+ });
15
+ it('carries fieldTypes through to the resolved source untouched', () => {
16
+ const tags = (0, _FieldTypeDefinition.defineFieldType)({
17
+ type: 'test.tags',
18
+ display: {
19
+ label: 'Tags',
20
+ icons: {
21
+ Small: Icon,
22
+ Medium: Icon
23
+ }
24
+ },
25
+ filter: {
26
+ filterable: false
27
+ }
28
+ });
29
+ const source = (0, _SchemaSource.defineSchemaSource)({
30
+ collectionId: 'c1',
31
+ loadSchema,
32
+ fieldTypes: [tags]
33
+ });
34
+ expect(source.fieldTypes).toEqual([tags]);
35
+ expect(source.fieldTypes[0]).toBe(tags);
36
+ });
37
+ it('carries a full filter config through to the resolved source', () => {
38
+ const createFilter = () => ({});
39
+ const Component = () => null;
40
+ const tags = (0, _FieldTypeDefinition.defineFieldType)({
41
+ type: 'test.tags',
42
+ display: {
43
+ label: 'Tags',
44
+ icons: {
45
+ Small: Icon,
46
+ Medium: Icon
47
+ }
48
+ },
49
+ filter: {
50
+ createFilter,
51
+ Component
52
+ }
53
+ });
54
+ const source = (0, _SchemaSource.defineSchemaSource)({
55
+ collectionId: 'c1',
56
+ loadSchema,
57
+ fieldTypes: [tags]
58
+ });
59
+ expect(source.fieldTypes[0].filter).toEqual({
60
+ createFilter,
61
+ Component
62
+ });
63
+ });
64
+ describe('isFilterTypeConfig', () => {
65
+ it('narrows an active filter config', () => {
66
+ expect((0, _FieldTypeDefinition.isFilterTypeConfig)({
67
+ createFilter: () => ({}),
68
+ Component: () => null
69
+ })).toBe(true);
70
+ });
71
+ it('rejects the opt-out variant', () => {
72
+ expect((0, _FieldTypeDefinition.isFilterTypeConfig)({
73
+ filterable: false
74
+ })).toBe(false);
75
+ });
76
+ });
77
+ });
78
+ //# sourceMappingURL=defineSchemaSource.fieldTypes.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_SchemaSource","require","_FieldTypeDefinition","loadSchema","Promise","reject","Error","Icon","describe","it","source","defineSchemaSource","collectionId","expect","fieldTypes","toEqual","tags","defineFieldType","type","display","label","icons","Small","Medium","filter","filterable","toBe","createFilter","Component","isFilterTypeConfig"],"sources":["../../../../src/types/__tests__/defineSchemaSource.fieldTypes.spec.ts"],"sourcesContent":["import { defineSchemaSource } from '../SchemaSource';\nimport { defineFieldType, isFilterTypeConfig } from '../FieldTypeDefinition';\nimport type { Filter } from '../../model';\n\nconst loadSchema = () => Promise.reject(new Error('not loaded in this test'));\n\nconst Icon = () => null;\n\ndescribe('defineSchemaSource — fieldTypes', () => {\n it('defaults fieldTypes to an empty array', () => {\n const source = defineSchemaSource({ collectionId: 'c1', loadSchema });\n\n expect(source.fieldTypes).toEqual([]);\n });\n\n it('carries fieldTypes through to the resolved source untouched', () => {\n const tags = defineFieldType<string[]>({\n type: 'test.tags',\n display: { label: 'Tags', icons: { Small: Icon, Medium: Icon } },\n filter: { filterable: false },\n });\n const source = defineSchemaSource({\n collectionId: 'c1',\n loadSchema,\n fieldTypes: [tags],\n });\n\n expect(source.fieldTypes).toEqual([tags]);\n expect(source.fieldTypes[0]).toBe(tags);\n });\n\n it('carries a full filter config through to the resolved source', () => {\n const createFilter = () => ({} as Filter<string[]>);\n const Component = () => null;\n const tags = defineFieldType<string[]>({\n type: 'test.tags',\n display: { label: 'Tags', icons: { Small: Icon, Medium: Icon } },\n filter: { createFilter, Component },\n });\n const source = defineSchemaSource({\n collectionId: 'c1',\n loadSchema,\n fieldTypes: [tags],\n });\n\n expect(source.fieldTypes[0].filter).toEqual({ createFilter, Component });\n });\n\n describe('isFilterTypeConfig', () => {\n it('narrows an active filter config', () => {\n expect(\n isFilterTypeConfig({\n createFilter: () => ({} as Filter<string[]>),\n Component: () => null,\n }),\n ).toBe(true);\n });\n\n it('rejects the opt-out variant', () => {\n expect(isFilterTypeConfig({ filterable: false })).toBe(false);\n });\n });\n});\n"],"mappings":";;AAAA,IAAAA,aAAA,GAAAC,OAAA;AACA,IAAAC,oBAAA,GAAAD,OAAA;AAGA,MAAME,UAAU,GAAGA,CAAA,KAAMC,OAAO,CAACC,MAAM,CAAC,IAAIC,KAAK,CAAC,yBAAyB,CAAC,CAAC;AAE7E,MAAMC,IAAI,GAAGA,CAAA,KAAM,IAAI;AAEvBC,QAAQ,CAAC,iCAAiC,EAAE,MAAM;EAChDC,EAAE,CAAC,uCAAuC,EAAE,MAAM;IAChD,MAAMC,MAAM,GAAG,IAAAC,gCAAkB,EAAC;MAAEC,YAAY,EAAE,IAAI;MAAET;IAAW,CAAC,CAAC;IAErEU,MAAM,CAACH,MAAM,CAACI,UAAU,CAAC,CAACC,OAAO,CAAC,EAAE,CAAC;EACvC,CAAC,CAAC;EAEFN,EAAE,CAAC,6DAA6D,EAAE,MAAM;IACtE,MAAMO,IAAI,GAAG,IAAAC,oCAAe,EAAW;MACrCC,IAAI,EAAE,WAAW;MACjBC,OAAO,EAAE;QAAEC,KAAK,EAAE,MAAM;QAAEC,KAAK,EAAE;UAAEC,KAAK,EAAEf,IAAI;UAAEgB,MAAM,EAAEhB;QAAK;MAAE,CAAC;MAChEiB,MAAM,EAAE;QAAEC,UAAU,EAAE;MAAM;IAC9B,CAAC,CAAC;IACF,MAAMf,MAAM,GAAG,IAAAC,gCAAkB,EAAC;MAChCC,YAAY,EAAE,IAAI;MAClBT,UAAU;MACVW,UAAU,EAAE,CAACE,IAAI;IACnB,CAAC,CAAC;IAEFH,MAAM,CAACH,MAAM,CAACI,UAAU,CAAC,CAACC,OAAO,CAAC,CAACC,IAAI,CAAC,CAAC;IACzCH,MAAM,CAACH,MAAM,CAACI,UAAU,CAAC,CAAC,CAAC,CAAC,CAACY,IAAI,CAACV,IAAI,CAAC;EACzC,CAAC,CAAC;EAEFP,EAAE,CAAC,6DAA6D,EAAE,MAAM;IACtE,MAAMkB,YAAY,GAAGA,CAAA,MAAO,CAAC,CAAC,CAAqB;IACnD,MAAMC,SAAS,GAAGA,CAAA,KAAM,IAAI;IAC5B,MAAMZ,IAAI,GAAG,IAAAC,oCAAe,EAAW;MACrCC,IAAI,EAAE,WAAW;MACjBC,OAAO,EAAE;QAAEC,KAAK,EAAE,MAAM;QAAEC,KAAK,EAAE;UAAEC,KAAK,EAAEf,IAAI;UAAEgB,MAAM,EAAEhB;QAAK;MAAE,CAAC;MAChEiB,MAAM,EAAE;QAAEG,YAAY;QAAEC;MAAU;IACpC,CAAC,CAAC;IACF,MAAMlB,MAAM,GAAG,IAAAC,gCAAkB,EAAC;MAChCC,YAAY,EAAE,IAAI;MAClBT,UAAU;MACVW,UAAU,EAAE,CAACE,IAAI;IACnB,CAAC,CAAC;IAEFH,MAAM,CAACH,MAAM,CAACI,UAAU,CAAC,CAAC,CAAC,CAACU,MAAM,CAAC,CAACT,OAAO,CAAC;MAAEY,YAAY;MAAEC;IAAU,CAAC,CAAC;EAC1E,CAAC,CAAC;EAEFpB,QAAQ,CAAC,oBAAoB,EAAE,MAAM;IACnCC,EAAE,CAAC,iCAAiC,EAAE,MAAM;MAC1CI,MAAM,CACJ,IAAAgB,uCAAkB,EAAC;QACjBF,YAAY,EAAEA,CAAA,MAAO,CAAC,CAAC,CAAqB;QAC5CC,SAAS,EAAEA,CAAA,KAAM;MACnB,CAAC,CACH,CAAC,CAACF,IAAI,CAAC,IAAI,CAAC;IACd,CAAC,CAAC;IAEFjB,EAAE,CAAC,6BAA6B,EAAE,MAAM;MACtCI,MAAM,CAAC,IAAAgB,uCAAkB,EAAC;QAAEJ,UAAU,EAAE;MAAM,CAAC,CAAC,CAAC,CAACC,IAAI,CAAC,KAAK,CAAC;IAC/D,CAAC,CAAC;EACJ,CAAC,CAAC;AACJ,CAAC,CAAC","ignoreList":[]}
@@ -2,7 +2,7 @@ import { useCallback } from 'react';
2
2
  import { useWixPatternsContainer, useCollectionContext } from '../providers';
3
3
  import { ImportState, } from '../state';
4
4
  export function useCreateImport(params) {
5
- const { config, importInputs, fieldsSource, detectColumnType, importModalState } = params;
5
+ const { config, importInputs, fieldsSource, detectColumnType, importModalState, } = params;
6
6
  const { showToast, translate, errorHandler, window, httpClient } = useWixPatternsContainer();
7
7
  const collection = useCollectionContext();
8
8
  return useCallback(() => {
@@ -1 +1 @@
1
- {"version":3,"file":"useImport.js","sourceRoot":"","sources":["../../../src/hooks/useImport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAC7E,OAAO,EAEL,WAAW,GAIZ,MAAM,UAAU,CAAC;AAgBlB,MAAM,UAAU,eAAe,CAC7B,MAAuB;IAEvB,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,GAC9E,MAAM,CAAC;IACT,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,GAC9D,uBAAuB,EAAE,CAAC;IAC5B,MAAM,UAAU,GAAG,oBAAoB,EAAQ,CAAC;IAEhD,OAAO,WAAW,CAAC,GAAG,EAAE;QACtB,OAAO,IAAI,WAAW,CAAC;YACrB,MAAM;YACN,YAAY;YACZ,YAAY;YACZ,UAAU;YACV,gBAAgB;YAChB,UAAU;YACV,gBAAgB;YAChB,SAAS;YACT,SAAS;YACT,YAAY;YACZ,WAAW,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE;gBACjC,MAAM,CAAC,QAAQ,CACb,+BAA+B,kBAAkB,CAAC,OAAO,CAAC,EAAE,EAC5D,QAAQ,CACT,CAAC;YACJ,CAAC;SACF,CAAC,CAAC;IACL,CAAC,EAAE,EAAE,CAAC,CAAC;AACT,CAAC"}
1
+ {"version":3,"file":"useImport.js","sourceRoot":"","sources":["../../../src/hooks/useImport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAC7E,OAAO,EAEL,WAAW,GAIZ,MAAM,UAAU,CAAC;AAgBlB,MAAM,UAAU,eAAe,CAC7B,MAAuB;IAEvB,MAAM,EACJ,MAAM,EACN,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,GACjB,GAAG,MAAM,CAAC;IACX,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,GAC9D,uBAAuB,EAAE,CAAC;IAC5B,MAAM,UAAU,GAAG,oBAAoB,EAAQ,CAAC;IAEhD,OAAO,WAAW,CAAC,GAAG,EAAE;QACtB,OAAO,IAAI,WAAW,CAAC;YACrB,MAAM;YACN,YAAY;YACZ,YAAY;YACZ,UAAU;YACV,gBAAgB;YAChB,UAAU;YACV,gBAAgB;YAChB,SAAS;YACT,SAAS;YACT,YAAY;YACZ,WAAW,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE;gBACjC,MAAM,CAAC,QAAQ,CACb,+BAA+B,kBAAkB,CAAC,OAAO,CAAC,EAAE,EAC5D,QAAQ,CACT,CAAC;YACJ,CAAC;SACF,CAAC,CAAC;IACL,CAAC,EAAE,EAAE,CAAC,CAAC;AACT,CAAC"}
package/dist/esm/index.js CHANGED
@@ -9,4 +9,5 @@ export * from './hooks/useCollectionDatasetSync';
9
9
  export { useSyncProps, useSelector, } from './hooks';
10
10
  export { QueryCache } from 'react-query/core';
11
11
  export { defineSchemaSource, sourceFetchData } from './types/SchemaSource';
12
+ export * from './types/FieldTypeDefinition';
12
13
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,QAAQ,CAAC;AACvB,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,cAAc,kCAAkC,CAAC;AACjD,OAAO,EAGL,YAAY,EACZ,WAAW,GACZ,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAqC9C,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,QAAQ,CAAC;AACvB,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,cAAc,kCAAkC,CAAC;AACjD,OAAO,EAGL,YAAY,EACZ,WAAW,GACZ,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAuC9C,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAM3E,cAAc,6BAA6B,CAAC"}
@@ -836,10 +836,12 @@ export class ImportState {
836
836
  const failed = progress?.itemsRejected ?? 0;
837
837
  const current = progress?.current ?? succeeded + failed;
838
838
  // The server splits successful items into created (new) and updated
839
- // (existing) — the summary step reports each. Absent on older jobs, in
840
- // which case both stay 0 and only the total `succeeded` is shown.
841
- const created = progress?.itemsSuccessfulInfo?.itemsCreated ?? 0;
842
- const updated = progress?.itemsSuccessfulInfo?.itemsUpdated ?? 0;
839
+ // (existing) — the summary step reports each. Some jobs omit this split
840
+ // entirely (`itemsSuccessfulInfo` absent); in that case treat every success
841
+ // as created so the summary still shows a count instead of a blank step.
842
+ const successInfo = progress?.itemsSuccessfulInfo;
843
+ const created = successInfo ? successInfo.itemsCreated ?? 0 : succeeded;
844
+ const updated = successInfo?.itemsUpdated ?? 0;
843
845
  // `totalCount` is fixed up front in `startImport` from the CSV row count
844
846
  // (one uploaded row per CSV row); polling only advances `processedCount`.
845
847
  runInAction(() => {