@happyvertical/smrt-education 0.58.0 → 0.59.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.
- package/AGENTS.md +6 -1
- package/agents/class-crunchlabs.md +83 -0
- package/agents/course-bundle-import.md +108 -18
- package/dist/catalog-contracts.d.ts +6 -2
- package/dist/catalog-contracts.d.ts.map +1 -1
- package/dist/catalog-materialization-service.d.ts.map +1 -1
- package/dist/catalog-policy.d.ts +11 -1
- package/dist/catalog-policy.d.ts.map +1 -1
- package/dist/catalog-preview.d.ts +6 -1
- package/dist/catalog-preview.d.ts.map +1 -1
- package/dist/chunks/course-authoring-contracts-C4Hqba-e.js +131 -0
- package/dist/chunks/course-authoring-contracts-C4Hqba-e.js.map +1 -0
- package/dist/chunks/course-bundle-zip-5KUe3C5H.js +323 -0
- package/dist/chunks/course-bundle-zip-5KUe3C5H.js.map +1 -0
- package/dist/chunks/{workbook-photo-service-COtyQL1y.js → workbook-photo-service-CjcB0y8Y.js} +226 -130
- package/dist/chunks/workbook-photo-service-CjcB0y8Y.js.map +1 -0
- package/dist/course-authoring-contracts.d.ts +1 -1
- package/dist/course-authoring-contracts.d.ts.map +1 -1
- package/dist/course-bundle-contracts.d.ts +21 -3
- package/dist/course-bundle-contracts.d.ts.map +1 -1
- package/dist/course-bundle-manifest-util.d.ts +15 -0
- package/dist/course-bundle-manifest-util.d.ts.map +1 -0
- package/dist/course-bundle-manifest-v2.d.ts +66 -0
- package/dist/course-bundle-manifest-v2.d.ts.map +1 -0
- package/dist/course-bundle-models.d.ts +29 -1
- package/dist/course-bundle-models.d.ts.map +1 -1
- package/dist/course-bundle-service.d.ts +104 -5
- package/dist/course-bundle-service.d.ts.map +1 -1
- package/dist/course-bundle.d.ts +81 -2
- package/dist/course-bundle.d.ts.map +1 -1
- package/dist/document-converter.d.ts +30 -18
- package/dist/document-converter.d.ts.map +1 -1
- package/dist/hearth-catalog-providers.d.ts +1 -1
- package/dist/hearth-catalog-providers.d.ts.map +1 -1
- package/dist/hearth-class-crunchlabs.d.ts +94 -0
- package/dist/hearth-class-crunchlabs.d.ts.map +1 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1633 -399
- package/dist/index.js.map +1 -1
- package/dist/ingestion.js +1 -1
- package/dist/manifest.json +418 -6
- package/dist/node.d.ts +3 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +134 -0
- package/dist/node.js.map +1 -0
- package/dist/smrt-knowledge.json +162 -10
- package/dist/sources/class-crunchlabs-files.d.ts +8 -0
- package/dist/sources/class-crunchlabs-files.d.ts.map +1 -0
- package/dist/sources/class-crunchlabs.d.ts +217 -0
- package/dist/sources/class-crunchlabs.d.ts.map +1 -0
- package/package.json +12 -8
- package/dist/chunks/workbook-photo-service-COtyQL1y.js.map +0 -1
package/dist/ingestion.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { F as normalizeWorkObservedLayout, I as normalizeWorkAnswerRegions, It as FamilyIntakeAssetCollection, Lt as FamilyIntakeCollection, M as QuestionExtractionPendingError, N as QuestionExtractionValidationError, Nt as WorkbookPhotoIntakeCollection, P as LessonWorkService, Tt as familyIntakeScopeId, Y as LearnerAuthorizationError, a as questionExtractionProposalDigest, an as aggregateWorkQuestions, kt as WorkbookPhotoAssetCollection, n as preservePhotoProposal, nn as WORK_MATH_VOCABULARY, on as validateWorkAssessment, r as validateWorkbookPhotoIdentification, rn as WorkAssessmentValidationError, sn as workAggregateFeedback, t as WorkbookPhotoService, u as QUESTION_EXTRACTION_ARGS_SCHEMA, zt as FamilyIntakeDecisionCollection } from "./chunks/workbook-photo-service-
|
|
1
|
+
import { F as normalizeWorkObservedLayout, I as normalizeWorkAnswerRegions, It as FamilyIntakeAssetCollection, Lt as FamilyIntakeCollection, M as QuestionExtractionPendingError, N as QuestionExtractionValidationError, Nt as WorkbookPhotoIntakeCollection, P as LessonWorkService, Tt as familyIntakeScopeId, Y as LearnerAuthorizationError, a as questionExtractionProposalDigest, an as aggregateWorkQuestions, kt as WorkbookPhotoAssetCollection, n as preservePhotoProposal, nn as WORK_MATH_VOCABULARY, on as validateWorkAssessment, r as validateWorkbookPhotoIdentification, rn as WorkAssessmentValidationError, sn as workAggregateFeedback, t as WorkbookPhotoService, u as QUESTION_EXTRACTION_ARGS_SCHEMA, zt as FamilyIntakeDecisionCollection } from "./chunks/workbook-photo-service-CjcB0y8Y.js";
|
|
2
2
|
import { getCurrentTenant, withTenant } from "@happyvertical/smrt-tenancy";
|
|
3
3
|
import { withPrivateAssetAccess } from "@happyvertical/smrt-assets";
|
|
4
4
|
import { registerPermissionDefinitions } from "@happyvertical/smrt-users";
|
package/dist/manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"version": "1.0.0",
|
|
3
3
|
"timestamp": 0,
|
|
4
4
|
"packageName": "@happyvertical/smrt-education",
|
|
5
|
-
"packageVersion": "0.
|
|
5
|
+
"packageVersion": "0.59.0",
|
|
6
6
|
"objects": {
|
|
7
7
|
"@happyvertical/smrt-education:CatalogProviderCollection": {
|
|
8
8
|
"name": "catalogprovidercollection",
|
|
@@ -869,7 +869,8 @@
|
|
|
869
869
|
"default": "catalog",
|
|
870
870
|
"enum": [
|
|
871
871
|
"catalog",
|
|
872
|
-
"teacher"
|
|
872
|
+
"teacher",
|
|
873
|
+
"operator"
|
|
873
874
|
]
|
|
874
875
|
},
|
|
875
876
|
"lastSeenRunId": {
|
|
@@ -15752,6 +15753,10 @@
|
|
|
15752
15753
|
"type": "text",
|
|
15753
15754
|
"required": false
|
|
15754
15755
|
},
|
|
15756
|
+
"audience": {
|
|
15757
|
+
"type": "text",
|
|
15758
|
+
"required": false
|
|
15759
|
+
},
|
|
15755
15760
|
"sectionKey": {
|
|
15756
15761
|
"type": "text",
|
|
15757
15762
|
"required": false
|
|
@@ -15857,7 +15862,7 @@
|
|
|
15857
15862
|
],
|
|
15858
15863
|
"schema": {
|
|
15859
15864
|
"tableName": "course_bundle_members",
|
|
15860
|
-
"ddl": "CREATE TABLE IF NOT EXISTS \"course_bundle_members\" (\n \"id\" UUID PRIMARY KEY NOT NULL,\n \"slug\" TEXT NOT NULL,\n \"context\" TEXT NOT NULL DEFAULT '',\n \"created_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"updated_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"tenant_id\" UUID NOT NULL,\n \"import_id\" UUID NOT NULL,\n \"ordinal\" INTEGER NOT NULL DEFAULT 0,\n \"path\" TEXT,\n \"sha256\" TEXT,\n \"size_bytes\" INTEGER NOT NULL DEFAULT 0,\n \"media_type\" TEXT,\n \"kind\" TEXT,\n \"status\" TEXT,\n \"reason\" TEXT,\n \"duplicate_of_path\" TEXT,\n \"role\" TEXT,\n \"section_key\" TEXT,\n \"lesson_key\" TEXT,\n \"rendered_sha256\" TEXT,\n \"rendered_size_bytes\" INTEGER NOT NULL DEFAULT 0,\n \"recorded_at\" TEXT\n);",
|
|
15865
|
+
"ddl": "CREATE TABLE IF NOT EXISTS \"course_bundle_members\" (\n \"id\" UUID PRIMARY KEY NOT NULL,\n \"slug\" TEXT NOT NULL,\n \"context\" TEXT NOT NULL DEFAULT '',\n \"created_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"updated_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"tenant_id\" UUID NOT NULL,\n \"import_id\" UUID NOT NULL,\n \"ordinal\" INTEGER NOT NULL DEFAULT 0,\n \"path\" TEXT,\n \"sha256\" TEXT,\n \"size_bytes\" INTEGER NOT NULL DEFAULT 0,\n \"media_type\" TEXT,\n \"kind\" TEXT,\n \"status\" TEXT,\n \"reason\" TEXT,\n \"duplicate_of_path\" TEXT,\n \"role\" TEXT,\n \"audience\" TEXT,\n \"section_key\" TEXT,\n \"lesson_key\" TEXT,\n \"rendered_sha256\" TEXT,\n \"rendered_size_bytes\" INTEGER NOT NULL DEFAULT 0,\n \"recorded_at\" TEXT\n);",
|
|
15861
15866
|
"columns": {
|
|
15862
15867
|
"id": {
|
|
15863
15868
|
"type": "UUID",
|
|
@@ -15954,6 +15959,11 @@
|
|
|
15954
15959
|
"notNull": false,
|
|
15955
15960
|
"unique": false
|
|
15956
15961
|
},
|
|
15962
|
+
"audience": {
|
|
15963
|
+
"type": "TEXT",
|
|
15964
|
+
"notNull": false,
|
|
15965
|
+
"unique": false
|
|
15966
|
+
},
|
|
15957
15967
|
"section_key": {
|
|
15958
15968
|
"type": "TEXT",
|
|
15959
15969
|
"notNull": false,
|
|
@@ -16029,7 +16039,7 @@
|
|
|
16029
16039
|
]
|
|
16030
16040
|
}
|
|
16031
16041
|
],
|
|
16032
|
-
"version": "
|
|
16042
|
+
"version": "24a188d0"
|
|
16033
16043
|
},
|
|
16034
16044
|
"importPath": "@happyvertical/smrt-education"
|
|
16035
16045
|
},
|
|
@@ -16060,7 +16070,7 @@
|
|
|
16060
16070
|
],
|
|
16061
16071
|
"schema": {
|
|
16062
16072
|
"tableName": "course_bundle_members",
|
|
16063
|
-
"ddl": "CREATE TABLE IF NOT EXISTS \"course_bundle_members\" (\n \"id\" UUID PRIMARY KEY NOT NULL,\n \"slug\" TEXT NOT NULL,\n \"context\" TEXT NOT NULL DEFAULT '',\n \"created_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"updated_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"tenant_id\" UUID NOT NULL,\n \"import_id\" UUID NOT NULL,\n \"ordinal\" INTEGER NOT NULL DEFAULT 0,\n \"path\" TEXT,\n \"sha256\" TEXT,\n \"size_bytes\" INTEGER NOT NULL DEFAULT 0,\n \"media_type\" TEXT,\n \"kind\" TEXT,\n \"status\" TEXT,\n \"reason\" TEXT,\n \"duplicate_of_path\" TEXT,\n \"role\" TEXT,\n \"section_key\" TEXT,\n \"lesson_key\" TEXT,\n \"rendered_sha256\" TEXT,\n \"rendered_size_bytes\" INTEGER NOT NULL DEFAULT 0,\n \"recorded_at\" TEXT\n);",
|
|
16073
|
+
"ddl": "CREATE TABLE IF NOT EXISTS \"course_bundle_members\" (\n \"id\" UUID PRIMARY KEY NOT NULL,\n \"slug\" TEXT NOT NULL,\n \"context\" TEXT NOT NULL DEFAULT '',\n \"created_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"updated_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"tenant_id\" UUID NOT NULL,\n \"import_id\" UUID NOT NULL,\n \"ordinal\" INTEGER NOT NULL DEFAULT 0,\n \"path\" TEXT,\n \"sha256\" TEXT,\n \"size_bytes\" INTEGER NOT NULL DEFAULT 0,\n \"media_type\" TEXT,\n \"kind\" TEXT,\n \"status\" TEXT,\n \"reason\" TEXT,\n \"duplicate_of_path\" TEXT,\n \"role\" TEXT,\n \"audience\" TEXT,\n \"section_key\" TEXT,\n \"lesson_key\" TEXT,\n \"rendered_sha256\" TEXT,\n \"rendered_size_bytes\" INTEGER NOT NULL DEFAULT 0,\n \"recorded_at\" TEXT\n);",
|
|
16064
16074
|
"columns": {
|
|
16065
16075
|
"id": {
|
|
16066
16076
|
"type": "UUID",
|
|
@@ -16157,6 +16167,11 @@
|
|
|
16157
16167
|
"notNull": false,
|
|
16158
16168
|
"unique": false
|
|
16159
16169
|
},
|
|
16170
|
+
"audience": {
|
|
16171
|
+
"type": "TEXT",
|
|
16172
|
+
"notNull": false,
|
|
16173
|
+
"unique": false
|
|
16174
|
+
},
|
|
16160
16175
|
"section_key": {
|
|
16161
16176
|
"type": "TEXT",
|
|
16162
16177
|
"notNull": false,
|
|
@@ -16232,7 +16247,404 @@
|
|
|
16232
16247
|
]
|
|
16233
16248
|
}
|
|
16234
16249
|
],
|
|
16235
|
-
"version": "
|
|
16250
|
+
"version": "24a188d0"
|
|
16251
|
+
},
|
|
16252
|
+
"importPath": "@happyvertical/smrt-education"
|
|
16253
|
+
},
|
|
16254
|
+
"@happyvertical/smrt-education:CourseBundleRendering": {
|
|
16255
|
+
"name": "coursebundlerendering",
|
|
16256
|
+
"className": "CourseBundleRendering",
|
|
16257
|
+
"qualifiedName": "@happyvertical/smrt-education:CourseBundleRendering",
|
|
16258
|
+
"collection": "coursebundlerenderings",
|
|
16259
|
+
"description": "Append-only record that a Word/PowerPoint member whose first rendering failed was rendered later by a deliberate retry (SMRT #4615).",
|
|
16260
|
+
"filePath": "/home/runner/work/smrt/smrt/packages/education/src/course-bundle-models.ts",
|
|
16261
|
+
"packageName": "@happyvertical/smrt-education",
|
|
16262
|
+
"fields": {
|
|
16263
|
+
"tenantId": {
|
|
16264
|
+
"type": "text",
|
|
16265
|
+
"required": true,
|
|
16266
|
+
"_meta": {
|
|
16267
|
+
"sqlType": "UUID",
|
|
16268
|
+
"__tenancy": {
|
|
16269
|
+
"isTenantIdField": true,
|
|
16270
|
+
"autoFilter": true,
|
|
16271
|
+
"required": true,
|
|
16272
|
+
"autoPopulate": true,
|
|
16273
|
+
"nullable": false,
|
|
16274
|
+
"mode": "required",
|
|
16275
|
+
"field": "tenantId",
|
|
16276
|
+
"allowSuperAdminBypass": false
|
|
16277
|
+
}
|
|
16278
|
+
}
|
|
16279
|
+
},
|
|
16280
|
+
"importId": {
|
|
16281
|
+
"type": "foreignKey",
|
|
16282
|
+
"required": true,
|
|
16283
|
+
"related": "CourseBundleImport",
|
|
16284
|
+
"_meta": {
|
|
16285
|
+
"required": true
|
|
16286
|
+
}
|
|
16287
|
+
},
|
|
16288
|
+
"sha256": {
|
|
16289
|
+
"type": "text",
|
|
16290
|
+
"required": false
|
|
16291
|
+
},
|
|
16292
|
+
"renderedSha256": {
|
|
16293
|
+
"type": "text",
|
|
16294
|
+
"required": false
|
|
16295
|
+
},
|
|
16296
|
+
"renderedSizeBytes": {
|
|
16297
|
+
"type": "integer",
|
|
16298
|
+
"required": true,
|
|
16299
|
+
"default": 0
|
|
16300
|
+
},
|
|
16301
|
+
"converterId": {
|
|
16302
|
+
"type": "text",
|
|
16303
|
+
"required": false
|
|
16304
|
+
},
|
|
16305
|
+
"retriedByUserId": {
|
|
16306
|
+
"type": "text",
|
|
16307
|
+
"required": false
|
|
16308
|
+
},
|
|
16309
|
+
"recordedAt": {
|
|
16310
|
+
"type": "text",
|
|
16311
|
+
"required": false
|
|
16312
|
+
}
|
|
16313
|
+
},
|
|
16314
|
+
"methods": {
|
|
16315
|
+
"delete": {
|
|
16316
|
+
"name": "delete",
|
|
16317
|
+
"async": true,
|
|
16318
|
+
"parameters": [],
|
|
16319
|
+
"returnType": "Promise<void>",
|
|
16320
|
+
"isStatic": false,
|
|
16321
|
+
"isPublic": true
|
|
16322
|
+
},
|
|
16323
|
+
"save": {
|
|
16324
|
+
"name": "save",
|
|
16325
|
+
"async": true,
|
|
16326
|
+
"parameters": [
|
|
16327
|
+
{
|
|
16328
|
+
"name": "options",
|
|
16329
|
+
"type": "SmrtSaveOptions",
|
|
16330
|
+
"optional": true
|
|
16331
|
+
}
|
|
16332
|
+
],
|
|
16333
|
+
"returnType": "Promise<this>",
|
|
16334
|
+
"isStatic": false,
|
|
16335
|
+
"isPublic": true
|
|
16336
|
+
}
|
|
16337
|
+
},
|
|
16338
|
+
"decoratorConfig": {
|
|
16339
|
+
"tableName": "course_bundle_renderings",
|
|
16340
|
+
"conflictColumns": [
|
|
16341
|
+
"tenant_id",
|
|
16342
|
+
"import_id",
|
|
16343
|
+
"sha256"
|
|
16344
|
+
],
|
|
16345
|
+
"indexes": [
|
|
16346
|
+
{
|
|
16347
|
+
"name": "course_bundle_renderings_unique",
|
|
16348
|
+
"columns": [
|
|
16349
|
+
"tenantId",
|
|
16350
|
+
"importId",
|
|
16351
|
+
"sha256"
|
|
16352
|
+
],
|
|
16353
|
+
"unique": true
|
|
16354
|
+
}
|
|
16355
|
+
],
|
|
16356
|
+
"api": false,
|
|
16357
|
+
"mcp": false,
|
|
16358
|
+
"cli": false,
|
|
16359
|
+
"tenantScoped": {
|
|
16360
|
+
"mode": "required"
|
|
16361
|
+
}
|
|
16362
|
+
},
|
|
16363
|
+
"extends": "SmrtObject",
|
|
16364
|
+
"exportName": "CourseBundleRendering",
|
|
16365
|
+
"collectionExportName": "CourseBundleRenderingCollection",
|
|
16366
|
+
"validationRules": [
|
|
16367
|
+
{
|
|
16368
|
+
"field": "importId",
|
|
16369
|
+
"rule": "required",
|
|
16370
|
+
"fieldType": "foreignKey"
|
|
16371
|
+
},
|
|
16372
|
+
{
|
|
16373
|
+
"field": "renderedSizeBytes",
|
|
16374
|
+
"rule": "required",
|
|
16375
|
+
"fieldType": "integer"
|
|
16376
|
+
}
|
|
16377
|
+
],
|
|
16378
|
+
"schema": {
|
|
16379
|
+
"tableName": "course_bundle_renderings",
|
|
16380
|
+
"ddl": "CREATE TABLE IF NOT EXISTS \"course_bundle_renderings\" (\n \"id\" UUID PRIMARY KEY NOT NULL,\n \"slug\" TEXT NOT NULL,\n \"context\" TEXT NOT NULL DEFAULT '',\n \"created_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"updated_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"tenant_id\" UUID NOT NULL,\n \"import_id\" UUID NOT NULL,\n \"sha256\" TEXT,\n \"rendered_sha256\" TEXT,\n \"rendered_size_bytes\" INTEGER NOT NULL DEFAULT 0,\n \"converter_id\" TEXT,\n \"retried_by_user_id\" TEXT,\n \"recorded_at\" TEXT\n);",
|
|
16381
|
+
"columns": {
|
|
16382
|
+
"id": {
|
|
16383
|
+
"type": "UUID",
|
|
16384
|
+
"primaryKey": true,
|
|
16385
|
+
"referenceKind": "id",
|
|
16386
|
+
"notNull": true
|
|
16387
|
+
},
|
|
16388
|
+
"slug": {
|
|
16389
|
+
"type": "TEXT",
|
|
16390
|
+
"notNull": true
|
|
16391
|
+
},
|
|
16392
|
+
"context": {
|
|
16393
|
+
"type": "TEXT",
|
|
16394
|
+
"notNull": true,
|
|
16395
|
+
"default": ""
|
|
16396
|
+
},
|
|
16397
|
+
"created_at": {
|
|
16398
|
+
"type": "TIMESTAMP",
|
|
16399
|
+
"notNull": true,
|
|
16400
|
+
"default": "current_timestamp"
|
|
16401
|
+
},
|
|
16402
|
+
"updated_at": {
|
|
16403
|
+
"type": "TIMESTAMP",
|
|
16404
|
+
"notNull": true,
|
|
16405
|
+
"default": "current_timestamp"
|
|
16406
|
+
},
|
|
16407
|
+
"tenant_id": {
|
|
16408
|
+
"type": "UUID",
|
|
16409
|
+
"referenceKind": "tenantId",
|
|
16410
|
+
"notNull": true,
|
|
16411
|
+
"unique": false
|
|
16412
|
+
},
|
|
16413
|
+
"import_id": {
|
|
16414
|
+
"type": "UUID",
|
|
16415
|
+
"referenceKind": "foreignKey",
|
|
16416
|
+
"notNull": true,
|
|
16417
|
+
"unique": false,
|
|
16418
|
+
"foreignKey": {
|
|
16419
|
+
"table": "course_bundle_imports",
|
|
16420
|
+
"column": "id",
|
|
16421
|
+
"onDelete": "CASCADE",
|
|
16422
|
+
"onUpdate": "CASCADE"
|
|
16423
|
+
}
|
|
16424
|
+
},
|
|
16425
|
+
"sha256": {
|
|
16426
|
+
"type": "TEXT",
|
|
16427
|
+
"notNull": false,
|
|
16428
|
+
"unique": false
|
|
16429
|
+
},
|
|
16430
|
+
"rendered_sha256": {
|
|
16431
|
+
"type": "TEXT",
|
|
16432
|
+
"notNull": false,
|
|
16433
|
+
"unique": false
|
|
16434
|
+
},
|
|
16435
|
+
"rendered_size_bytes": {
|
|
16436
|
+
"type": "INTEGER",
|
|
16437
|
+
"notNull": true,
|
|
16438
|
+
"unique": false,
|
|
16439
|
+
"default": 0
|
|
16440
|
+
},
|
|
16441
|
+
"converter_id": {
|
|
16442
|
+
"type": "TEXT",
|
|
16443
|
+
"notNull": false,
|
|
16444
|
+
"unique": false
|
|
16445
|
+
},
|
|
16446
|
+
"retried_by_user_id": {
|
|
16447
|
+
"type": "TEXT",
|
|
16448
|
+
"notNull": false,
|
|
16449
|
+
"unique": false
|
|
16450
|
+
},
|
|
16451
|
+
"recorded_at": {
|
|
16452
|
+
"type": "TEXT",
|
|
16453
|
+
"notNull": false,
|
|
16454
|
+
"unique": false
|
|
16455
|
+
}
|
|
16456
|
+
},
|
|
16457
|
+
"indexes": [
|
|
16458
|
+
{
|
|
16459
|
+
"name": "course_bundle_renderings_tenant_id_import_id_idx",
|
|
16460
|
+
"columns": [
|
|
16461
|
+
"tenant_id",
|
|
16462
|
+
"import_id",
|
|
16463
|
+
"sha256"
|
|
16464
|
+
],
|
|
16465
|
+
"unique": true,
|
|
16466
|
+
"nullsNotDistinct": true
|
|
16467
|
+
},
|
|
16468
|
+
{
|
|
16469
|
+
"name": "course_bundle_renderings_slug_context_idx",
|
|
16470
|
+
"columns": [
|
|
16471
|
+
"slug",
|
|
16472
|
+
"context"
|
|
16473
|
+
]
|
|
16474
|
+
},
|
|
16475
|
+
{
|
|
16476
|
+
"name": "course_bundle_renderings_unique",
|
|
16477
|
+
"columns": [
|
|
16478
|
+
"tenant_id",
|
|
16479
|
+
"import_id",
|
|
16480
|
+
"sha256"
|
|
16481
|
+
],
|
|
16482
|
+
"unique": true
|
|
16483
|
+
},
|
|
16484
|
+
{
|
|
16485
|
+
"name": "course_bundle_renderings_tenant_id_created_at_idx",
|
|
16486
|
+
"columns": [
|
|
16487
|
+
"tenant_id",
|
|
16488
|
+
"created_at"
|
|
16489
|
+
]
|
|
16490
|
+
},
|
|
16491
|
+
{
|
|
16492
|
+
"name": "course_bundle_renderings_import_id_idx",
|
|
16493
|
+
"columns": [
|
|
16494
|
+
"import_id"
|
|
16495
|
+
]
|
|
16496
|
+
}
|
|
16497
|
+
],
|
|
16498
|
+
"version": "8f393b32"
|
|
16499
|
+
},
|
|
16500
|
+
"importPath": "@happyvertical/smrt-education"
|
|
16501
|
+
},
|
|
16502
|
+
"@happyvertical/smrt-education:CourseBundleRenderingCollection": {
|
|
16503
|
+
"name": "coursebundlerenderingcollection",
|
|
16504
|
+
"className": "CourseBundleRenderingCollection",
|
|
16505
|
+
"qualifiedName": "@happyvertical/smrt-education:CourseBundleRenderingCollection",
|
|
16506
|
+
"collection": "coursebundlerenderings",
|
|
16507
|
+
"filePath": "/home/runner/work/smrt/smrt/packages/education/src/course-bundle-models.ts",
|
|
16508
|
+
"packageName": "@happyvertical/smrt-education",
|
|
16509
|
+
"fields": {},
|
|
16510
|
+
"methods": {},
|
|
16511
|
+
"decoratorConfig": {
|
|
16512
|
+
"tableName": "course_bundle_renderings",
|
|
16513
|
+
"api": false,
|
|
16514
|
+
"mcp": false,
|
|
16515
|
+
"cli": false
|
|
16516
|
+
},
|
|
16517
|
+
"extends": "SmrtCollection",
|
|
16518
|
+
"extendsTypeArg": "CourseBundleRendering",
|
|
16519
|
+
"exportName": "CourseBundleRenderingCollection",
|
|
16520
|
+
"collectionExportName": "CourseBundleRenderingCollectionCollection",
|
|
16521
|
+
"collectionOf": "@happyvertical/smrt-education:CourseBundleRendering",
|
|
16522
|
+
"inheritedExposure": [
|
|
16523
|
+
"api",
|
|
16524
|
+
"mcp",
|
|
16525
|
+
"cli"
|
|
16526
|
+
],
|
|
16527
|
+
"schema": {
|
|
16528
|
+
"tableName": "course_bundle_renderings",
|
|
16529
|
+
"ddl": "CREATE TABLE IF NOT EXISTS \"course_bundle_renderings\" (\n \"id\" UUID PRIMARY KEY NOT NULL,\n \"slug\" TEXT NOT NULL,\n \"context\" TEXT NOT NULL DEFAULT '',\n \"created_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"updated_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"tenant_id\" UUID NOT NULL,\n \"import_id\" UUID NOT NULL,\n \"sha256\" TEXT,\n \"rendered_sha256\" TEXT,\n \"rendered_size_bytes\" INTEGER NOT NULL DEFAULT 0,\n \"converter_id\" TEXT,\n \"retried_by_user_id\" TEXT,\n \"recorded_at\" TEXT\n);",
|
|
16530
|
+
"columns": {
|
|
16531
|
+
"id": {
|
|
16532
|
+
"type": "UUID",
|
|
16533
|
+
"primaryKey": true,
|
|
16534
|
+
"referenceKind": "id",
|
|
16535
|
+
"notNull": true
|
|
16536
|
+
},
|
|
16537
|
+
"slug": {
|
|
16538
|
+
"type": "TEXT",
|
|
16539
|
+
"notNull": true
|
|
16540
|
+
},
|
|
16541
|
+
"context": {
|
|
16542
|
+
"type": "TEXT",
|
|
16543
|
+
"notNull": true,
|
|
16544
|
+
"default": ""
|
|
16545
|
+
},
|
|
16546
|
+
"created_at": {
|
|
16547
|
+
"type": "TIMESTAMP",
|
|
16548
|
+
"notNull": true,
|
|
16549
|
+
"default": "current_timestamp"
|
|
16550
|
+
},
|
|
16551
|
+
"updated_at": {
|
|
16552
|
+
"type": "TIMESTAMP",
|
|
16553
|
+
"notNull": true,
|
|
16554
|
+
"default": "current_timestamp"
|
|
16555
|
+
},
|
|
16556
|
+
"tenant_id": {
|
|
16557
|
+
"type": "UUID",
|
|
16558
|
+
"referenceKind": "tenantId",
|
|
16559
|
+
"notNull": true,
|
|
16560
|
+
"unique": false
|
|
16561
|
+
},
|
|
16562
|
+
"import_id": {
|
|
16563
|
+
"type": "UUID",
|
|
16564
|
+
"referenceKind": "foreignKey",
|
|
16565
|
+
"notNull": true,
|
|
16566
|
+
"unique": false,
|
|
16567
|
+
"foreignKey": {
|
|
16568
|
+
"table": "course_bundle_imports",
|
|
16569
|
+
"column": "id",
|
|
16570
|
+
"onDelete": "CASCADE",
|
|
16571
|
+
"onUpdate": "CASCADE"
|
|
16572
|
+
}
|
|
16573
|
+
},
|
|
16574
|
+
"sha256": {
|
|
16575
|
+
"type": "TEXT",
|
|
16576
|
+
"notNull": false,
|
|
16577
|
+
"unique": false
|
|
16578
|
+
},
|
|
16579
|
+
"rendered_sha256": {
|
|
16580
|
+
"type": "TEXT",
|
|
16581
|
+
"notNull": false,
|
|
16582
|
+
"unique": false
|
|
16583
|
+
},
|
|
16584
|
+
"rendered_size_bytes": {
|
|
16585
|
+
"type": "INTEGER",
|
|
16586
|
+
"notNull": true,
|
|
16587
|
+
"unique": false,
|
|
16588
|
+
"default": 0
|
|
16589
|
+
},
|
|
16590
|
+
"converter_id": {
|
|
16591
|
+
"type": "TEXT",
|
|
16592
|
+
"notNull": false,
|
|
16593
|
+
"unique": false
|
|
16594
|
+
},
|
|
16595
|
+
"retried_by_user_id": {
|
|
16596
|
+
"type": "TEXT",
|
|
16597
|
+
"notNull": false,
|
|
16598
|
+
"unique": false
|
|
16599
|
+
},
|
|
16600
|
+
"recorded_at": {
|
|
16601
|
+
"type": "TEXT",
|
|
16602
|
+
"notNull": false,
|
|
16603
|
+
"unique": false
|
|
16604
|
+
}
|
|
16605
|
+
},
|
|
16606
|
+
"indexes": [
|
|
16607
|
+
{
|
|
16608
|
+
"name": "course_bundle_renderings_tenant_id_import_id_idx",
|
|
16609
|
+
"columns": [
|
|
16610
|
+
"tenant_id",
|
|
16611
|
+
"import_id",
|
|
16612
|
+
"sha256"
|
|
16613
|
+
],
|
|
16614
|
+
"unique": true,
|
|
16615
|
+
"nullsNotDistinct": true
|
|
16616
|
+
},
|
|
16617
|
+
{
|
|
16618
|
+
"name": "course_bundle_renderings_slug_context_idx",
|
|
16619
|
+
"columns": [
|
|
16620
|
+
"slug",
|
|
16621
|
+
"context"
|
|
16622
|
+
]
|
|
16623
|
+
},
|
|
16624
|
+
{
|
|
16625
|
+
"name": "course_bundle_renderings_unique",
|
|
16626
|
+
"columns": [
|
|
16627
|
+
"tenant_id",
|
|
16628
|
+
"import_id",
|
|
16629
|
+
"sha256"
|
|
16630
|
+
],
|
|
16631
|
+
"unique": true
|
|
16632
|
+
},
|
|
16633
|
+
{
|
|
16634
|
+
"name": "course_bundle_renderings_tenant_id_created_at_idx",
|
|
16635
|
+
"columns": [
|
|
16636
|
+
"tenant_id",
|
|
16637
|
+
"created_at"
|
|
16638
|
+
]
|
|
16639
|
+
},
|
|
16640
|
+
{
|
|
16641
|
+
"name": "course_bundle_renderings_import_id_idx",
|
|
16642
|
+
"columns": [
|
|
16643
|
+
"import_id"
|
|
16644
|
+
]
|
|
16645
|
+
}
|
|
16646
|
+
],
|
|
16647
|
+
"version": "8f393b32"
|
|
16236
16648
|
},
|
|
16237
16649
|
"importPath": "@happyvertical/smrt-education"
|
|
16238
16650
|
},
|
package/dist/node.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,OAAO,EAAE,2BAA2B,EAAE,MAAM,qCAAqC,CAAC"}
|
package/dist/node.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { s as COURSE_BUNDLE_LIMITS, t as crc32 } from "./chunks/course-bundle-zip-5KUe3C5H.js";
|
|
2
|
+
import { open, readFile, readdir } from "node:fs/promises";
|
|
3
|
+
import { basename, join, relative, sep } from "node:path";
|
|
4
|
+
import { lstatSync } from "node:fs";
|
|
5
|
+
import { inflateRawSync } from "node:zlib";
|
|
6
|
+
//#region src/sources/class-crunchlabs-files.ts
|
|
7
|
+
var EOCD = 101010256;
|
|
8
|
+
var CENTRAL = 33639248;
|
|
9
|
+
var LOCAL = 67324752;
|
|
10
|
+
var MAX_CENTRAL_DIRECTORY = 16 * 1024 * 1024;
|
|
11
|
+
var MAX_DEPTH = 8;
|
|
12
|
+
async function readAt(path, position, length) {
|
|
13
|
+
const handle = await open(path, "r");
|
|
14
|
+
try {
|
|
15
|
+
const available = Math.max(0, (await handle.stat()).size - position);
|
|
16
|
+
const buffer = Buffer.alloc(Math.min(length, available));
|
|
17
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, position);
|
|
18
|
+
return buffer.subarray(0, bytesRead);
|
|
19
|
+
} finally {
|
|
20
|
+
await handle.close();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async function sizeOf(path) {
|
|
24
|
+
const handle = await open(path, "r");
|
|
25
|
+
try {
|
|
26
|
+
return (await handle.stat()).size;
|
|
27
|
+
} finally {
|
|
28
|
+
await handle.close();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async function listZip(path) {
|
|
32
|
+
const handle = await open(path, "r");
|
|
33
|
+
let total;
|
|
34
|
+
try {
|
|
35
|
+
total = (await handle.stat()).size;
|
|
36
|
+
} finally {
|
|
37
|
+
await handle.close();
|
|
38
|
+
}
|
|
39
|
+
const tailLength = Math.min(total, 65557);
|
|
40
|
+
const tail = await readAt(path, total - tailLength, tailLength);
|
|
41
|
+
let eocd = -1;
|
|
42
|
+
for (let i = tail.length - 22; i >= 0; i--) if (tail.readUInt32LE(i) === EOCD && i + 22 + tail.readUInt16LE(i + 20) === tail.length) {
|
|
43
|
+
eocd = i;
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
if (eocd === -1) throw new Error(`${basename(path)} is not a readable zip.`);
|
|
47
|
+
const entries = tail.readUInt16LE(eocd + 10);
|
|
48
|
+
const size = tail.readUInt32LE(eocd + 12);
|
|
49
|
+
const offset = tail.readUInt32LE(eocd + 16);
|
|
50
|
+
if (tail.readUInt16LE(eocd + 4) !== 0 || tail.readUInt16LE(eocd + 6) !== 0 || entries === 65535 || size === 4294967295 || offset === 4294967295) throw new Error(`${basename(path)} is a zip64 or multi-disk archive.`);
|
|
51
|
+
if (size > MAX_CENTRAL_DIRECTORY || offset + size > total) throw new Error(`${basename(path)} has an invalid central directory.`);
|
|
52
|
+
const central = await readAt(path, offset, size);
|
|
53
|
+
const out = [];
|
|
54
|
+
let at = 0;
|
|
55
|
+
for (let n = 0; n < entries; n++) {
|
|
56
|
+
if (at + 46 > central.length || central.readUInt32LE(at) !== CENTRAL) throw new Error(`${basename(path)} has a damaged central directory.`);
|
|
57
|
+
const flags = central.readUInt16LE(at + 8);
|
|
58
|
+
const nameLength = central.readUInt16LE(at + 28);
|
|
59
|
+
const extraLength = central.readUInt16LE(at + 30);
|
|
60
|
+
const commentLength = central.readUInt16LE(at + 32);
|
|
61
|
+
const name = central.toString("utf8", at + 46, at + 46 + nameLength);
|
|
62
|
+
const entry = {
|
|
63
|
+
name,
|
|
64
|
+
method: central.readUInt16LE(at + 10),
|
|
65
|
+
crc: central.readUInt32LE(at + 16),
|
|
66
|
+
compressed: central.readUInt32LE(at + 20),
|
|
67
|
+
size: central.readUInt32LE(at + 24),
|
|
68
|
+
offset: central.readUInt32LE(at + 42)
|
|
69
|
+
};
|
|
70
|
+
at += 46 + nameLength + extraLength + commentLength;
|
|
71
|
+
if (name.endsWith("/") || flags & 1 || entry.method !== 0 && entry.method !== 8) continue;
|
|
72
|
+
if (entry.size === 4294967295 || entry.compressed === 4294967295) continue;
|
|
73
|
+
out.push(entry);
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
async function readZipEntry(path, entry) {
|
|
78
|
+
if (entry.size > COURSE_BUNDLE_LIMITS.maxEntryBytes) throw new Error(`${entry.name} is larger than a bundle member may be.`);
|
|
79
|
+
const fileSize = await sizeOf(path);
|
|
80
|
+
if (entry.compressed > COURSE_BUNDLE_LIMITS.maxEntryBytes || entry.offset + 30 + entry.compressed > fileSize) throw new Error(`${entry.name} has an implausible compressed size.`);
|
|
81
|
+
const local = await readAt(path, entry.offset, 30);
|
|
82
|
+
if (local.length < 30 || local.readUInt32LE(0) !== LOCAL) throw new Error(`${entry.name} has a damaged local header.`);
|
|
83
|
+
const data = await readAt(path, entry.offset + 30 + local.readUInt16LE(26) + local.readUInt16LE(28), entry.compressed);
|
|
84
|
+
if (data.length !== entry.compressed) throw new Error(`${entry.name} is truncated.`);
|
|
85
|
+
const bytes = entry.method === 0 ? data : inflateRawSync(data, { maxOutputLength: Math.max(entry.size, 1) });
|
|
86
|
+
if (bytes.length !== entry.size || crc32(bytes) !== entry.crc) throw new Error(`${entry.name} failed its size or checksum check.`);
|
|
87
|
+
return new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
88
|
+
}
|
|
89
|
+
var exportOf = (zipName) => /-(\d{8}T\d{6}Z)-/.exec(zipName)?.[1] ?? "";
|
|
90
|
+
function folderClassCrunchLabsSource(directory) {
|
|
91
|
+
return { async files() {
|
|
92
|
+
const out = [];
|
|
93
|
+
const walk = async (dir, depth) => {
|
|
94
|
+
if (depth > MAX_DEPTH) return;
|
|
95
|
+
const children = await readdir(dir, { withFileTypes: true });
|
|
96
|
+
for (const child of children.sort((a, b) => a.name < b.name ? -1 : 1)) {
|
|
97
|
+
if (child.name.startsWith(".")) continue;
|
|
98
|
+
const full = join(dir, child.name);
|
|
99
|
+
if (child.isSymbolicLink()) continue;
|
|
100
|
+
if (child.isDirectory()) {
|
|
101
|
+
await walk(full, depth + 1);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (!child.isFile()) continue;
|
|
105
|
+
if (/\.zip$/i.test(child.name)) {
|
|
106
|
+
const exportId = exportOf(child.name);
|
|
107
|
+
for (const entry of await listZip(full)) out.push({
|
|
108
|
+
path: entry.name,
|
|
109
|
+
size: entry.size,
|
|
110
|
+
crc32: entry.crc.toString(16).padStart(8, "0"),
|
|
111
|
+
exportId,
|
|
112
|
+
read: () => readZipEntry(full, entry)
|
|
113
|
+
});
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const size = lstatSync(full).size;
|
|
117
|
+
out.push({
|
|
118
|
+
path: relative(directory, full).split(sep).join("/"),
|
|
119
|
+
size,
|
|
120
|
+
read: async () => {
|
|
121
|
+
if (size > COURSE_BUNDLE_LIMITS.maxEntryBytes) throw new Error(`${child.name} is larger than a bundle member may be.`);
|
|
122
|
+
return new Uint8Array(await readFile(full));
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
await walk(directory, 0);
|
|
128
|
+
return out;
|
|
129
|
+
} };
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
132
|
+
export { folderClassCrunchLabsSource };
|
|
133
|
+
|
|
134
|
+
//# sourceMappingURL=node.js.map
|
package/dist/node.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.js","names":[],"sources":["../src/sources/class-crunchlabs-files.ts"],"sourcesContent":["/**\n * Node readers for Class CrunchLabs unit downloads on disk (SMRT #4616): a\n * folder, or the zip parts a Drive export splits it into, or both mixed. Zips\n * are read lazily from their central directory, so a multi-gigabyte part that\n * is mostly video costs only the few entries that are actually used. Server\n * only (`node:fs`, `node:zlib`).\n */\nimport { type Dirent, lstatSync } from 'node:fs';\nimport { open, readdir, readFile } from 'node:fs/promises';\nimport { basename, join, relative, sep } from 'node:path';\nimport { inflateRawSync } from 'node:zlib';\nimport { COURSE_BUNDLE_LIMITS } from '../course-bundle-contracts.js';\nimport { crc32 } from '../course-bundle-zip.js';\nimport type {\n ClassCrunchLabsFile,\n ClassCrunchLabsFileSource,\n} from './class-crunchlabs.js';\n\nconst EOCD = 0x06054b50;\nconst CENTRAL = 0x02014b50;\nconst LOCAL = 0x04034b50;\nconst MAX_CENTRAL_DIRECTORY = 16 * 1024 * 1024;\nconst MAX_DEPTH = 8;\n\ninterface ZipEntry {\n name: string;\n method: number;\n crc: number;\n compressed: number;\n size: number;\n offset: number;\n}\n\nasync function readAt(\n path: string,\n position: number,\n length: number,\n): Promise<Buffer> {\n const handle = await open(path, 'r');\n try {\n // Never allocate more than the file can supply, whatever a header claims.\n const available = Math.max(0, (await handle.stat()).size - position);\n const buffer = Buffer.alloc(Math.min(length, available));\n const { bytesRead } = await handle.read(buffer, 0, buffer.length, position);\n return buffer.subarray(0, bytesRead);\n } finally {\n await handle.close();\n }\n}\n\nasync function sizeOf(path: string): Promise<number> {\n const handle = await open(path, 'r');\n try {\n return (await handle.stat()).size;\n } finally {\n await handle.close();\n }\n}\n\n/** The file entries of a zip, from its central directory alone. */\nasync function listZip(path: string): Promise<ZipEntry[]> {\n const handle = await open(path, 'r');\n let total: number;\n try {\n total = (await handle.stat()).size;\n } finally {\n await handle.close();\n }\n const tailLength = Math.min(total, 22 + 0xffff);\n const tail = await readAt(path, total - tailLength, tailLength);\n let eocd = -1;\n for (let i = tail.length - 22; i >= 0; i--) {\n if (\n tail.readUInt32LE(i) === EOCD &&\n i + 22 + tail.readUInt16LE(i + 20) === tail.length\n ) {\n eocd = i;\n break;\n }\n }\n if (eocd === -1) throw new Error(`${basename(path)} is not a readable zip.`);\n const entries = tail.readUInt16LE(eocd + 10);\n const size = tail.readUInt32LE(eocd + 12);\n const offset = tail.readUInt32LE(eocd + 16);\n if (\n tail.readUInt16LE(eocd + 4) !== 0 ||\n tail.readUInt16LE(eocd + 6) !== 0 ||\n entries === 0xffff ||\n size === 0xffffffff ||\n offset === 0xffffffff\n )\n throw new Error(`${basename(path)} is a zip64 or multi-disk archive.`);\n if (size > MAX_CENTRAL_DIRECTORY || offset + size > total)\n throw new Error(`${basename(path)} has an invalid central directory.`);\n const central = await readAt(path, offset, size);\n const out: ZipEntry[] = [];\n let at = 0;\n for (let n = 0; n < entries; n++) {\n if (at + 46 > central.length || central.readUInt32LE(at) !== CENTRAL)\n throw new Error(`${basename(path)} has a damaged central directory.`);\n const flags = central.readUInt16LE(at + 8);\n const nameLength = central.readUInt16LE(at + 28);\n const extraLength = central.readUInt16LE(at + 30);\n const commentLength = central.readUInt16LE(at + 32);\n const name = central.toString('utf8', at + 46, at + 46 + nameLength);\n const entry: ZipEntry = {\n name,\n method: central.readUInt16LE(at + 10),\n crc: central.readUInt32LE(at + 16),\n compressed: central.readUInt32LE(at + 20),\n size: central.readUInt32LE(at + 24),\n offset: central.readUInt32LE(at + 42),\n };\n at += 46 + nameLength + extraLength + commentLength;\n // Directories, encrypted entries and exotic methods are not unit files.\n if (\n name.endsWith('/') ||\n flags & 1 ||\n (entry.method !== 0 && entry.method !== 8)\n )\n continue;\n if (entry.size === 0xffffffff || entry.compressed === 0xffffffff) continue;\n out.push(entry);\n }\n return out;\n}\n\nasync function readZipEntry(\n path: string,\n entry: ZipEntry,\n): Promise<Uint8Array> {\n if (entry.size > COURSE_BUNDLE_LIMITS.maxEntryBytes)\n throw new Error(`${entry.name} is larger than a bundle member may be.`);\n // A hostile header may claim a payload far beyond the member limit or the file.\n const fileSize = await sizeOf(path);\n if (\n entry.compressed > COURSE_BUNDLE_LIMITS.maxEntryBytes ||\n entry.offset + 30 + entry.compressed > fileSize\n )\n throw new Error(`${entry.name} has an implausible compressed size.`);\n const local = await readAt(path, entry.offset, 30);\n if (local.length < 30 || local.readUInt32LE(0) !== LOCAL)\n throw new Error(`${entry.name} has a damaged local header.`);\n const start =\n entry.offset + 30 + local.readUInt16LE(26) + local.readUInt16LE(28);\n const data = await readAt(path, start, entry.compressed);\n if (data.length !== entry.compressed)\n throw new Error(`${entry.name} is truncated.`);\n const bytes =\n entry.method === 0\n ? data\n : inflateRawSync(data, { maxOutputLength: Math.max(entry.size, 1) });\n if (bytes.length !== entry.size || crc32(bytes) !== entry.crc)\n throw new Error(`${entry.name} failed its size or checksum check.`);\n return new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n}\n\n/** Drive names parts like `Unit_Class CrunchLabs-20261102T164712Z-1-001.zip`; the timestamp orders exports. */\nconst exportOf = (zipName: string) =>\n /-(\\d{8}T\\d{6}Z)-/.exec(zipName)?.[1] ?? '';\n\n/**\n * Every unit file under `directory`: plain files, and the entries of any `.zip`\n * (a zip is not expanded into the filesystem, and a zip inside a zip is not\n * opened). Symbolic links are skipped; hidden files are ignored.\n */\nexport function folderClassCrunchLabsSource(\n directory: string,\n): ClassCrunchLabsFileSource {\n return {\n async files() {\n const out: ClassCrunchLabsFile[] = [];\n const walk = async (dir: string, depth: number): Promise<void> => {\n if (depth > MAX_DEPTH) return;\n const children: Dirent[] = await readdir(dir, { withFileTypes: true });\n for (const child of children.sort((a, b) =>\n a.name < b.name ? -1 : 1,\n )) {\n if (child.name.startsWith('.')) continue;\n const full = join(dir, child.name);\n if (child.isSymbolicLink()) continue;\n if (child.isDirectory()) {\n await walk(full, depth + 1);\n continue;\n }\n if (!child.isFile()) continue;\n if (/\\.zip$/i.test(child.name)) {\n const exportId = exportOf(child.name);\n for (const entry of await listZip(full))\n out.push({\n path: entry.name,\n size: entry.size,\n crc32: entry.crc.toString(16).padStart(8, '0'),\n exportId,\n read: () => readZipEntry(full, entry),\n });\n continue;\n }\n const size = lstatSync(full).size;\n out.push({\n path: relative(directory, full).split(sep).join('/'),\n size,\n read: async () => {\n if (size > COURSE_BUNDLE_LIMITS.maxEntryBytes)\n throw new Error(\n `${child.name} is larger than a bundle member may be.`,\n );\n return new Uint8Array(await readFile(full));\n },\n });\n }\n };\n await walk(directory, 0);\n return out;\n },\n };\n}\n"],"mappings":"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"}
|